1 模块概述
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@Overridepublic SqlSession openSession() {return new DefaultSqlSession(configuration);}
1.3 Builder对象
XMLConfigBuilder和XMLMapperBuilder
SqlSessionFactoryBuilder
2 调用过程
3 架构设计思想
使用端
提供核⼼配置⽂件:
sqlMapConfig.xml : 存放数据源信息,引⼊mapper.xml
- Mapper.xml : sql语句的配置⽂件信息
框架端
- 读取配置文件
读取完成以后以流的形式存在,我们不能将读取到的配置信息以流的形式存放在内存中,不好操作,可以创建javaBean来存储。
- Configuration : 存放数据库基本信息、Map<唯⼀标识,Mapper> 唯⼀标识:namespace + “.” + id
- MappedStatement:sql语句、statement类型、输⼊参数java类型、输出参数java类型
- 解析配置文件
创建sqlSessionFactoryBuilder类:
⽅法:sqlSessionFactory build():
- 使⽤dom4j解析配置⽂件,将解析出来的内容封装到Configuration和MappedStatement中
- 创建SqlSessionFactory的实现类DefaultSqlSession
- 创建SqlSessionFactory
⽅法:openSession() : 获取sqlSession接⼝的实现类实例对象
-
涉及到的设计模式
4 优化
4.1 当前的弊端
代码:
public List<User> findAll() throws Exception {InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 调用List<User> users = sqlSession.selectList("user.selectOne");return users;}public User findByCondition(User user) throws Exception {InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 调用User result = sqlSession.selectOne("user.selectOne", user);return result;}
存在硬编码
statementId需要手动写,和xml中的namespace.id对应
- 存在重复代码
每个方法都要重复加载配置文件、创建SqlSessionFactory、生产SqlSession。
4.2 解决
思路:使用代理模式生成Dao层接口的代理实现类
@Overridepublic <T> T getMapper(Class<?> mapperClass) {// 使用JDK动态代理 为DAO接口生成代理对象并返回Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 参数 1. statementId:namespace.id; invoke 方法中无法获取到mapper.xml中的namespace.id,所以要根据method方法的方法名,所以这就是为什么mybatis中 namespace为dao接口的全限定类名,id为方法名。String className = method.getDeclaringClass().getName();String methodName = method.getName();String statementId = className + "." + methodName;// 参数 2. params: args// 获取被调用方法的返回值类型Type genericReturnType = method.getGenericReturnType();// 判断是否进行了 范型类型参数化if (genericReturnType instanceof ParameterizedType) {List<Object> objects = selectList(statementId, args);return objects;}return selectOne(statementId, args);}});return (T) proxyInstance;}
�
