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@Override
public 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层接口的代理实现类
@Override
public <T> T getMapper(Class<?> mapperClass) {
// 使用JDK动态代理 为DAO接口生成代理对象并返回
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public 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;
}
�