自定义持久层

思路:

  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);
    }
}

上一篇:webapi读取配置文件内容


下一篇:mybatis 初始化过程