Mybatis 的核心逻辑:简单的描述成是为了给一个接口提供代理类,类中包括了对 Mapper 也就是 xml 文件中的 SQL 信息(类型、入参、出参、条件)进行解析和处理,这个处理过程就是对数据库的操作以及返回对应的结果给到接口。
在step02中使用到了MapperRegistry
, 对包路径进行扫描注册映射器,并在 DefaultSqlSession
中进行使用。在这一个章节中主要,通过对 XML 文件的解析和处理就可以完成 Mapper 映射器的注册和 SQL 管理。把命名空间、SQL描述、映射信息维护起来。
SqlSessionFactoryBuilder 是作为mybatis的入口,通过xml文件的io解析,并且返回处理
通过解析把 XML 信息注册到 Configuration 配置类中,再通过传递 Configuration 配置类到各个逻辑处理类里,包括 DefaultSqlSession 中,这样就可以在获取映射器和执行SQL的时候,从配置类中拿到对应的内容了。
mybatis-step-03
└── src
├── main
│ └── java
│ └── com.qf
│ ├── binding
│ │ ├── MapperMethod.java
│ │ ├── MapperProxy.java
│ │ ├── MapperProxyFactory.java
│ │ └── MapperRegistry.java
│ ├── builder
│ │ ├── xml
│ │ │ └── XMLConfigBuilder.java
│ │ └── BaseBuilder.java
│ ├── io
│ │ └── Resources.java
│ ├── mapping
│ │ ├── MappedStatement.java
│ │ └── SqlCommandType.java
│ └── session
│ ├── defaults
│ │ ├── DefaultSqlSession.java
│ │ └── DefaultSqlSessionFactory.java
│ ├── Configuration.java
│ ├── SqlSession.java
│ ├── SqlSessionFactory.java
│ └── SqlSessionFactoryBuilder.java
└── test
├── java
│ └── com.qf
│ ├── dao
│ │ └── IUserDao.java
│ ├── po
│ │ └── User.java
│ └── ApiTest.java
└── resources
├── mapper
│ └──User_Mapper.xml
└── mybatis-config-datasource.xml
package com.qf.mybatis.session;
import com.qf.mybatis.builder.xml.XMLConfigBuilder;
import com.qf.mybatis.session.bind.DefaultSqlSessionFactory;
import java.io.Reader;
/**
* 建造者模式,构建sqlSession工厂
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader){
XMLConfigBuilder xmlConfigBuilder=new XMLConfigBuilder(reader);
return build(xmlConfigBuilder.parse());
}
public SqlSessionFactory build(Configuration configuration){
return new DefaultSqlSessionFactory(configuration);
}
}
SqlSessionFactoryBuilder
是作为整个 Mybatis 的入口类,通过指定解析XML的IO,引导整个流程的启动
package com.qf.mybatis.builder.xml;
import com.qf.mybatis.builder.BaseBuilder;
import com.qf.mybatis.io.Resources;
import com.qf.mybatis.mapping.MappedStatement;
import com.qf.mybatis.mapping.SqlCommandType;
import com.qf.mybatis.session.Configuration;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XMLConfigBuilder extends BaseBuilder {
private Element root;
public XMLConfigBuilder(Reader reader) {
//1、调用父类初始化
super(new Configuration());
//2、dom4j处理xml文件
SAXReader saxReader = new SAXReader();
try {
Document read = saxReader.read(new InputSource(reader));
root = read.getRootElement();
}catch (Exception 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.getResourcesAsReader(resource);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new InputSource(reader));
Element root = document.getRootElement();
//命名空间
String namespace = root.attributeValue("namespace");
//SELECT
List<Element> select = root.elements("select");
for (Element element : select) {
//id,parameterType,resultType,sql
String id = element.attributeValue("id");
String parameterType = element.attributeValue("parameterType");
String resultType = element.attributeValue("resultType");
String sql = element.getText();
//正则解析sql语句
// ? 匹配
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 name = element.getName();
String msId=namespace+"."+id;
SqlCommandType sqlCommandType=SqlCommandType.valueOf(name.toUpperCase(Locale.ENGLISH));
MappedStatement mappedStatement = new MappedStatement.Builder(configuration, msId, sqlCommandType, parameterType, resultType, sql, parameter).build();
//添加解析
configuration.addMappedStatement(mappedStatement);
//注册mapper映射器
configuration.addMapper(Resources.getClass(namespace));
}
}
}
}
XML解析处理,解析sql语句到MappedStatement
类里面,解析xml 的namespace注册到MapperRegister
映射注册机。
MappedStatement
、MapperRegister
共同组成了Configuration
类。
package com.qf.mybatis.session;
import com.qf.mybatis.bind.MapperRegister;
import com.qf.mybatis.mapping.MappedStatement;
import java.util.HashMap;
import java.util.Map;
public class Configuration {
/**
* 映射注册机
*/
protected MapperRegister mapperRegistry = new MapperRegister(this);
/**
* 映射的语句,存在Map里
*/
protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();
public Configuration() {
this.mapperRegistry=mapperRegistry;
}
public MapperRegister getMapperRegistry() {
return mapperRegistry;
}
public void addMappedStatement(MappedStatement mappedStatement){
mappedStatements.put(mappedStatement.getId(),mappedStatement);
}
public MappedStatement getMappedStatement(String id){
return mappedStatements.get(id);
}
public void addMapper(Class<?> aClass){
mapperRegistry.addMapper(aClass);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type,sqlSession);
}
}
配置类包含了MapperRegistry
和MappedStatement
package com.qf.mybatis.session.bind;
import com.qf.mybatis.mapping.MappedStatement;
import com.qf.mybatis.session.Configuration;
import com.qf.mybatis.session.SqlSession;
public class DefaultSqlSession implements SqlSession {
public Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration=configuration;
}
/**
* 目前是返回方法
* @param statement
* @param
* @return
*/
public <T> T selectOne(String statement) {
return (T) ("你被代理了"+statement);
}
public <T> T selectOne(String statement, Object param) {
MappedStatement mappedStatement = configuration.getMappedStatement(statement);
return (T) ("你被代理了"+"方法:"+mappedStatement.getId()+"入参:"+param+mappedStatement.getSql());
}
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type,this);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
}
mapperRegistry
替换为 Configuration
定义的dao接口和对应的xml文件
package com.qf.mybatis.dao;
public interface IUserDao {
String queryUserInfoById(String id);
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatis.dao.IUserDao">
<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.qf.mybatis.po.User">
SELECT id, userId, userHead, createTime
FROM user
where id = #{id}
select>
mapper>
ApiTest
package com.qf.mybatis.test;
import com.qf.mybatis.dao.IUserDao;
import com.qf.mybatis.io.Resources;
import com.qf.mybatis.po.User;
import com.qf.mybatis.session.SqlSession;
import com.qf.mybatis.session.SqlSessionFactory;
import com.qf.mybatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Reader;
public class ApiTest {
private Logger logger= LoggerFactory.getLogger(ApiTest.class);
@Test
public void test_SqlSessionFactory() throws IOException {
//1、sqlsession工厂里面拿取sqlsession
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
Reader reader = Resources.getResourcesAsReader("mybatis-config-datasource.xml");
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
//2、获取映射器
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//3、测试结果
String user = userDao.queryUserInfoById("11L");
logger.info("结果:{}",user);
}
}
结果:
"C:\Program Files\Java\jdk1.8.0_231\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\idea\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar=54097:D:\idea\IntelliJ IDEA 2019.1.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\idea\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar;D:\idea\IntelliJ IDEA 2019.1.1\plugins\junit\lib\junit-rt.jar;D:\idea\IntelliJ IDEA 2019.1.1\plugins\junit\lib\junit5-rt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\rt.jar;E:\zhoutao\mybatis-write\mybatis\step-03-xmlparse\target\test-classes;E:\zhoutao\mybatis-write\mybatis\step-03-xmlparse\target\classes;D:\maven\myrepository\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;D:\maven\myrepository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;D:\maven\myrepository\junit\junit\4.7\junit-4.7.jar;D:\maven\myrepository\cn\hutool\hutool-all\5.5.0\hutool-all-5.5.0.jar;D:\maven\myrepository\org\slf4j\slf4j-api\1.7.5\slf4j-api-1.7.5.jar;D:\maven\myrepository\org\slf4j\jcl-over-slf4j\1.7.5\jcl-over-slf4j-1.7.5.jar;D:\maven\myrepository\ch\qos\logback\logback-classic\1.0.9\logback-classic-1.0.9.jar;D:\maven\myrepository\ch\qos\logback\logback-core\1.0.9\logback-core-1.0.9.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.qf.mybatis.test.ApiTest,test_SqlSessionFactory
23:34:42.299 [main] INFO com.qf.mybatis.test.ApiTest - 结果:你被代理了方法:com.qf.mybatis.dao.IUserDao.queryUserInfoById入参:[Ljava.lang.Object;@2aafb23c
SELECT id, userId, userHead, createTime
FROM user
where id = ?
Process finished with exit code 0
目前的使用方式就和 Mybatis 非常像了,通过加载 xml 配置文件,交给 SqlSessionFactoryBuilder 进行构建解析,并获取 SqlSessionFactory 工厂。这样就可以顺利的开启 Session 以及完成后续的操作。看到了sql语句的输出
SqlSessionFactoryBuilder 的引入包装了整个执行过程,包括:XML 文件的解析、Configuration 配置类的处理,让 DefaultSqlSession 可以更加灵活的拿到对应的信息,获取 Mapper 和 SQL 语句。。