Spring 整合Mybatis 后,如何对其配置文件进行加载和解析,如何进行数据的CRUD。
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
dependency>
<dependency>
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="dbconfig.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
<environment id="development1">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
<environment id="development2">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/Test.xml"/>
mappers>
configuration>
dbconfig.properties:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3406/mybatis
jdbc.username=root
jdbc.password=ddsoft
TestMapper:
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Map;
public interface TestMapper {
@Select("select '123'")
public String selectOne(String str);
public Map selectById(@Param(value = "id") String id);
}
TestMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springdabaihua.mapper.TestMapper">
<select id="selectById" resultType="java.util.Map">
select * from tb_user
where 1=1
<if test="id != null and id != ''">
and id =#{id}
if>
select>
mapper>
TestMain:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
import java.util.Map;
public class TestMain {
public static void main(String[] args) throws IOException {
String resource ="mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
// 解析数据源 解析 xml 中的sql 语句
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 解析 Executor 执行器
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
Object abc = sqlSession.selectOne("com.example.springdabaihua.mapper.TestMapper.selectOne","123");
System.out.println("abc = " + abc);
Map abc1 =(Map) sqlSession.selectOne("com.example.springdabaihua.mapper.TestMapper.selectById","1");
abc1.entrySet().stream().forEach(e->{
System.out.println("e.toString() = " + e.toString());
});
// System.out.println("abc1 = " + abc1);
}
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
// 配置文件加载读取 成为 Document 文件
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 具体标签值解析并设置
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
if (reader != null) {
reader.close();
}
} catch (IOException var13) {
}
}
return var5;
}
// 解析资源并返回 Configuration
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
// 解析configuration 标签
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
parseConfiguration:
private void parseConfiguration(XNode root) {
try {
// 解析获取 数据库连接等
this.propertiesElement(root.evalNode("properties"));
// 解析 全局配置
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
// 读取虚拟文件,一般不用
this.loadCustomVfs(settings);
// 加载 日志的实现
this.loadCustomLogImpl(settings);
// 加载别名处理器 TypeAliasRegistry 我们在Mapper.xml 文件中可以 直接在
//
this.typeAliasesElement(root.evalNode("typeAliases"));
// 解析插件 分页等 加入到 InterceptorChain 责任链中
this.pluginElement(root.evalNode("plugins"));
// 工厂类配置一般不配置直接使用默认的 Default 开头的
this.objectFactoryElement(root.evalNode("objectFactory"));//
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 加载全部的配置文件
this.settingsElement(settings);
// 加载 properties key-value 值 得到TransactionFactory 事务工厂 和 DataSourceFactory 数据源
// 数据库事务及连接处理
this.environmentsElement(root.evalNode("environments"));
// 数据库厂商id 一般不使用
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析类型处理器 TypeHandlerRegistry java 类型到 sql 数据类型转换的类型处理器
// spring 可以自动转换 对传入sql 的参数进行转换,对返回的结果集数据转换为java 类型
this.typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mapper xml 资源 放入到 Map, MapperProxyFactory>> knownMappers = new HashMap();
// map 的代理对象,只是放入接口类,后面通过jdk 的动态代理生成mapper 的代理对象
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
这里重点对 mysql 数据源的配置 以及对mapper.xml 配置进行分析:
environmentsElement: mysql 数据源的配置
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
// 事务工厂获取 通过 transactionManager
// 通过type JDBC 别名 判定使用什么事务工厂,可以获取数据库连接
// spring整合后 拿到spring 的 事务工厂 SpringManagedTransactionFactory
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
// 数据库工厂
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = (new Environment.Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
this.configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
}
这里在 (child.evalNode(“dataSource”) 直接从环境变量里获取里数据的驱动,用户名,密码等信息,这些信息是由 this.propertiesElement(root.evalNode(“properties”)); 完成将解析后端的数据放入到 Properties variables 属性中;
mapperElement : 对于mapper.xml 解析,对于mapper 资源加载的几种方式
读取mapper 标签的配置:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
InputStream inputStream;
Throwable var8;
XMLMapperBuilder mapperParser;
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
var8 = null;
try {
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} catch (Throwable var32) {
var8 = var32;
throw var32;
} finally {
if (inputStream != null) {
if (var8 != null) {
try {
inputStream.close();
} catch (Throwable var29) {
var8.addSuppressed(var29);
}
} else {
inputStream.close();
}
}
}
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
var8 = null;
try {
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} catch (Throwable var31) {
var8 = var31;
throw var31;
} finally {
if (inputStream != null) {
if (var8 != null) {
try {
inputStream.close();
} catch (Throwable var30) {
var8.addSuppressed(var30);
}
} else {
inputStream.close();
}
}
}
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
从这里看到 如果是 package 则直接放入到MapperRegistry 的 knownMappers 中,如果是其他的,则依次解析 resource,url,class;本文中通过 resource 进行资源的设置;
mapperParser.parse():
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
// mapper .xml 是否被解析过
// 解析mapper 标签 并将解析完成的结果放入到 Configuration 中的 Map mappedStatements
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
// mapper 接口层方法的绑定,解析了@Select @Insert 等标签
// 并将解析完成的结果放入到 Configuration 中的 Map mappedStatements
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
this.configurationElement(this.parser.evalNode(“/mapper”)):解析mapper.xml 中的sql
private void configurationElement(XNode context) {
try {
// 命名空间获取
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.isEmpty()) {
// 为Mapper xml 文件中的crud 构建一个个的MappedStatement 对象
this.builderAssistant.setCurrentNamespace(namespace);
// 解析 mapper.xml 引用其他 mapper.xml
this.cacheRefElement(context.evalNode("cache-ref"));
// 解析二级缓存
this.cacheElement(context.evalNode("cache"));
// 解析parameterMap
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析 resultMap
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析sql
this.sqlElement(context.evalNodes("/mapper/sql"));
// 解析 select|insert|update|delete
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
}
}
this.bindMapperForNamespace() 方法与sql 语句的绑定:
private void bindMapperForNamespace() {
String namespace = this.builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException var4) {
}
if (boundType != null && !this.configuration.hasMapper(boundType)) {
this.configuration.addLoadedResource("namespace:" + namespace);
// 闯入类路径进行解析
this.configuration.addMapper(boundType);
}
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
// sql 组装和绑定
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
public void parse() {
String resource = this.type.toString();
if (!this.configuration.isResourceLoaded(resource)) {
this.loadXmlResource();
this.configuration.addLoadedResource(resource);
this.assistant.setCurrentNamespace(this.type.getName());
this.parseCache();
this.parseCacheRef();
// 获取类中的所有方法
Method[] var2 = this.type.getMethods();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
Method method = var2[var4];
if (this.canHaveStatement(method)) {
// 如果不是桥接方法,也不是 Default 方法
if (this.getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent() && method.getAnnotation(ResultMap.class) == null) {
// 是否@Select 注解修饰,如果是则 进行参数的解析
this.parseResultMap(method);
}
try {
// 语句的解析
this.parseStatement(method);
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
}
this.parsePendingMethods();
}
public static void main(String[] args) throws IOException {
String resource ="mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
// 解析数据源 解析 xml 中的sql 语句
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 解析 Executor 执行器
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
Object abc = sqlSession.selectOne("com.example.springdabaihua.mapper.TestMapper.selectOne","123");
System.out.println("abc = " + abc);
Map abc1 =(Map) sqlSession.selectOne("com.example.springdabaihua.mapper.TestMapper.selectById","1");
abc1.entrySet().stream().forEach(e->{
System.out.println("e.toString() = " + e.toString());
});
System.out.println("abc1 = " + abc1);
}
sqlSession.selectOne() 方法执行sql:
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
List var6;
try {
// 从解析后的map 中 获取sql
MappedStatement ms = this.configuration.getMappedStatement(statement);
// 通过执行器执行并返回结果
var6 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);
} catch (Exception var10) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);
} finally {
ErrorContext.instance().reset();
}
return var6;
}
Spring 中主要通过 SqlSessionFactoryBuilder().build(reader) 方法完成 事务管理器,数据源以及sql 语句的解析和绑定,并将解析好的sql 放入到map 中,执行时通过key 获取具体的sql 语句交由执行器执行,然后返回执行结果。