一、Mybatis使用
1、新建mybatis配置文件
<?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>
<!-- 导入数据库配置文件的信息-->
<properties resource="jdbc.properties">
</properties>
<!-- 配置setting属性-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启了一个驼峰命名规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="cn.j3code.studyspring.mybatis.helloworld.entity"/>
</typeAliases>
<!-- 配置数据库-->
<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>
</environments>
<!-- mappers中注册我们所有写的dao接口的实现(映射)文件-->
<mappers>
<mapper resource="cn/j3code/studyspring/mybatis/helloworld/mapper/UserMapper.xml"/>
<!-- 如果映射文件有十几百个的话,可以用下面的全局注册
<package name="文件所在包路径"></package>
<package name="cn.liuliang.Dao"></package>
-->
</mappers>
</configuration>
2、新建UserMapper.xml用于测试
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.j3code.studyspring.mybatis.helloworld.mapper.UserMapper">
<select id="findAll" resultType="cn.j3code.studyspring.mybatis.helloworld.entity.User">
select * from t_user;
</select>
<resultMap id="user" type="cn.j3code.studyspring.mybatis.helloworld.entity.User">
<result property="name" javaType="string" column="name" jdbcType="VARCHAR"/>
</resultMap>
<select id="findById" parameterType="int" resultMap="user">
select * from t_user where id=#{id}
</select>
<insert id="insert" parameterType="user">
insert into t_user(id, name, age) values(#{id},#{name},#{age})
</insert>
</mapper>
3、实际使用样例
package cn.j3code.studyspring.mybatis;
import cn.j3code.studyspring.mybatis.helloworld.entity.User;
import cn.j3code.studyspring.mybatis.helloworld.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
@Slf4j
public class MyBatisTest {
private SqlSession sqlSession;
@BeforeEach
public void startUp() throws IOException{
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
SqlSessionFactory sqlSessionFactory = builder.build(resource.getInputStream());
sqlSession = sqlSessionFactory.openSession();
}
@Test
public void testMybatis() {
try {
// 第一个参数为xml文件的namespace+方法名
int rows = sqlSession.insert("cn.j3code.studyspring.mybatis.helloworld.mapper.UserMapper.insert"
, new User(null, "JERRY", 18));
sqlSession.commit();
log.info("rows -> {}", rows);
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally {
sqlSession.close();
}
}
@Test
public void testProcy() throws IOException{
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rows = mapper.insert(new User(null, "JERRY", 19));
sqlSession.commit();
log.info("rows -> {}", rows);
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally {
sqlSession.close();
}
}
}
二、常用类
1、XpathParser
Xpath可以简单的使用路径表达式在XML文档中选取节点元素。该工具核心作用就是解析xml文件,包括我们的配置文件和mapper文件。这项技术也是爬虫核心技术之一。在浏览器中我们可以轻松的获取一个标签的xpath。
粘贴到的路径如下:
/html/body/div[1]/div[1]/div[2]/div/div[2]/div[3]
含义:根目录下的html下的body下的第一个div下的第一个div下的第二个div下的第一个div下的第二个div下的第三个div。
测试demo
@Test
public void testxPathParser() throws Exception{
ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
XPathParser xPathParser = new XPathParser(resource.getInputStream());
XNode xNode = xPathParser.evalNode("/configuration/properties");
log.info("node -> {}", xNode);
}
2、Configuration类
a.测试组装Configuration
@Test
public void testConfiguration() throws Exception{
Configuration configuration = new Configuration();
Properties properties = new Properties();
properties.put("driver", "com.mysql.cj.jdbc.Driver");
properties.put("url", "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&useSSL=false&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
properties.put("username", "root");
properties.put("password", "root");
configuration.setVariables(properties);
// 添加日志实现和驼峰式命名
configuration.setLogImpl(Slf4jImpl.class);
configuration.setMapUnderscoreToCamelCase(true);
// 配置别名
configuration.getTypeAliasRegistry().registerAliases("cn.j3code.studyspring.mybatis.helloworld.entity");
// 配置环境
PooledDataSource pooledDataSource = new PooledDataSource();
pooledDataSource.setDriver(configuration.getVariables().getProperty("driver"));
pooledDataSource.setUrl(configuration.getVariables().getProperty("url"));
pooledDataSource.setUsername(configuration.getVariables().getProperty("username"));
pooledDataSource.setPassword(configuration.getVariables().getProperty("password"));
JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", jdbcTransactionFactory, pooledDataSource);
configuration.setEnvironment(environment);
StaticSqlSource sqlSource = new StaticSqlSource(configuration,
"insert into user(name, age) values ('jerry', 1000)");
MappedStatement mappedStatement = new MappedStatement.Builder(
configuration,
"study.insert",
sqlSource,
SqlCommandType.INSERT
).build();
// 扫描文件也是将xml中每个标签作为一个statement添加到configuration中
configuration.addMappedStatement(mappedStatement);
log.info("configuration -> {}", configuration);
}
b. 使用XMLConfigBuilder接下生成Configuration
@Test
public void testXMLConfigBuilder() throws Exception{
ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(resource.getInputStream());
Configuration configuration = xmlConfigBuilder.parse();
log.info("configuration -> {}", configuration);
}
// 进入parse方法
private final XPathParser parser;
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
// 解析标志位
this.parsed = true;
// 使用XPathParser获取configuration节点并调用parseConfiguration逐个解析
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
// 逐个获取解析节点
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
3、OGNL表达式
@Test
public void testOGNL() throws OgnlException {
User user = new User(1, "tom", 200);
Object name = Ognl.getValue("name != null && name == 'hh'", user);
log.info("name -> {}", name);
}
4、别名注册器
mybatis提供了TypeAliasRegistry作为别名注册器,同时默认注入了大量的基础类型的别名,他是配置类的一个成员变量:
protected final TypeAliasRegistry typeAliasRegistry;
this.typeAliasRegistry = new TypeAliasRegistry();
// 实现如下
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.ibatis.type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.io.Resources;
public class TypeAliasRegistry {
// key:别名, value: class
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap();
public TypeAliasRegistry() {
this.registerAlias("string", String.class);
this.registerAlias("byte", Byte.class);
this.registerAlias("long", Long.class);
this.registerAlias("short", Short.class);
this.registerAlias("int", Integer.class);
this.registerAlias("integer", Integer.class);
this.registerAlias("double", Double.class);
this.registerAlias("float", Float.class);
this.registerAlias("boolean", Boolean.class);
this.registerAlias("byte[]", Byte[].class);
this.registerAlias("long[]", Long[].class);
this.registerAlias("short[]", Short[].class);
this.registerAlias("int[]", Integer[].class);
this.registerAlias("integer[]", Integer[].class);
this.registerAlias("double[]", Double[].class);
this.registerAlias("float[]", Float[].class);
this.registerAlias("boolean[]", Boolean[].class);
this.registerAlias("_byte", Byte.TYPE);
this.registerAlias("_long", Long.TYPE);
this.registerAlias("_short", Short.TYPE);
this.registerAlias("_int", Integer.TYPE);
this.registerAlias("_integer", Integer.TYPE);
this.registerAlias("_double", Double.TYPE);
this.registerAlias("_float", Float.TYPE);
this.registerAlias("_boolean", Boolean.TYPE);
this.registerAlias("_byte[]", byte[].class);
this.registerAlias("_long[]", long[].class);
this.registerAlias("_short[]", short[].class);
this.registerAlias("_int[]", int[].class);
this.registerAlias("_integer[]", int[].class);
this.registerAlias("_double[]", double[].class);
this.registerAlias("_float[]", float[].class);
this.registerAlias("_boolean[]", boolean[].class);
this.registerAlias("date", Date.class);
this.registerAlias("decimal", BigDecimal.class);
this.registerAlias("bigdecimal", BigDecimal.class);
this.registerAlias("biginteger", BigInteger.class);
this.registerAlias("object", Object.class);
this.registerAlias("date[]", Date[].class);
this.registerAlias("decimal[]", BigDecimal[].class);
this.registerAlias("bigdecimal[]", BigDecimal[].class);
this.registerAlias("biginteger[]", BigInteger[].class);
this.registerAlias("object[]", Object[].class);
this.registerAlias("map", Map.class);
this.registerAlias("hashmap", HashMap.class);
this.registerAlias("list", List.class);
this.registerAlias("arraylist", ArrayList.class);
this.registerAlias("collection", Collection.class);
this.registerAlias("iterator", Iterator.class);
this.registerAlias("ResultSet", ResultSet.class);
}
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
} else {
String key = string.toLowerCase(Locale.ENGLISH);
Class value;
if (this.TYPE_ALIASES.containsKey(key)) {
value = (Class)this.TYPE_ALIASES.get(key);
} else {
value = Resources.classForName(string);
}
return value;
}
} catch (ClassNotFoundException var4) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + var4, var4);
}
}
public void registerAliases(String packageName) {
this.registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
Iterator var5 = typeSet.iterator();
while(var5.hasNext()) {
Class<?> type = (Class)var5.next();
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
this.registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
Alias aliasAnnotation = (Alias)type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
this.registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
} else {
String key = alias.toLowerCase(Locale.ENGLISH);
if (this.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
} else {
this.TYPE_ALIASES.put(key, value);
}
}
}
public void registerAlias(String alias, String value) {
try {
this.registerAlias(alias, Resources.classForName(value));
} catch (ClassNotFoundException var4) {
throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + var4, var4);
}
}
public Map<String, Class<?>> getTypeAliases() {
return Collections.unmodifiableMap(this.TYPE_ALIASES);
}
}
测试注册别名
@Test
public void testTypeAliasRegistry(){
TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
// 提供两种方法
// 1.注册单个别名
typeAliasRegistry.registerAlias("user", User.class);
// 2.使用包扫描的方式注册别名
typeAliasRegistry.registerAliases("com.study.entity");
}
// 注册单个别名方法
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
} else {
// 先将别名转为小写
String key = alias.toLowerCase(Locale.ENGLISH);
// 判断别名是否已经存在
if (this.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
} else {
// 不存在,则放入map
this.TYPE_ALIASES.put(key, value);
}
}
}
// 注册批量别名方法
public void registerAliases(String packageName) {
this.registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 获取传入包下面的所有类
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
Iterator var5 = typeSet.iterator();
while(var5.hasNext()) {
Class<?> type = (Class)var5.next();
// 不是匿名类 | 不是接口 | 不是内部类
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
this.registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
// 获取类型名称
String alias = type.getSimpleName();
// 类上有Alias注解的话获取Alias注解中配置的名称替换之
Alias aliasAnnotation = (Alias)type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
// 调用单个别名注册方法注册之
this.registerAlias(alias, type);
}
另外在Configuration初始化时,也会自动注册大量别名
public Configuration() {
this.safeResultHandlerEnabled = true;
this.multipleResultSetsEnabled = true;
this.useColumnLabel = true;
this.cacheEnabled = true;
this.useActualParamName = true;
this.localCacheScope = LocalCacheScope.SESSION;
this.jdbcTypeForNull = JdbcType.OTHER;
this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
this.defaultExecutorType = ExecutorType.SIMPLE;
this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
this.variables = new Properties();
this.reflectorFactory = new DefaultReflectorFactory();
this.objectFactory = new DefaultObjectFactory();
this.objectWrapperFactory = new DefaultObjectWrapperFactory();
this.lazyLoadingEnabled = false;
this.proxyFactory = new JavassistProxyFactory();
this.mapperRegistry = new MapperRegistry(this);
this.interceptorChain = new InterceptorChain();
this.typeHandlerRegistry = new TypeHandlerRegistry();
this.typeAliasRegistry = new TypeAliasRegistry();
this.languageRegistry = new LanguageDriverRegistry();
this.mappedStatements = new StrictMap("Mapped Statements collection");
this.caches = new StrictMap("Caches collection");
this.resultMaps = new StrictMap("Result Maps collection");
this.parameterMaps = new StrictMap("Parameter Maps collection");
this.keyGenerators = new StrictMap("Key Generators collection");
this.loadedResources = new HashSet();
this.sqlFragments = new StrictMap("XML fragments parsed from previous mappers");
this.incompleteStatements = new LinkedList();
this.incompleteCacheRefs = new LinkedList();
this.incompleteResultMaps = new LinkedList();
this.incompleteMethods = new LinkedList();
this.cacheRefMap = new HashMap();
// 注册别名
this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
this.languageRegistry.register(RawLanguageDriver.class);
}
5、类型转换器
5.1 TypeHandler
TypeHandler负责对查询结果进行类型转换,我们可以使用ResultSet的原生方法对结果进行类型转换。我们当然可以使用switch-case语法通过判断类型选择调用方法如下:
public Object getResult(JDBCType jdbcType, ResultSet rs, String columnName) throws Exception{
Object object = null;
switch (jdbcType){
case INTEGER:
object = rs.getInt(columnName);
case BIGINT:
object = rs.getLong(columnName);
case VARCHAR:
object = rs.getString(columnName);
// 省略其他众多情况
}
return object;
}
但这样是面向过程的编码风格,对扩展不利,以后每次新增类型都要修改代码。因此mybatis抽象出一个TypeHandler作为顶层接口,对转换工作做了模型抽离,如下:
public interface TypeHandler<T> {
void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
T getResult(ResultSet var1, String var2) throws SQLException;
T getResult(ResultSet var1, int var2) throws SQLException;
T getResult(CallableStatement var1, int var2) throws SQLException;
}
同时实现了大量的实现策略(策略设计模式),核心策略在构造时注册,新增的通过配置文件添加,极大降低了系统耦合性。
5.2、TypeHandlerRegistry
mybatis提供了一个专门的注册器用来注册TypeHandler。TypeHandlerRegistry也是配置类的一个成员变量:
protected final TypeHandlerRegistry typeHandlerRegistry;
TypeHandlerRegistry的作用如下:
- 一是为了注册存储类型转换器
- 二是为了根据javaType或者jdbcType查询合适的TypeHandler
当然,我们可以思考一下javaType–jdbcType以及TypeHandler三者的关系:
事实上,我们一般情况会根据BaseTypeHandler的泛型来确定该处理器的javaType,从而确定使用的是rs的getXXX和setXXX方法,jdbcType本质上没有什么具体的作用,更多的是作为一种标识,用来确定使用一个唯一的handler,在定义TypeHandler时需要指定泛型,也就意味着javaType一定会被指定,那么我们选取TypeHandler时就会有以下两种情况:
- 1、指定了javaType,也指定了jdbcType,我们可以匹配一个确定的TypeHandler。
- 2、指定了javaType,未指定jdbcType,我们可以匹配更多TypeHandler,但我们只选一个作为默认
TypeHandlerRegistry源码:
public final class TypeHandlerRegistry {
// jdbcType与handler映射,jdbcType为一个枚举类
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap(JdbcType.class);
// javaType与map(jdbc-handler map)的映射,一个javaType可能对应多个jdbcType
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap();
// 兜底方案,当所有typeHandler无法处理时,尝试采用该handler处理
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
// 所有的类型转换器,
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap();
// 存储只知道jdbcType不知道javaType
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = new HashMap();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
// 构造方法
public TypeHandlerRegistry() {
// 传入java类型
this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
this.register((JdbcType)JdbcType.TINYINT, (TypeHandler)(new ByteTypeHandler()));
this.register((Class)Short.class, (TypeHandler)(new ShortTypeHandler()));
this.register((Class)Short.TYPE, (TypeHandler)(new ShortTypeHandler()));
this.register((JdbcType)JdbcType.SMALLINT, (TypeHandler)(new ShortTypeHandler()));
this.register((Class)Integer.class, (TypeHandler)(new IntegerTypeHandler()));
this.register((Class)Integer.TYPE, (TypeHandler)(new IntegerTypeHandler()));
this.register((JdbcType)JdbcType.INTEGER, (TypeHandler)(new IntegerTypeHandler()));
this.register((Class)Long.class, (TypeHandler)(new LongTypeHandler()));
this.register((Class)Long.TYPE, (TypeHandler)(new LongTypeHandler()));
this.register((Class)Float.class, (TypeHandler)(new FloatTypeHandler()));
this.register((Class)Float.TYPE, (TypeHandler)(new FloatTypeHandler()));
this.register((JdbcType)JdbcType.FLOAT, (TypeHandler)(new FloatTypeHandler()));
this.register((Class)Double.class, (TypeHandler)(new DoubleTypeHandler()));
this.register((Class)Double.TYPE, (TypeHandler)(new DoubleTypeHandler()));
this.register((JdbcType)JdbcType.DOUBLE, (TypeHandler)(new DoubleTypeHandler()));
this.register((Class)Reader.class, (TypeHandler)(new ClobReaderTypeHandler()));
this.register((Class)String.class, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
this.register((Class)String.class, JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
this.register((Class)String.class, JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((Class)String.class, JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((Class)String.class, JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
this.register((JdbcType)JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
this.register((JdbcType)JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
this.register((JdbcType)JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
this.register((JdbcType)JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
this.register((JdbcType)JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((JdbcType)JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((JdbcType)JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
this.register((Class)Object.class, JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
this.register((JdbcType)JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
this.register((Class)BigInteger.class, (TypeHandler)(new BigIntegerTypeHandler()));
this.register((JdbcType)JdbcType.BIGINT, (TypeHandler)(new LongTypeHandler()));
this.register((Class)BigDecimal.class, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.REAL, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.DECIMAL, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.NUMERIC, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((Class)InputStream.class, (TypeHandler)(new BlobInputStreamTypeHandler()));
this.register((Class)Byte[].class, (TypeHandler)(new ByteObjectArrayTypeHandler()));
this.register((Class)Byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
this.register((Class)Byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
this.register((Class)byte[].class, (TypeHandler)(new ByteArrayTypeHandler()));
this.register((Class)byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
this.register((Class)byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
this.register((JdbcType)JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
this.register((JdbcType)JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
this.register(Object.class, this.UNKNOWN_TYPE_HANDLER);
this.register(Object.class, JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
this.register(JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
this.register((Class)Date.class, (TypeHandler)(new DateTypeHandler()));
this.register((Class)Date.class, JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
this.register((Class)Date.class, JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
this.register((JdbcType)JdbcType.TIMESTAMP, (TypeHandler)(new DateTypeHandler()));
this.register((JdbcType)JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
this.register((JdbcType)JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
this.register((Class)java.sql.Date.class, (TypeHandler)(new SqlDateTypeHandler()));
this.register((Class)Time.class, (TypeHandler)(new SqlTimeTypeHandler()));
this.register((Class)Timestamp.class, (TypeHandler)(new SqlTimestampTypeHandler()));
if (Jdk.dateAndTimeApiExists) {
Java8TypeHandlersRegistrar.registerDateAndTimeHandlers(this);
}
this.register((Class)Character.class, (TypeHandler)(new CharacterTypeHandler()));
this.register((Class)Character.TYPE, (TypeHandler)(new CharacterTypeHandler()));
}
// 对于java类型的处理
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
this.register((Type)javaType, (TypeHandler)typeHandler);
}
// ---->处理
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
// 首先从typeHandler中获取一个注解,这个注解中包含一个数组,这个数组为jdbcType
MappedJdbcTypes mappedJdbcTypes = (MappedJdbcTypes)typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
JdbcType[] var4 = mappedJdbcTypes.value();
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
JdbcType handledJdbcType = var4[var6];
// 将每一个javaType,jdbcType与handler进行注册
this.register(javaType, handledJdbcType, typeHandler);
}
// 如果没有注解,或者注解里没有内容,则以javaType的形式进行注册
if (mappedJdbcTypes.includeNullJdbcType()) {
this.register((Type)javaType, (JdbcType)null, (TypeHandler)typeHandler);
}
} else {
this.register((Type)javaType, (JdbcType)null, (TypeHandler)typeHandler);
}
}
// 最终处理
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
// 先根据jdbcType拿到所有map
Map<JdbcType, TypeHandler<?>> map = (Map)this.TYPE_HANDLER_MAP.get(javaType);
if (map == null) {
map = new HashMap();
this.TYPE_HANDLER_MAP.put(javaType, map);
}
// 添加后重新塞入
((Map)map).put(jdbcType, handler);
}
// 最后将javaType和handler放入all中
this.ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
}
自定义实现stringTypeHandler,用于理解 MappedJdbcTypes
// 有多个jdbcType与之对应,但具体的实现(handler)对应一个
@MappedJdbcTypes({JdbcType.CHAR,JdbcType.VARCHAR})
public class StringTypeHandler implements TypeHandler<String> {
@Override
public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
}
// 例子中只重写了该方法
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getResult(ResultSet resultSet, int i) throws SQLException {
return null;
}
@Override
public String getResult(CallableStatement callableStatement, int i) throws SQLException {
return null;
}
}
6、反射工具
mybatis封装后的反射工具可以极其简单实现简单属性以及复杂属性的get和set操作,这在获取参数成员变量,封装返回结果等操作时很有用:
@Test
public void testMetaObject(){
Object user = new User(1, "hhh", 42, new House(120, 1234, 4331));
MetaObject metaObject = MetaObject.forObject(user
, new DefaultObjectFactory()
, new DefaultObjectWrapperFactory()
, new DefaultReflectorFactory());
// 1、修改user属性
metaObject.setValue("house.area", 150);
metaObject.setValue("house.latitude", 9876);
System.out.println(user);
// 2、修改当前对象的值
metaObject.setValue("name", "aaa");
System.out.println(user);
// 3、取值 area = #{house.area}
Object value = metaObject.getValue("house.area");
System.out.println(value);
value = metaObject.getValue("house");
System.out.println(value);
value = metaObject.getValue("name");
System.out.println(value);
}
6.1、MetaObject
public class MetaObject {
// 原始对象
private final Object originalObject;
// 包装后的对象
private final ObjectWrapper objectWrapper;
// 以下三个工厂我们使用默认就可以了,当然我们可以自由设定
// 用来实例化对象的工厂
private final ObjectFactory objectFactory;
// 用来获取一个包装的对象工厂,给我们扩展使用
private final ObjectWrapperFactory objectWrapperFactory;
// 可以为每一个类生成Reflector,他提供了反射的基本能力
private final ReflectorFactory reflectorFactory;
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
// 1、不包装
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper)object;
// 2、由工厂进行包装
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
// 3、框架自行包装
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map)object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection)object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
// 对外公开构建方法
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
return object == null ? SystemMetaObject.NULL_META_OBJECT : new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
(1)、ObjectFactory
ObjectFactory提供了实例化一个类的能力,可以根据class类型构建对象,默认采用反射的方式:
// DefaultObjectFactory
<T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor constructor;
if (constructorArgTypes != null && constructorArgs != null) {
constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} else {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
} catch (Exception var9) {
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
Iterator var6 = constructorArgTypes.iterator();
while(var6.hasNext()) {
Class<?> argType = (Class)var6.next();
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1);
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
Iterator var11 = constructorArgs.iterator();
while(var11.hasNext()) {
Object argValue = var11.next();
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1);
}
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
}
}
(2)、ObjectWrapperFactory
mybatis提供了ObjectWrapperFactory可以对Object的包装过程实现自定义,其默认实现如下,默认实现不会做任何事情,存在的意义是为了扩展,实现自定义
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {
public DefaultObjectWrapperFactory() {
}
public boolean hasWrapperFor(Object object) {
// 默认返回不能包装
return false;
}
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
// 永远不应该调用DefaultObjectWrapperFactory来提供ObjectWrapper
throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
}
}
(3)、ObjectWrapper
在上面的例子中,操作对象大多是通过ObjectWapper进行的,他的继承结构如下,以BeanWrapper为例,分析下原理
从源码中得知,BeanWrapper维护了一个object实例本身,一个metaClass(用来进行反射操作)
public class BeanWrapper extends BaseWrapper {
private final Object object;
private final MetaClass metaClass;
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
this.object = object;
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
}
MetaClass的构建过程如下,一个metaclass实例维护了一个reflectorFactory用来提供reflector,而reflector是真正的反射工具
public class MetaClass {
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
}
(4)、ReflectorFactory
ReflectorFactory维护了我们需要的Reflector,并提供了创建方式,其默认实现是DefaultReflectorFactory,ReflectorFactory也可以进行自定义。
public class DefaultReflectorFactory implements ReflectorFactory {
private boolean classCacheEnabled = true;
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap();
public DefaultReflectorFactory() {
}
public boolean isClassCacheEnabled() {
return this.classCacheEnabled;
}
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
public Reflector findForClass(Class<?> type) {
if (this.classCacheEnabled) {
Reflector cached = (Reflector)this.reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
this.reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
}
(5)、Reflector
Reflector是一个反射工具,里边缓存了一个类的类型,setter方法,getter方法,默认构造器:
public class Reflector {
private final Class<?> type;
private final String[] readablePropertyNames;
private final String[] writeablePropertyNames;
private final Map<String, Invoker> setMethods = new HashMap();
private final Map<String, Invoker> getMethods = new HashMap();
private final Map<String, Class<?>> setTypes = new HashMap();
private final Map<String, Class<?>> getTypes = new HashMap();
private Constructor<?> defaultConstructor;
private Map<String, String> caseInsensitivePropertyMap = new HashMap();
// 构建过程
public Reflector(Class<?> clazz) {
this.type = clazz;
this.addDefaultConstructor(clazz);
this.addGetMethods(clazz);
this.addSetMethods(clazz);
this.addFields(clazz);
this.readablePropertyNames = (String[])this.getMethods.keySet().toArray(new String[this.getMethods.keySet().size()]);
this.writeablePropertyNames = (String[])this.setMethods.keySet().toArray(new String[this.setMethods.keySet().size()]);
String[] var2 = this.readablePropertyNames;
int var3 = var2.length;
int var4;
String propName;
for(var4 = 0; var4 < var3; ++var4) {
propName = var2[var4];
this.caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
var2 = this.writeablePropertyNames;
var3 = var2.length;
for(var4 = 0; var4 < var3; ++var4) {
propName = var2[var4];
this.caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
}
6.2、复杂属性的赋值
metaObject.setValue("house.area", 150);
(1)实现过程
// setter,赋值操作
public void setValue(String name, Object value) {
// 属性解析器,用于解析属性
PropertyTokenizer prop = new PropertyTokenizer(name);
// 如果是非简单属性,如:house.area
if (prop.hasNext()) {
// 如果area没有值,需要先实例化area
MetaObject metaValue = this.metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null && prop.getChildren() != null) {
// 如果value为null,不要实例化子属性
return;
}
// 使用objectFactory构建一个user实例
// objectFactory的默认实现就是使用反射创建一个实例
metaValue = this.objectWrapper.instantiatePropertyValue(name, prop, this.objectFactory);
}
// metaValue:house,prop.getChildren()--> area,给area赋值
metaValue.setValue(prop.getChildren(), value);
} else {
// 普通属性的赋值
this.objectWrapper.set(prop, value);
}
}
(2)属性分析器(迭代器设计模式)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.ibatis.reflection.property;
import java.util.Iterator;
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private final String indexedName;
private String index;
private final String children;
public PropertyTokenizer(String fullname) {
// '.'
int delim = fullname.indexOf(46);
// 说明这个属性中有,如user.house
if (delim > -1) {
// 截取.之前的内容作为name,此处是house
this.name = fullname.substring(0, delim);
// .之后的内容作为children,此处是area
this.children = fullname.substring(delim + 1);
} else {
// 否则就是fullname
this.name = fullname;
this.children = null;
}
this.indexedName = this.name;
// 包含 [
delim = this.name.indexOf(91);
if (delim > -1) {
this.index = this.name.substring(delim + 1, this.name.length() - 1);
this.name = this.name.substring(0, delim);
}
}
// 继续分析子属性
public PropertyTokenizer next() {
return new PropertyTokenizer(this.children);
}
}
(3)构建成员变量实例
house如果没有实例化,但我们试图为其赋值,就必须通过反射进行实例化
metaValue = this.objectWrapper.instantiatePropertyValue(name, prop, this.objectFactory);
以BeanWapper的实现为例:
public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
// 通过属性的名字获取属性类型,
Class<?> type = this.getSetterType(prop.getName());
try {
// 根据类型创建一个实例
Object newObject = objectFactory.create(type);
// 构建一个metaObject
MetaObject metaValue = MetaObject.forObject(newObject, this.metaObject.getObjectFactory(), this.metaObject.getObjectWrapperFactory(), this.metaObject.getReflectorFactory());
// 赋值
this.set(prop, newObject);
return metaValue;
} catch (Exception var7) {
throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + var7.toString(), var7);
}
}
public Class<?> getSetterType(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = this.metaObject.metaObjectForProperty(prop.getIndexedName());
return metaValue == SystemMetaObject.NULL_META_OBJECT ? this.metaClass.getSetterType(name) : metaValue.getSetterType(prop.getChildren());
} else {
return this.metaClass.getSetterType(name);
}
}
7、ErrorContext
在mybatis中为了更好的定位异常或错误,特封装ErrorContext类,用于描述错误信息,他比传统异常机制要好很多,输出的内容可以自由定制,他使用了ThreadLocal,可以非常好的将问题定位到某个线程,同时保证了线程安全。
源码如下:
public class ErrorContext {
private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal();
private ErrorContext stored;
private String resource;
private String activity;
private String object;
private String message;
private String sql;
private Throwable cause;
public static ErrorContext instance() {
ErrorContext context = (ErrorContext)LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
public ErrorContext store() {
this.stored = this;
LOCAL.set(new ErrorContext());
return (ErrorContext)LOCAL.get();
}
}
ErrorContext负责封装异常上下文,而ExceptionFactory则负责输出异常上下文:
public class ExceptionFactory {
private ExceptionFactory() {
}
public static RuntimeException wrapException(String message, Exception e) {
return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
}
}
使用示例如下:
@Test
public void testErrorContext(){
ExecutorService service = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
final int j = i;
service.execute(()-> {
try {
// ErrorContext存储在threadLocal中
ErrorContext.instance()
.activity("在第[" + j + "]流程中。")
.object(this.getClass().getName())
.sql("select xxx fromfd user")
.resource("user.xml");
if(new Random().nextInt(10) > 6){
int m = 1/0;
}
}catch (Exception e){
// 该方法从threadLocal中取出ErrorContext
throw ExceptionFactory.wrapException("sql has errors", e);
}
countDownLatch.countDown();
});
}
}
三、sql封装
1、MappedStatement
MappedStatement翻译过来是映射语句,其实就是用来封装sql语句的,每个sql标签都会映射成为一个MappedStatement实例,如下是一个sql标签的xml表现形式
<insert id="insert" parameterType="account" flushCache="" keyColumn="" parameterMap=""
useGeneratedKeys="" databaseId="" keyProperty="" lang="" statementType="" timeout="">
insert into account(username, money) values(#{username},#{money})
</insert>
属性 | 描述 |
---|---|
id | 在命名空间中唯一标识符,可以用来引用这条语句 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为MyBatis可以通过类型处理器(TypeHandler推断出具体传入语句的参数,默认值为未设置(unset) |
parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。 |
resultType | 期望从这条语句返回结果的类全限定名或别名,注意,如果返回的是集合,那应该设置为集合内元素的类型,而不是集合本身。resultType和resultMap之间只能同时使用一个 |
resultMap | 对外部resultMap的命名引用 |
flushCache | 将其设置为true后,只要语句被调用都会导致本地缓存和二级缓存被清空,默认值:false |
useCache | 将其设置为true后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对select元素为true。需要前提开启二级缓存 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动) |
fetchSize | 这是一个驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。默认值为未设置(unset)(依赖驱动) |
statementType | 可选 STATEMENT,PREPARED,或 CALLABLE。这会让mybatis分别使用Statement,PreparedStatement 或CallableStatement。默认值:PREPARED |
resultSetType | FORWARD_ONLY(结果即指针只能向前),SCROLL_INSENSITIVE(结果集指针可以滚动,可执行上一个、下一个、回到第一个等操作)或 DEFAULT(等价于 unset)中的一个,默认值为 unset(依赖数据库驱动) |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis会加载所有不带databaseId或匹配当前databaseId的语句;如果带和不带的语句都有,则不带的会被忽略 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句:如果为true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false |
resultSets | 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔 |
public final class MappedStatement {
// 源自于哪个mapper文件
private String resource;
// 配置文件
private Configuration configuration;
// id
private String id;
// 每次从服务器获取的数量
private Integer fetchSize;
private Integer timeout;
// STATEMENT,PREPARED,或 CALLABLE
private StatementType statementType;
private ResultSetType resultSetType;
// 对sql的包装
private SqlSource sqlSource;
// 缓存
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
// 是否需要刷新缓存
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
// UNKNOW,INSERT,UPDATE,DELETE,SELECT
private SqlCommandType sqlCommandType;
// 主键生成器
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
// 是否存在嵌套查询的结果集
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
// 语言驱动
private LanguageDriver lang;
// resultSet
private String[] resultSets;
}
封装MappedStatement样例
@Test
public void teseMappedStatement() throws Exception{
// 构建configuration
InputStream inputStream = Resources.getResourceAsStream("myBatisConfig.xml");
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream);
Configuration configuration = xmlConfigBuilder.parse();
String sql = "select id,name,age from t_user";
SqlSource sqlSource = new StaticSqlSource(configuration, sql);
MappedStatement.Builder builder = new MappedStatement.Builder(configuration, "insert", sqlSource, SqlCommandType.SELECT);
// 设置结果集
List<ResultMapping> resultMappingList = new ArrayList<>();
ResultMapping.Builder idBuilder = new ResultMapping.Builder(
configuration, "id", "id", new IntegerTypeHandler()
);
ResultMapping.Builder nameBuilder = new ResultMapping.Builder(
configuration, "name", "name",String.class
);
ResultMapping.Builder ageBuilder = new ResultMapping.Builder(
configuration, "id", "age", Integer.class
);
resultMappingList.add(idBuilder.build());
resultMappingList.add(nameBuilder.build());
resultMappingList.add(ageBuilder.build());
ResultMap resultMap = new ResultMap.Builder(
configuration, "myResultMap", User.class, resultMappingList
).build();
builder.resultMaps(Arrays.asList(resultMap));
builder.fetchSize(300);
builder.databaseId("mysql");
MappedStatement mappedStatement = builder.build();
log.info("mappedStatement = " + mappedStatement);
// 1、去除标签 <if> <where>
// 2、替换占位符 #{id} --> select * from user where id = ?
BoundSql boundSql = mappedStatement.getBoundSql(new User(1, null, 33));
String sql1 = boundSql.getSql();
System.out.println(sql1);
}
StringJoiner
@Test
public void testStringJoiner(){
StringJoiner stringJoiner = new StringJoiner("-");
stringJoiner.add("1");
stringJoiner.add("2");
stringJoiner.add("3");
stringJoiner.add("4");
stringJoiner.add("5");
System.out.println(stringJoiner);
}
mappedStatement.getBoundSql方法解析
public BoundSql getBoundSql(Object parameterObject) {
// 有四个实现,常用的是DynamicSqlSource和StaticSqlSource
BoundSql boundSql = this.sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(this.configuration, boundSql.getSql(), this.parameterMap.getParameterMappings(), parameterObject);
}
Iterator var4 = boundSql.getParameterMappings().iterator();
while(var4.hasNext()) {
ParameterMapping pm = (ParameterMapping)var4.next();
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = this.configuration.getResultMap(rmId);
if (rm != null) {
this.hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
StaticSqlSource.getBoundSql
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(this.configuration, this.sql, this.parameterMappings, parameterObject);
}
DynamicSqlSource
public class DynamicSqlSource implements SqlSource {
private final Configuration configuration;
private final SqlNode rootSqlNode;
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(this.configuration, parameterObject);
this.rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
Iterator var7 = context.getBindings().entrySet().iterator();
while(var7.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry)var7.next();
boundSql.setAdditionalParameter((String)entry.getKey(), entry.getValue());
}
return boundSql;
}
}
DynamicContext
// 编译解析sql语句所需要的上下文
public class DynamicContext {
public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";
// 当前ognl上下文
private final ContextMap bindings;
// 用于append sql
private final StringBuilder sqlBuilder = new StringBuilder();
private int uniqueNumber = 0;
// 初始化ognl
static {
OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
}
// 构造方法
public DynamicContext(Configuration configuration, Object parameterObject) {
if (parameterObject != null && !(parameterObject instanceof Map)) {
// 初始化metaObject,方便赋值取值等操作
MetaObject metaObject = configuration.newMetaObject(parameterObject);
this.bindings = new ContextMap(metaObject);
} else {
this.bindings = new ContextMap((MetaObject)null);
}
// 将原始param对象保存在bindings中,
this.bindings.put("_parameter", parameterObject);
this.bindings.put("_databaseId", configuration.getDatabaseId());
}
public String getSql() {
return this.sqlBuilder.toString().trim();
}
public void appendSql(String sql) {
this.sqlBuilder.append(sql);
this.sqlBuilder.append(" ");
}
// ognl表达式所需要的上下文
static class ContextMap extends HashMap<String, Object> {
private static final long serialVersionUID = 2977601501966151582L;
private MetaObject parameterMetaObject;
public ContextMap(MetaObject parameterMetaObject) {
this.parameterMetaObject = parameterMetaObject;
}
public Object get(Object key) {
String strKey = (String)key;
if (super.containsKey(strKey)) {
return super.get(strKey);
} else {
return this.parameterMetaObject != null ? this.parameterMetaObject.getValue(strKey) : null;
}
}
}
}
SqlNode
sqlNode封装了XML中的sql节点,对于多个节点的封装,我们统一使用MixedSqlNode,其继承结构如下:
public interface SqlNode {
boolean apply(DynamicContext var1);
}
public class MixedSqlNode implements SqlNode {
// 每一个标签的集合
private final List<SqlNode> contents;
public MixedSqlNode(List<SqlNode> contents) {
this.contents = contents;
}
public boolean apply(DynamicContext context) {
Iterator var2 = this.contents.iterator();
// 遍历每一个标签,并传递context进行拼接
while(var2.hasNext()) {
SqlNode sqlNode = (SqlNode)var2.next();
sqlNode.apply(context);
}
return true;
}
}
每一个sqlNode都有不同的职责,其中IfSqlNode使用ognl表达式解析test结果,如果为true,会继续解析if标签中的其他sqlNode,如果为false,直接丢弃。where和set标签主要用于处理前后缀,共同继承自trimSqlNode,trimSqlNode用于除去前后缀,where和set用于定义前后缀的内容。
获取sqlSource(将XNode解析为sqlSource)
<select>
select * from t_user
<where>
<if test="id != null">
and id = #{id}
</if>
</where>
</select>
@Test
public void testXMLLanguageDriver() throws Exception{
// 构建configuration
InputStream inputStream = Resources.getResourceAsStream("myBatisConfig.xml");
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream);
Configuration configuration = xmlConfigBuilder.parse();
inputStream = Resources.getResourceAsStream("cn/j3code/studyspring/mybatis/helloworld/mapper/sql.xml");
XPathParser xPathParser = new XPathParser(inputStream);
XNode xNode = xPathParser.evalNode("/select");
XMLLanguageDriver xmlLanguageDriver = new XMLLanguageDriver();
SqlSource sqlSource = xmlLanguageDriver.createSqlSource(configuration, xNode, User.class);
System.out.println(sqlSource);
}
最终构造的sqlSource结构
四、配置类的读取流程
@BeforeEach
public void startUp() throws IOException{
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
ClassPathResource resource = new ClassPathResource("myBatisConfig.xml");
// 1、入口
SqlSessionFactory sqlSessionFactory = builder.build(resource.getInputStream());
sqlSession = sqlSessionFactory.openSession();
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 2、parser.parse()最终返回Configuration
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
// 3、
// 将解析标志设为true
this.parsed = true;
// 获取配置xml中configuration节点
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
private void parseConfiguration(XNode root) {
// 4、逐个解析每一个标签并赋值
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.eva lNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
PooledDataSourceFactory
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
PooledDataSource
public class PooledDataSource implements DataSource {
private static final Log log = LogFactory.getLog(PooledDataSource.class);
private final PoolState state = new PoolState(this);
private final UnpooledDataSource dataSource;
// 最大连接活跃数
protected int poolMaximumActiveConnections = 10;
// 最大空闲连接数
protected int poolMaximumIdleConnections = 5;
// 连接最大使用时间
protected int poolMaximumCheckoutTime = 20000;
// 最大等待时间
protected int poolTimeToWait = 20000;
// 可容忍的最大坏连接数
protected int poolMaximumLocalBadConnectionTolerance = 3;
// 探活
protected String poolPingQuery = "NO PING QUERY SET";
protected boolean poolPingEnabled;
protected int poolPingConnectionsNotUsedFor;
private int expectedConnectionTypeCode;
public PooledDataSource() {
this.dataSource = new UnpooledDataSource();
}
public PooledDataSource(UnpooledDataSource dataSource) {
this.dataSource = dataSource;
}
// 获取连接
public Connection getConnection() throws SQLException {
return this.popConnection(this.dataSource.getUsername(), this.dataSource.getPassword()).getProxyConnection();
}
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
while(conn == null) {
synchronized(this.state) {
PoolState var10000;
// 空闲的连接不为空,直接从空闲连接中拿一个返回
if (!this.state.idleConnections.isEmpty()) {
conn = (PooledConnection)this.state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
// 当前活跃数<池子允许的最大活跃数
} else if (this.state.activeConnections.size() < this.poolMaximumActiveConnections) {
conn = new PooledConnection(this.dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
// 当前活跃数=池子允许的最大活跃数
} else {
// 拿到最开始的连接(最先创建的连接)
PooledConnection oldestActiveConnection = (PooledConnection)this.state.activeConnections.get(0);
// 判断最老的连接已经使用的时间
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
// 若这个时间>允许使用的最大时间
if (longestCheckoutTime > (long)this.poolMaximumCheckoutTime) {
// state中相应状态连接数+1
++this.state.claimedOverdueConnectionCount;
var10000 = this.state;
var10000.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
var10000 = this.state;
var10000.accumulatedCheckoutTime += longestCheckoutTime;
// 从activeConnections中移除该连接
this.state.activeConnections.remove(oldestActiveConnection);
// 若该连接不是自动提交的
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
// 将操作进行回滚
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException var16) {
log.debug("Bad connection. Could not roll back");
}
}
// 重新new一个新的连接
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
// 将旧的连接置为不可用
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// 第一个连接也没超时,等待
try {
if (!countedWait) {
// 必须去等待数量+1
++this.state.hadToWaitCount;
// 等待标志置为true
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + this.poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
// 开始等待
this.state.wait((long)this.poolTimeToWait);
var10000 = this.state;
var10000.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException var17) {
break;
}
}
}
// 已经获得连接
if (conn != null) {
// 测试连接是否可用(ping)
if (conn.isValid()) {
// 连接是不是自动提交
if (!conn.getRealConnection().getAutoCommit()) {
// 先回滚
conn.getRealConnection().rollback();
}
// 设置连接类型
conn.setConnectionTypeCode(this.assembleConnectionTypeCode(this.dataSource.getUrl(), username, password));
// 设置连接使用时间
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
// 修改池状态
this.state.activeConnections.add(conn);
++this.state.requestCount;
var10000 = this.state;
var10000.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
// 如果连接不可用
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
// 对坏连接进行统计
++this.state.badConnectionCount;
++localBadConnectionCount;
conn = null;
// 如果本次最大坏的连接数>最大的空闲连接数+最大可容忍的坏的连接数
if (localBadConnectionCount > this.poolMaximumIdleConnections + this.poolMaximumLocalBadConnectionTolerance) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
// 抛异常
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
} else {
return conn;
}
}
}
PoolState
// 保存了当前pool的一些状态
public class PoolState {
protected PooledDataSource dataSource;
// 空闲的连接
protected final List<PooledConnection> idleConnections = new ArrayList();
// 活跃的连接
protected final List<PooledConnection> activeConnections = new ArrayList();
// 当前池子被请求的次数
protected long requestCount = 0L;
// 累计的请求时间
protected long accumulatedRequestTime = 0L;
// 累计的使用时间(连接在外部被使用的时间)
protected long accumulatedCheckoutTime = 0L;
// 过期未还的连接数量
protected long claimedOverdueConnectionCount = 0L;
// 累计的未还的连接的使用时间
protected long accumulatedCheckoutTimeOfOverdueConnections = 0L;
// 累计的等待时长
protected long accumulatedWaitTime = 0L;
// 必须去等待的数量
protected long hadToWaitCount = 0L;
// 坏的连接数量
protected long badConnectionCount = 0L;
public PoolState(PooledDataSource dataSource) {
this.dataSource = dataSource;
}
}
归还连接
// org.apache.ibatis.datasource.pooled.PooledConnection#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 若调用的方法为close
if ("close".hashCode() == methodName.hashCode() && "close".equals(methodName)) {
this.dataSource.pushConnection(this);
return null;
} else {
try {
if (!Object.class.equals(method.getDeclaringClass())) {
this.checkConnection();
}
return method.invoke(this.realConnection, args);
} catch (Throwable var6) {
throw ExceptionUtil.unwrapThrowable(var6);
}
}
}
// 归还连接
protected void pushConnection(PooledConnection conn) throws SQLException {
synchronized(this.state) {
// 从活跃连接中删除该连接
this.state.activeConnections.remove(conn);
// 当前连接可用
if (conn.isValid()) {
PoolState var10000;
// 若空闲连接的数量<容器允许的最大的空闲连接数 &&
if (this.state.idleConnections.size() < this.poolMaximumIdleConnections && conn.getConnectionTypeCode() == this.expectedConnectionTypeCode) {
// 做统计
var10000 = this.state;
var10000.accumulatedCheckoutTime += conn.getCheckoutTime();
// 回滚连接
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 重新包装
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
// 将连接放到空闲连接
this.state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
// 将原来连接置为不可用
conn.invalidate();
if (log.isDebugEnabled()) {
log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
}
// 唤醒state上等待的线程
this.state.notifyAll();
} else {
// 空闲的连接已经满了
// 统计
var10000 = this.state;
var10000.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 将连接断开
conn.getRealConnection().close();
if (log.isDebugEnabled()) {
log.debug("Closed connection " + conn.getRealHashCode() + ".");
}
// 该连接置为不可用
conn.invalidate();
}
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
}
++this.state.badConnectionCount;
}
}
}
解析Mappers标签
// 解析mapper入口
this.mapperElement(root.evalNode("mappers"));
// 可配置方式1
<mapper resource="cn/mybatis/helloworld/mapper/UserMapper.xml"/>
// 可配置方式2
<package name="文件所在包路径"></package>
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;
// 判断子元素是是否是package
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");
XMLMapperBuilder mapperParser;
InputStream inputStream;
// 配的resource
if (resource != null && url == null && mapperClass == null) {
// 解析资源时,在当前线程下封装ErrorContext,若出问题,则告知可能该resource有问题
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
// 配的url
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} 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
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
mapperParser.parse();
public void parse() {
// 若该mapper未被解析,则进行解析
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));
// 将资源即mapper.xml放入configuration(下次需要加载时直接拿)
this.configuration.addLoadedResource(this.resource);
// 将mapper和命名空间绑定
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
private void configurationElement(XNode context) {
// 逐个获取标签进行解析
try {
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
this.builderAssistant.setCurrentNamespace(namespace);
this.cacheRefElement(context.evalNode("cache-ref"));
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
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. Cause: " + var3, var3);
}
}
阻塞式缓存
特点:调用get方法获取同一个key时,需要进行阻塞等待
public class BlockingCache implements Cache {
// 超时时间
private long timeout;
// 被装饰者
private final Cache delegate;
// 针对每一个key建立一个ReentrantLock
private final ConcurrentHashMap<Object, ReentrantLock> locks;
public BlockingCache(Cache delegate) {
this.delegate = delegate;
this.locks = new ConcurrentHashMap();
}
public Object getObject(Object key) {
// 获取锁
this.acquireLock(key);
// 执行数据库操作
Object value = this.delegate.getObject(key);
if (value != null) {
this.releaseLock(key);
}
return value;
}
private ReentrantLock getLockForKey(Object key) {
ReentrantLock lock = new ReentrantLock();
// 如果指定的key已存在,则不会put,返回这个key对应的value,
// 如果指定的key不存在,则返回null
ReentrantLock previous = (ReentrantLock)this.locks.putIfAbsent(key, lock);
// 如果previous == null,说明是第一次访问
return previous == null ? lock : previous;
}
private void acquireLock(Object key) {
// 获取绑定在该key上的ReentrantLock
Lock lock = this.getLockForKey(key);
if (this.timeout > 0L) {
try {
boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
}
} catch (InterruptedException var4) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
}
} else {
lock.lock();
}
}
}
private void releaseLock(Object key) {
ReentrantLock lock = (ReentrantLock)this.locks.get(key);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
public void putObject(Object key, Object value) {
try {
this.delegate.putObject(key, value);
} finally {
this.releaseLock(key);
}
}
五、理解sqlSession
1、sqlSession = sqlSessionFactory.openSession();
DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
// 拿到事务工厂
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 执行器
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
TransactionFactory的实现
ManagedTransactionFactory:空壳事务管理器,用于继承后自行扩展;
SpringManagedTransactionFactory:spring实现的事务工厂;
JdbcTransactionFactory:mybatis实现的简单事务工厂;
Executor
// 里面包含一些具体的sql操作
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement var1, Object var2) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
boolean isCached(MappedStatement var1, CacheKey var2);
void clearLocalCache();
void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
Transaction getTransaction();
void close(boolean var1);
boolean isClosed();
void setExecutorWrapper(Executor var1);
}
Mybatis提供了三种sql执行器,分别是SIMPLE(默认)、REUSE、BATCH。CachingExecutor只负责管理缓存
- SIMPLE(SimpleExecutor),相当于JDBC的stmt.execute(sql) 执行完毕即关闭
- REUSE(ReuseExecutor),相当于JDBC的stmt.execute(sql) 执行完不关闭,而是将stmt存入 Map<String,Statement> 中缓存,其中key为执行的sql模板;
- BATCH (BatchExecutor),相当于JDBC语句的 stmt.addBatch(sql),即仅将执行SQL加入到批量计划但不真正执行,所以此时不会执行返回受影响行数,而只有执行stmt.executeBatch()后才会真正执行sql。
Executor executor = this.configuration.newExecutor(tx, execType);
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
Executor执行流程
执行器在执行query方法时,可以分为三个步骤:
- 解析获取sql
- 执行sql
- 处理结果集
1、CachingExecutor.query
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 拿到绑定sql
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 根据ms,参数,分页信息,boundSql生成一个缓存的key
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 从ms中拿到cache(mepper.xml中配置的cache)
Cache cache = ms.getCache();
if (cache != null) {
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, parameterObject, boundSql);
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
return list;
}
}
// 未配置二级缓存则不生效
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// BaseExecutor.query
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
// isFlushCacheRequired:是否sql执行完成刷新缓存(sql上的配置)
// this.queryStack == 0:避免在嵌套查询的过程中把有必要存在的缓存刷新掉
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
// 去一级缓存中拿
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
// 将输出参数进行保存
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 到数据库中查
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
DeferredLoad deferredLoad = (DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
// 如果当前缓存配置为STATEMENT,清除缓存
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
// 从数据库里查询数据
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 先用占位符占位置,存在嵌套查询时,有些其他语句需要当前数据,拿到占位符,放到deferLoad,最终查询完毕,统一从一级缓存拿到
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = this.boundSql.getSql();
statement.execute(sql);
return this.resultSetHandler.handleResultSets(statement);
}
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
List<Object> multipleResults = new ArrayList();
int resultSetCount = 0;
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
this.validateResultMapsCount(rsw, resultMapCount);
while(rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
String[] resultSets = this.mappedStatement.getResultSets();
if (resultSets != null) {
while(rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
}
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
}
return this.collapseSingleResultList(multipleResults);
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
} else if (this.resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
}
} finally {
this.closeResultSet(rsw.getResultSet());
}
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
this.ensureNoRowBounds();
this.checkResultHandler();
this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext();
this.skipRows(rsw.getResultSet(), rowBounds);
while(this.shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, (String)null);
Object rowValue = this.getRowValue(rsw, discriminatedResultMap);
this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, (String)null);
if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
MetaObject metaObject = this.configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (this.shouldApplyAutomaticMappings(resultMap, false)) {
foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, (String)null) || foundValues;
}
foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, (String)null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
}
return rowValue;
}
query执行逻辑总结
- query方法会包装成一个CachingExecutor(需要配置中开启二级缓存才生效)
- 根据ms,查询参数,分页信息,boundsql构造一个用于缓存的key(二级缓存)
- 当二级缓存未配置开启或未拿到缓存数据时,走BaseExecutor.query
- baseExecutor中提供了查询模板(针对一级缓存:deferredLoad和嵌套查询:queryStack)。先在以及缓存中拿,未拿到走查询
六、懒加载
在mybatis中增加如下配置可实现懒加载
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
<setting name="lazyLoadTriggerMethods" value=""/>
- lazyLoadingEnabled:懒加载的全局开关。如果设置为false,则相关的sql会立即执行,默认为false
- aggressiveLazyLoading:急切的执行懒加载,设置为true时,只要触发了懒加载就会将该层及其级联的查询全部执行,默认为true。设置为false时,每个属性都按需加载,这个配置适用于多层级联的懒加载
- lazyLoadTriggerMethods:触发一次性加载的方法
// DefaultResultSetHandler.getNestedQueryMappingValue
ResultLoader resultLoader = new ResultLoader(this.configuration, this.executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
if (propertyMapping.isLazy()) {
// 开启懒加载时,会将resultLoader放入loaderMap中,需要时再调用resultLoader.loadResult进行加载
lazyLoader.addLoader(property, metaResultObject, resultLoader);
value = DEFERED;
} else {
// 否则直接进行加载
value = resultLoader.loadResult();
}
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
String upperFirst = getUppercaseFirstProperty(property);
if (!upperFirst.equalsIgnoreCase(property) && this.loaderMap.containsKey(upperFirst)) {
throw new ExecutorException("Nested lazy loaded result property '" + property + "' for query id '" + resultLoader.mappedStatement.getId() + " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
} else {
this.loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
}
}
加载时机:在使用返回值或使用返回值中和级联字段相关的get,set方法时进行加载。因此,具体获取结果的对象一定为代理对象,详见代码如下:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 此时获取的rowValue为代理对象
Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, (String)null);
if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
MetaObject metaObject = this.configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (this.shouldApplyAutomaticMappings(resultMap, false)) {
foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, (String)null) || foundValues;
}
foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, (String)null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
}
return rowValue;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false;
List<Class<?>> constructorArgTypes = new ArrayList();
List<Object> constructorArgs = new ArrayList();
// 创建结果普通实例
Object resultObject = this.createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
Iterator var9 = propertyMappings.iterator();
while(var9.hasNext()) {
ResultMapping propertyMapping = (ResultMapping)var9.next();
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
// 创建代理结果
resultObject = this.configuration.getProxyFactory().createProxy(resultObject, lazyLoader, this.configuration, this.objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
return resultObject;
}
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return JavassistProxyFactory.EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
// EnhancedResultObjectProxyImpl
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
String methodName = method.getName();
try {
synchronized(this.lazyLoader) {
if ("writeReplace".equals(methodName)) {
Object original;
if (this.constructorArgTypes.isEmpty()) {
original = this.objectFactory.create(this.type);
} else {
original = this.objectFactory.create(this.type, this.constructorArgTypes, this.constructorArgs);
}
PropertyCopier.copyBeanProperties(this.type, enhanced, original);
if (this.lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, this.lazyLoader.getProperties(), this.objectFactory, this.constructorArgTypes, this.constructorArgs);
}
return original;
}
if (this.lazyLoader.size() > 0 && !"finalize".equals(methodName)) {
if (!this.aggressive && !this.lazyLoadTriggerMethods.contains(methodName)) {
String property;
// 调用set方法,懒加载会被取消
if (PropertyNamer.isSetter(methodName)) {
property = PropertyNamer.methodToProperty(methodName);
this.lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
// 如果为get方法,拿到Property
property = PropertyNamer.methodToProperty(methodName);
// 判断lazyLoader中是否有该property,有则加载
if (this.lazyLoader.hasLoader(property)) {
this.lazyLoader.load(property);
}
}
} else {
// 触发一次性加载
this.lazyLoader.loadAll();
}
}
}
return methodProxy.invoke(enhanced, args);
} catch (Throwable var10) {
throw ExceptionUtil.unwrapThrowable(var10);
}
}
七、动态代理
mybatis提供了非常强大的动态代理能力,我们可以使用getMapper方法获取一个代理对象,更加方便我们执行增删改查操作
public void testProcy() throws IOException{
try {
// mapper即为代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rows = mapper.insert(new User(null, "JERRY", 19));
sqlSession.commit();
log.info("rows -> {}", rows);
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally {
sqlSession.close();
}
}
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// mapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果为Object的方法直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
// mapperMethod.execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch (this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
// 调用sqlSession的相应方法,本质是调用executor的相应方法
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
测试代理demo
@Test
public void testCreateProxy() throws IOException{
MapperProxy<UserMapper> mapperProxy = new MapperProxy<>(sqlSession, UserMapper.class, new HashMap<>());
UserMapper mapper = (UserMapper)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{UserMapper.class}, mapperProxy);
int rows = mapper.insert(new User(null, "HAHAHHA", 20));
sqlSession.commit();
}
八、插件
Mybatis允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,Mybatis允许使用插件来拦截的方法调用包括以下内容:
- Executor --> (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
- ParameterHandler --> (getParameterObject,setParameters)
- ResultSetHandler --> (handleResultSets,handleOutputParameters)
- StatementHandler --> (prepare,parameterize,batch,update,query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看Mybatis发行包中的源代码。如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。因为在试图修改或重写已有方法的行为时,很可能会破坏MyBatis的核心模块。这些都是更底层的类和方法,所以使用插件时要特别当心。
使用Mybatis插件可以极大提升Mybatis的可扩展性,如实现自动分页,分库分表,数据脱敏,sql信息统计,数据加解密等功能。
通过Mybatis提供的强大机制,使用插件是非常简单的,只需实现Interceptor接口,并指定想要拦截的方法签名即可。其中@Signature注解包含三个元素:type,method,args。其中,type指明要拦截的类,method指明方法名,args指明方法的参数列表。通过指定着三个元素,我们就能完全确定一个要拦截的方法。
package cn.j3code.studyspring.mybatis.helloworld.interceptors;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import java.sql.Statement;
import java.util.Properties;
@Slf4j
@Intercepts({
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}),
})
public class SqlCostTimeInterceptor implements Interceptor {
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1、拿到sql语句
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
Long start = System.currentTimeMillis();
Object result = null;
try{
result = invocation.proceed();
}finally {
if(log.isDebugEnabled()){
log.info("name --> {}", properties.get("name"));
log.debug("sql语句[{}],执行耗时{}毫秒", sql, System.currentTimeMillis() - start);
}
}
return result;
}
@Override
public Object plugin(Object target) {
//如果没有特殊定制,直接使用Plugin这个工具类返回一个代理对象即可
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<plugins>
<plugin interceptor="cn.j3code.studyspring.mybatis.helloworld.interceptors.SqlCostTimeInterceptor">
<property name="name" value="hello plugin"/>
</plugin>
</plugins>
插件源码流程:
Mybatis的插件很巧妙的使用代理的方式实现了责任链设计模式,插件被应用是统一调用了interceptorChain.pluginAll()方法。
在创建parameterHandler,ResultSetHandler,StatementHandler以及Executor的时候,都调用了pluginAll方法
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList();
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)var2.next();
}
return target;
}
}
每一个拦截器都是Interceptor的具体实现,调用wrap方法会对目标对象进行包装,如下:
Plugin.wrap(target, this);
public class Plugin implements InvocationHandler {
// 方法通过wrap进行动态代理,返回代理对象
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
}
// 本质在方法调用时会执行invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 获取拦截的方法
Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
// 如果是被拦截的方法,本质会执行插件的intercept方法
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
}