ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

自定义持久层

2021-11-20 21:06:02  阅读:123  来源: 互联网

标签:return String 自定义 offset new 持久 configuration public


思路:

  1. 使用端:项目-引用自定义持久层框架
    1. 定义sqlMapperConfig.xml(数据库的配置信息,存放mapper.xml的路径);
    2. 定义mapper.xml(存放sql语句);
  2. 自定义持久层框架本身;
    1. 加载配置文件:
      1. 根据配置文件的路径,加载配置文件成输入流,存在内存中;
        1. 创建Resource类---存在方法:inputSteam getInputSteam(String path);
    2. 创建两个JavaBean容器
      1. 存放解析配置文件的内容:
        1. Configuartion:核心配置类-存放sqlMapperConfig.xml解析出来的内容;
        2. MappedStatement:映射配置类,存放mapper.xml解析出来的内容;
    3. 解析配置文件(dom4j):
      1. 创建类SqlSessionFactorBuilder方法:存在bulid(inputSteam)
        1. 使用dom4j解析配置文件,将解析出来的信息保存到容器中;
        2. 创建SqlsessionFactory对象;生产SqlSession会话对象;
    4. 创建SqlsessionFactory接口及它的实现类DefaultSqlsessionFactory:
      1. 存在openSession-生产SqlSession;
    5. 创建SqlSession接口及它的实现类DefaultSqlSession:
      1. 定义对数据库的CRUD操作;
    6. 创建Executor接口及实现类SimpleExecutor实现类:
      1. query(Configuration,MappedStatement,Object...)

代码实现:

持久层框架

框架结构图

  1. 创建Configuration类(用于保存sqlMapConfig.xml解析后的信息)
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 20:42
 * @ClassName Configuration
 * @Description:
 */
public class Configuration {
    private DataSource dataSource;
    private Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
    public DataSource getDataSource() {
        return dataSource;
    }
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }
    public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
        this.mappedStatementMap = mappedStatementMap;
    }
}
  1. 创建MapperStament类,用于保存Mapper.xml解析后的信息
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 20:40
 * @ClassName MappedStatement
 * @Description:
 */
public class MappedStatement {
    private String id;
    private String resultType;
    private String paramterType;
    private String sql;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getResultType() {
        return resultType;
    }
    public void setResultType(String resultType) {
        this.resultType = resultType;
    }
    public String getParamterType() {
        return paramterType;
    }
    public void setParamterType(String paramterType) {
        this.paramterType = paramterType;
    }
    public String getSql() {
        return sql;
    }
    public void setSql(String sql) {
        this.sql = sql;
    }
}
  1. 创建BoundSql类,用于存储解析后的SQL语句
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/18 21:25
 * @ClassName BoundSql
 * @Description:
 */
public class BoundSql {
    private String sql;
    private List<ParameterMapping> parameterMappings = new ArrayList<>();

    public BoundSql(String sql, List<ParameterMapping> parameterMappings) {
        this.sql = sql;
        this.parameterMappings = parameterMappings;
    }
    public String getSql() {
        return sql;
    }
    public void setSql(String sql) {
        this.sql = sql;
    }
    public List<ParameterMapping> getParameterMappings() {
        return parameterMappings;
    }
    public void setParameterMappings(List<ParameterMapping> parameterMappings) {
        this.parameterMappings = parameterMappings;
    }
}
  1. 创建Resource类
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/16 22:20
 * @ClassName Resources
 * @Description:读取配置文件路径,转换成输入流
 */
public class Resources {
    public static InputStream getInputStream(String path){
        InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
        return resourceAsStream;
    }
}
  1. 创建SqlSession接口
    1. 方法selectList():根据条件查询,返回多条记录,statemenid为mapper.xml文件中的namespace+.+标签id,object..为条件参数
    2. 方法selectOne():根据条件查询,返回单条记录
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 20:40
 * @interfaceName SqlSession
 * @Description:
 */
public interface SqlSession {
    <E> List<E> selectList(String statemenid,Object... params) throws IllegalAccessException, IntrospectionException, InstantiationException, NoSuchFieldException, SQLException, InvocationTargetException, ClassNotFoundException;
    <T> T selectOne(String statemenid,Object... params) throws IllegalAccessException, ClassNotFoundException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException;
}
  1. 创建sqlSessionFactory接口
    1. 方法openSession():生产SqlSession对象
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 21:00
 * @interfaceName SqlSessionFactory
 * @Description:
 */
public interface SqlSessionFactory {
    SqlSession openSession();
}
  1. 创建Executrer接口
    1. 方法query():执行查询语句的方法
public interface Executer {
    <E>List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException;
}
  1. 创建XMLMapperBuilder类,用于解析Mapper.xml文件
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 21:35
 * @ClassName XMLMapperBuilder
 * @Description:
 */
public class XMLMapperBuilder {
    private Configuration configuration;
    public XMLMapperBuilder(Configuration configuration){
        this.configuration=configuration;
    }
    public void parse(InputStream inputStream) throws DocumentException {
        Document document = new SAXReader().read(inputStream);
        //获取根节点
        Element rootElement = document.getRootElement();
        String namespace = rootElement.attributeValue("namespace");
        //查找<select></select>标签
        List<Element> listSelect = rootElement.selectNodes("//select");
        for (Element element : listSelect){
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String paramterType = element.attributeValue("paramterType");
            String sqlText = element.getTextTrim();
            String key = namespace+"."+id;
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParamterType(paramterType);
            mappedStatement.setSql(sqlText);
            configuration.getMappedStatementMap().put(key,mappedStatement);
        }
    }
}
  1. 创建XMLConfigBuilder(用于解析配置文件sqlMapConfig.xml)
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 21:02
 * @ClassName XMLConfigBuilder
 * @Description:
 */
public class XMLConfigBuilder {
    private Configuration configuration;
    public XMLConfigBuilder(){
         this.configuration = new Configuration();
    }
    public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
        Document document = new SAXReader().read(inputStream);
        //获取根对象<configuration></configuration>
        Element rootElement = document.getRootElement();
        //Xpath表达式查询<property></property>标签
        List<Element> listProperty = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element element : listProperty){
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name,value);
        }
        //建立连接池
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //写入链接参数
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setUser(properties.getProperty("username"));
        comboPooledDataSource.setPassword(properties.getProperty("password"));
        this.configuration.setDataSource(comboPooledDataSource);
        //解析mapper.xml
        List<Element> listMapper = rootElement.selectNodes("mapper");
        for(Element element : listMapper){
            String mapperPath = element.attributeValue("resource");
            InputStream inputStreamMapper = Resources.getInputStream(mapperPath);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(this.configuration);
            xmlMapperBuilder.parse(inputStreamMapper);
        }
        return this.configuration;
    }
}
  1. 实现SqlSessionFactor的实现类DefaultSqlSessionFactor
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 21:56
 * @ClassName DefaultSqlSesionFactory
 * @Description:
 */
public class DefaultSqlSesionFactory implements SqlSessionFactory {
    private Configuration configuration;
    public DefaultSqlSesionFactory(Configuration configuration){
        this.configuration=configuration;
    }
    @Override
    public SqlSession openSession() {
        return new DefaultSqlSesion(configuration);
    }
}
  1. 创建GenericTokenParser,解析SQL语句
/**
 * @author 小林林同学
 */
public class GenericTokenParser {
  private final String openToken; //开始标记
  private final String closeToken; //结束标记
  private final TokenHandler handler; //标记处理器

  public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
    this.openToken = openToken;
    this.closeToken = closeToken;
    this.handler = handler;
  }

  /**
   * 解析${}和#{}
   * @param text
   * @return
   * 该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据。
   * 其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现
   */
  public String parse(String text) {
    // 验证参数问题,如果是null,就返回空字符串。
    if (text == null || text.isEmpty()) {
      return "";
    }

    // 下面继续验证是否包含开始标签,如果不包含,默认不是占位符,直接原样返回即可,否则继续执行。
    int start = text.indexOf(openToken, 0);
    if (start == -1) {
      return text;
    }

   // 把text转成字符数组src,并且定义默认偏移量offset=0、存储最终需要返回字符串的变量builder,
    // text变量中占位符对应的变量名expression。判断start是否大于-1(即text中是否存在openToken),如果存在就执行下面代码
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    while (start > -1) {
     // 判断如果开始标记前如果有转义字符,就不作为openToken进行处理,否则继续处理
      if (start > 0 && src[start - 1] == '\\') {
        builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();
      } else {
        //重置expression变量,避免空指针或者老数据干扰。
        if (expression == null) {
          expression = new StringBuilder();
        } else {
          expression.setLength(0);
        }
        builder.append(src, offset, start - offset);
        offset = start + openToken.length();
        int end = text.indexOf(closeToken, offset);
        while (end > -1) {存在结束标记时
          if (end > offset && src[end - 1] == '\\') {//如果结束标记前面有转义字符时
            // this close token is escaped. remove the backslash and continue.
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else {//不存在转义字符,即需要作为参数进行处理
            expression.append(src, offset, end - offset);
            offset = end + closeToken.length();
            break;
          }
        }
        if (end == -1) {
          // close token was not found.
          builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
          //首先根据参数的key(即expression)进行参数处理,返回?作为占位符
          builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();
        }
      }
      start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
  }
}
  1. 创建ParameterMapping类
public class ParameterMapping {
    private String content;
    public ParameterMapping(String content) {
        this.content = content;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}
  1. 创建TokenHandler接口
public interface TokenHandler {
  String handleToken(String content);
}
  1. 创建TokenHandler接口实现类
public class ParameterMappingTokenHandler implements TokenHandler {
   private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
   // context是参数名称 #{id} #{username}
   public String handleToken(String content) {
      parameterMappings.add(buildParameterMapping(content));
      return "?";
   }
   private ParameterMapping buildParameterMapping(String content) {
      ParameterMapping parameterMapping = new ParameterMapping(content);
      return parameterMapping;
   }
   public List<ParameterMapping> getParameterMappings() {
      return parameterMappings;
   }
   public void setParameterMappings(List<ParameterMapping> parameterMappings) {
      this.parameterMappings = parameterMappings;
   }
}
  1. 创建SqlSessionFactoryBulidr类(用于生产SqlSessionFactory)
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 21:00
 * @ClassName SqlSessionFactoryBuildr
 * @Description:
 */
public class SqlSessionFactoryBuildr {
    public SqlSessionFactory builder(InputStream inputStream) throws DocumentException, PropertyVetoException {
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
        Configuration configuration = xmlConfigBuilder.parseConfig(inputStream);
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSesionFactory(configuration);
        return sqlSessionFactory;
    }
}
  1. 创建Executrer的实现类SimpleExecutrer
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 22:26
 * @ClassName SimpleExecutor
 * @Description:
 */
public class SimpleExecutor implements Executer{
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
        //1、注册驱动,获取链接
        Connection connection = configuration.getDataSource().getConnection();
        //2、获取sql语句-》转换sql语句
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        //3、获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
        //4、设置参数
        String paramterType = mappedStatement.getParamterType();
        Class<?> paramertypeClass = getClassType(paramterType);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i=0; i<parameterMappings.size(); i++){
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();
            Field declaredField = paramertypeClass.getDeclaredField(content);
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);
            preparedStatement.setObject(i+1,o);
        }
        //5、执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        String resultType = mappedStatement.getResultType();
        Class<?> resultClassType = getClassType(resultType);
        ArrayList<Object> objects = new ArrayList<>();
        while (resultSet.next()){
            Object o = resultClassType.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();
            for(int i=1; i<=metaData.getColumnCount(); i++){
                String columnName = metaData.getColumnName(i);
                Object value = resultSet.getObject(columnName);
                //使用反射或者内省,根据数据库表和实体的对应关系,完成封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultClassType);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o,value);
            }
            objects.add(o);
        }
        return (List<E>) objects;
    }

    private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
        if (paramterType!=null) {
            return Class.forName(paramterType);
        }
        return null;
    }

    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{","}",parameterMappingTokenHandler);
        String sqlText = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql = new BoundSql(sqlText, parameterMappings);
        return boundSql;
    }
}
  1. 创建SqlSession的实现类DefaultSqlSession
/**
 * @Auther: 小林林同学
 * @Date: 2021/11/17 22:00
 * @ClassName DefaultSqlSesion
 * @Description:
 */
public class DefaultSqlSesion implements SqlSession {
    private Configuration configuration;
    public DefaultSqlSesion(Configuration configuration){
         this.configuration = configuration;
     }
    @Override
    public <E> List<E> selectList(String statemenid, Object... params) throws IllegalAccessException, IntrospectionException, InstantiationException, NoSuchFieldException, SQLException, InvocationTargetException, ClassNotFoundException {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statemenid);
        List<Object> objectList = simpleExecutor.query(configuration, mappedStatement, params);
        return (List<E>) objectList;
    }
    @Override
    public <T> T selectOne(String statemenid, Object... params) throws IllegalAccessException, ClassNotFoundException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException {
        List<Object> objects = selectList(statemenid, params);
        if(objects.size()==1){
            return (T) objects;
        }else {
            throw new RuntimeException("查询结果为空或返回结果值过多");
        }
    }
}

使用端

引用自定义持久层框架,并调用相关类

/**
 * @Auther: 小林林同学
 * @Date: 2021/11/16 22:30
 * @ClassName IpersistenceTest
 * @Description:
 */
public class IpersistenceTest {
    @Test
    public void test() throws PropertyVetoException, DocumentException, IllegalAccessException, IntrospectionException, InstantiationException, NoSuchFieldException, SQLException, InvocationTargetException, ClassNotFoundException {
        InputStream inputStream = Resources.getInputStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuildr().builder(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(1);
        user.setUsername("lucy");
        Object o = sqlSession.selectOne("user.selectOne", user);
        System.out.print(user);
    }
}

标签:return,String,自定义,offset,new,持久,configuration,public
来源: https://blog.csdn.net/lpc_97/article/details/121445371

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有