在使用mybatis时,都是直接将sql写在xml中,由mybatis去解析后,映射执行,再通过代理模式实现IDao.
package com.linnine.mybatis.session;
/**
* SqlSessionFactory 建造者工厂
* 整个mybatis的入口类
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactory builder(Reader reader){
//解析xml文件
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);
//将解析内容转换成配置信息类
return builder(xmlConfigBuilder.parse());
}
public SqlSessionFactory builder(Configuration config){
return new DefaultSqlSessionFactory(config);
}
}
先引入一下这个依赖
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
xml解析类
package com.linnine.mybatis.builder;
/**
* xml 解析类
*/
public class XMLConfigBuilder extends BaseBuilder {
private Element root;
public XMLConfigBuilder(Reader reader) {
super(new Configuration());
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(new InputSource(reader));
root =document.getRootElement();
} catch (DocumentException e) {
e.printStackTrace();
}
}
public Configuration parse() {
//解析映射器
try {
mapperElement(root.element("mappers"));
} catch (Exception e) {
throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
return configuration;
}
private void mapperElement(Element mappers) throws Exception {
List<Element> mapperList = mappers.elements("mapper");
for (Element e : mapperList) {
String resource = e.attributeValue("resource");
Reader reader = Resources.getResourceAsReader(resource);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new InputSource(reader));
Element root = document.getRootElement();
String namespace = root.attributeValue("namespace");
List<Element> selectNodes = root.elements("select");
for (Element node : selectNodes) {
String id = node.attributeValue("id");
String parameterType = node.attributeValue("parameterType");
String resultType = node.attributeValue("resultType");
String sql = node.getText();
Map<Integer, String> parameter = new HashMap<>();
//匹配参数占位符
Pattern pattern = Pattern.compile("(#\\{(.*?)})");
Matcher matcher = pattern.matcher(sql);
for (int i = 1; matcher.find(); i++) {
String g1 = matcher.group(1);
String g2 = matcher.group(2);
parameter.put(i,g2);
sql = sql.replace(g1, "?");
}
String msId=namespace+"."+id;
String nodeName = node.getName();
SqlCommandType sqlCommandType=SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//解析处理
MappedStatement mappedStatement = new MappedStatement.Builder(configuration,msId,sqlCommandType,parameterType,resultType,sql,parameter).build();
//添加解析后的sql
configuration.addMappedStatement(mappedStatement);
}
configuration.addMapper(Resources.classForName(namespace));
}
}
}
管理configuration
package com.linnine.mybatis.builder;
public abstract class BaseBuilder {
protected final Configuration configuration;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
}
public Configuration getConfiguration() {
return configuration;
}
}
配置管理类, 主要是管理mapper的注册,以及statement 也就是sql的相关信息.
package com.linnine.mybatis.session;
public class Configuration {
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
protected final Map<String, MappedStatement> mappedStatements = new HashMap();
public <T> void addMapper(Class<T> type){
mapperRegistry.addMapper(type);
}
public <T> T getMapper(Class<T> type,SqlSession sqlSession){
return mapperRegistry.getMapper(type,sqlSession);
}
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
public MappedStatement getMappedStatement(String id) {
return mappedStatements.get(id);
}
}
对映射代理工厂进行管理
package com.linnine.mybatis.binding;
/**
* 再对映射代理工厂进行管理
*/
public class MapperRegistry {
// 注册 代理映射工厂
private final Map<Class<?>,MapperProxyFactory<?>> knownMappers = new HashMap<>();
private Configuration configuration;
public MapperRegistry(Configuration configuration) {
this.configuration=configuration;
}
/**
* 获取代理类
* @param type
* @param sqlSession
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession){
// 获取代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null){
throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//通过代理工厂生产代理类
return mapperProxyFactory.newInstance(sqlSession);
}catch (Exception e){
throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
}
}
public <T> void addMapper(Class<T> type){
//只注册接口
if (type.isInterface()){
//如果已存在不添加
if (hasMapper(type)){
throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
}
//根据类型创建一个代理工厂后注册
knownMappers.put(type,new MapperProxyFactory<>(type));
}
}
/**
* 通过包名扫描添加
* @param packageName
*/
public void addMappers(String packageName){
Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName);
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
}
mybatis里好多循环依赖套来套去的 特别是configuration 吐了.
改造一下sql会话
package com.linnine.mybatis.session;
public class DefaultSqlSession implements SqlSession{
private final Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
/**
* 在这个类里统一代理
* @param statement
* @param parameter
* @param <T>
* @return
*/
@Override
public <T> T selectOne(String statement, Object parameter) {
MappedStatement mappedStatement = configuration.getMappedStatement(statement);
return (T)("你被代理了!"+"方法:"+statement+",入参:"+parameter+"\n待执行sql: "+ mappedStatement.getSql());
}
/**
* 从注册中心拿
* @param type
* @param <T>
* @return
*/
@Override
public <T> T getMapper(Class<T> type) {
//返回代理类,并返回代理方法的处理结果
return configuration.getMapper(type,this);
}
}
改造一下工厂
package com.linnine.mybatis.session;
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration config;
public DefaultSqlSessionFactory(Configuration config) {
this.config =config;
}
@Override
public SqlSession openSqlSession() {
return new DefaultSqlSession(config);
}
}
sql操作类型
package com.linnine.mybatis.binding.mapping;
public enum SqlCommandType {
/**
* 未知
*/
UNKNOWN,
/**
* 插入
*/
INSERT,
/**
* 更新
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 查找
*/
SELECT;
}
xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
<mapper namespace="com.linnine.mybatis.dao.IUserDao">
<select id="queryUserInfoById" parameterType="java.lang.String" resultType="java.lang.String">
SELECT id, userId, userHead, createTime
FROM user
where id = #{id}
</select>
</mapper>
package com.linnine.mybatis.dao;
public interface IUserDao {
String queryUserInfoById(String uId);
}
@Test
public void testMapperProxyFactory() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().builder(reader);
SqlSession sqlSession = sqlSessionFactory.openSqlSession();
IUserDao mapper = sqlSession.getMapper(IUserDao.class);
String user = mapper.queryUserInfoById("10001");
System.out.println(user);
}

这里模拟了方法代理,和sql解析映射,日志打印