1 模块概述

自定义持久层框架.png

1.1 实体

Configuration

  • 存储数据源(DataSource)
  • 查询参数信息(Map mappedStatementMap)

    MappedStatement:

  • id:对应xml中<select>标签下的id

  • resultType:对应xml中<select>标签下的resultType
  • parameterType:对应xml中<select>标签下的parameterType
  • sql:对应xml中<select>标签中的文本信息(带有自定义占位符#{}的sql语句)

    BoundSql

  • Sql语句(sqlText)

  • 参数列表(List parameterMappingList)

    1.2 接口及实现

    SimpleExecutor

  • public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params):封装JDBC代码,这里进行真正的数据库操作

  • private Class<?> getClassType(String parameterType):通过反射,获取参数/结果类型。
  • private BoundSql getBoundSql(String sql):对sql中的 #{}解析;解析#{}中的参数并存储

    DefaultSqlSession

    Sql会话,这里调用SimpleExecutor中的query方法。

    DefaultSqlSessionFactory

  • public SqlSession openSession():执行DefaultSqlSession的工厂方法,返回一个DefaultSqlSession

    1. @Override
    2. public SqlSession openSession() {
    3. return new DefaultSqlSession(configuration);
    4. }

    1.3 Builder对象

    XMLConfigBuilder和XMLMapperBuilder

    输入文件流,用dom4j解析XML配置文件。

    SqlSessionFactoryBuilder

    创建SqlSessionFactory。

    2 调用过程

    调用关系.jpg

    3 架构设计思想

    使用端

    提供核⼼配置⽂件:

  • sqlMapConfig.xml : 存放数据源信息,引⼊mapper.xml

  • Mapper.xml : sql语句的配置⽂件信息

    框架端

  1. 读取配置文件

读取完成以后以流的形式存在,我们不能将读取到的配置信息以流的形式存放在内存中,不好操作,可以创建javaBean来存储。

  1. Configuration : 存放数据库基本信息、Map<唯⼀标识,Mapper> 唯⼀标识:namespace + “.” + id
  2. MappedStatement:sql语句、statement类型、输⼊参数java类型、输出参数java类型
    1. 解析配置文件

创建sqlSessionFactoryBuilder类:
⽅法:sqlSessionFactory build():

  1. 使⽤dom4j解析配置⽂件,将解析出来的内容封装到Configuration和MappedStatement中
  2. 创建SqlSessionFactory的实现类DefaultSqlSession
    1. 创建SqlSessionFactory

⽅法:openSession() : 获取sqlSession接⼝的实现类实例对象

  1. 创建sqlSession接⼝及实现类:主要封装crud⽅法

    涉及到的设计模式

    Builder构建者设计模式工厂模式代理模式

    4 优化

    4.1 当前的弊端

    代码:

    1. public List<User> findAll() throws Exception {
    2. InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml");
    3. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    4. SqlSession sqlSession = sqlSessionFactory.openSession();
    5. // 调用
    6. List<User> users = sqlSession.selectList("user.selectOne");
    7. return users;
    8. }
    9. public User findByCondition(User user) throws Exception {
    10. InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml");
    11. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    12. SqlSession sqlSession = sqlSessionFactory.openSession();
    13. // 调用
    14. User result = sqlSession.selectOne("user.selectOne", user);
    15. return result;
    16. }
  2. 存在硬编码

statementId需要手动写,和xml中的namespace.id对应

  1. 存在重复代码

每个方法都要重复加载配置文件、创建SqlSessionFactory、生产SqlSession

4.2 解决

思路:使用代理模式生成Dao层接口的代理实现类

  1. @Override
  2. public <T> T getMapper(Class<?> mapperClass) {
  3. // 使用JDK动态代理 为DAO接口生成代理对象并返回
  4. Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
  5. @Override
  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  7. // 参数 1. statementId:namespace.id; invoke 方法中无法获取到mapper.xml中的namespace.id,所以要根据method方法的方法名,所以这就是为什么mybatis中 namespace为dao接口的全限定类名,id为方法名。
  8. String className = method.getDeclaringClass().getName();
  9. String methodName = method.getName();
  10. String statementId = className + "." + methodName;
  11. // 参数 2. params: args
  12. // 获取被调用方法的返回值类型
  13. Type genericReturnType = method.getGenericReturnType();
  14. // 判断是否进行了 范型类型参数化
  15. if (genericReturnType instanceof ParameterizedType) {
  16. List<Object> objects = selectList(statementId, args);
  17. return objects;
  18. }
  19. return selectOne(statementId, args);
  20. }
  21. });
  22. return (T) proxyInstance;
  23. }