MapperProxyFactory
Mapper是什么?
Mapper由两个文件组成
1 Java文件(映射器类/IDAO)用户定义的数据库操作接口:想要对数据库进行的操作
public interface IUserService {
queryNameById();
queryAgeById();
}
2 XML文件(XML映射文件)操作的具体实现:SQL语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.IUserService">
<select id="queryNameById" parameterType="java.lang.Integer" resultType="java.lang.String">
SELECT name FROM user WHERE id = #{id}
</select>
<select id="queryAgeById" parameterType="java.lang.Integer" resultType="java.lang.String">
SELECT age FROM user WHERE id = #{id}
</select>
</mapper>
在 XML 文件中,通过 namespace,可以找到对应 IDAO类,后面会用到
MapperProxy
通过IDAO接口 + XML文件,可以完成对数据库的操作
那么,IDAO接口和XML文件,是如何联系在一起的?
通过代理类
将IDAO接口和XML文件封装到代理类中
之后,通过这个代理类,当调用IDAO接口中的方法时,最终会调用XML中对应SQL语句
这个代理类就是MapperProxy,如图
public class MapperProxy implements InvocationHandler {
private Class IDAO;
private HashMap<String, String> xmlFile;
public MapperProxy(Class IDAO, HashMap<String, String> xmlFile) {
this.IDAO = IDAO;
this.xmlFile = xmlFile;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return xmlFile.get(method.getName());
}
}
测试如下
HashMap<String, String> xmlFile = new HashMap<>();
xmlFile.put("queryNameById", "SELECT name FROM user WHERE id = 1");
xmlFile.put("queryAgeById", "SELECT age FROM user WHERE id = 1");
MapperProxy mapperProxy = new MapperProxy(IUserService.class, xmlFile);
IUserService o = (IUserService) Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, mapperProxy);
System.out.println(o.queryNameById()); // SELECT name FROM user WHERE id = 1
测试的时候,每次都要调用 Proxy.newProxyInstance,很麻烦;于是,封装到 MapperProxy 中
public Object getProxyObject(){
return Proxy.newProxyInstance(IDAO.getClassLoader(), new Class[]{IDAO}, this);
}
// 之后就可以
MapperProxy mapperProxy = new MapperProxy(IUserService.class, xmlFile);
IUserService o = (IUserService) mapperProxy.getProxyObject();
System.out.println(o.queryNameById()); // SELECT name FROM user WHERE id = 1
MapperProxyFactory
相同的IDAO接口,可有不同的实现方式,甚至,所使用的数据库也可能不同
因此,干脆将IDAO封装到一个类中,之后通过 newInstance(xmlFIle) 的方式生成 MapperProxy 代理对象
另外,Proxy.newProxyInstance 的逻辑也封装到 newInstance 中
这个类就是MapperProxyFactory
public class MapperProxyFactory<T> {
private Class<T> IDAO;
public MapperProxyFactory(Class<T> IDAO) {
this.IDAO = IDAO;
}
public T newInstance(HashMap<String, String> xmlFIle) {
final MapperProxy mapperProxy = new MapperProxy(IDAO, xmlFIle);
return (T) Proxy.newProxyInstance(IDAO.getClassLoader(), new Class[]{IDAO}, mapperProxy);
}
}
使用如下
IUserService o = new MapperProxyFactory<>(IUserService.class).newInstance(xmlFile);
System.out.println(o.queryNameById());
这里的 sqlSession
理解为 XML 文件,mapperInterface
理解为 IDAO
,后面就使用作者的了
MapperRegistry 及 SqlSession 封装
MapperRegistry
MapperProxyFactory 只是针对一个IDAO接口,并不能添加、删除;那么,就必定需要一个集中式管理的类
这个类就是 MapperRegistry,相当于封装了多个 IDAO 接口,用于集中式管理IDAO接口
SqlSession
之前定义的 sqlsession (xmlFile)为一个 HashMap,现将其封装成一个对象,并由 SqlSessionFactory 构造,该工厂持有 MapperRegistry(封装多个IDAO服务)
通过该工厂,可创建 SqlSession,但此时 SqlSession 中未定义具体实现
为何要创建一个 SqlSessionFactory ?直接 new SqlSession 不行吗?
诶,好像还真可以~
如果要强行解释的话,那么我的强行解释就是,Factory,从语义上可以理解为创建 xxx 的工厂,很规范。。。。。容易理解
XML解析
Configuration 类
根据 mapper.xml 的信息,namespace 对应 IDAO的位置,select 等对应 SQL 语句,通过解析 XML,可获取这些内容
解析出这些内容后,必定要存储,于是用到了 Configuration 类:将 xmlFile(对数据库的操作)的信息,封装到 Configuration 类中
Configuration 类中有
- mapperRegistry(为多个IDAO服务):根据 mapper.xml 文件下的 namespace 进行添加的
- mappedStatements(与IDAO对应的SQL语句):类型为
HashMap<String, MappedStatement>
,一个 MappedStatement 对应一条 SQL 语句
XML environments 解析
Environment
这里会解析 environment(用于连接数据库的信息:driver、url、username、password),并放入 Configration类中
于是,Configuration 中多出了个 Environment 类实例的成员变量,Environment 类中有
- id:如 development
- transactionFactory:如 JdbcTransactionFactory
- dataSource:如 DruidDataSource
typeAliasRegistry 类型别名注册机
Configration类
中有个名为 typeAliasRegistry
的成员变量,它是 TypeAliasRegistry 类的实例,类中有个 HashMap,用于存储别名
<select id="queryUser" resultType="com.foo.bar.baz.quz.User">
SELECT * FROM user WHERE id = 1
</select>
resultType 很长是不是 ? 起个别名吧~
<configuration>
<typeAliases>
<typeAlias alias="user" type="com.foo.bar.baz.quz.User"/>
</typeAliases>
</configuration>
<select id="queryUser" resultType="user">
SELECT * FROM user WHERE id = 1
</select>
在 new Configuration
的时候(还未解析XML文件时),会将 JdbcTransactionFactory、DruidDataSourceFactory
存放到 typeAliasRegistry
内的 HashMap
中
之后,根据 XML 文件中的信息,可通过 typeAliasRegistry
获取到JdbcTransactionFactory、DruidDataSourceFactory
,用于创建 Environment