MyBatis使用总结

MyBatis总结

参考

https://mybatis.org/mybatis-3/zh/getting-started.html

浅析MyBatis的动态代理原理

Mybatis教程-实战看这一篇就够了


【MyBatis系列2】最全MyBatis核心配置文件总结

【MyBatis系列4】一对一,一对多,多对多查询及延迟加载(N+1问题)分析

【MyBatis系列5】MyBatis4大核心对象SqlSessionFactoryBuiler,SqlSessionFactory,SqlSession,Mapper

【MyBatis系列6】SQL执行流程

【MyBatis系列7】Executor

【MyBatis系列8】缓存

【MyBatis系列9】插件

【MyBatis系列10】日志

1. demo

在这里插入图片描述

1.配置数据源:db.properties

2.配置mybatis

  • 注意mapper.xml

3.确认实体类:Country

  • Country
  • CountryMapper
  • CountryMapper.xml

4.Demo.main

  1. 获取mybatis-config.xml的文件流
  2. 构造SqlSessionFactory
  3. 得到SqlSession
  4. 获取CountryMapper
  5. 执行SQL

2. mybatis-config

<?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="db.properties"></properties>
    <settings>
        <!-- 打印查询语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
        <!-- 开启驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!-- 别名 -->
    <typeAliases/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="CountryMapper.xml"/>
    </mappers>
</configuration>

3. mapper

默认本地一级缓存:

开启全局二级缓存

3.1 resultMap
<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>
3.2 sql
if
<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>
choose、when、otherwise
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>
trim、where、set
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>
foreach
<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
3.3 cache
<cache/>

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

<cache type="com.domain.something.MyCustomCache"/>

<cache-ref namespace="com.someone.application.data.SomeMapper"/>
3.4 CRUD
<select id="selectAuthor" parameterType="int" resultType="hashmap">
  SELECT * FROM Author WHERE ID = #{id}
</select>

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>

4. 日志

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

可选值:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了 org.apache.ibatis.logging.Log 接口,且构造方法以字符串为参数的类完全限定名。

更细粒度的配置:比如包、mapper、方法

log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE

log4j.logger.org.mybatis.example.BlogMapper=TRACE

log4j.logger.org.mybatis.example=TRACE

5. 接口分析

SqlSessionFactoryBuilder
	XMLConfigBuilder
	Configuration
SqlSessionFactory
	ExecutorType
		TransactionFactory
		Transaction
		TransactionIsolationLevel
SqlSession
  1. 根据mybatis的配置,得到文件流,并执行SqlSessionFactoryBuilder#build方法,最后返回SqlSessionFactory

    // SqlSessionFactoryBuilder build
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    Configuration config = parser.parse();
    return new DefaultSqlSessionFactory(config);
    
    // XMLConfigBuilder parse
    this.parseConfiguration(this.parser.evalNode("/configuration"));
    // XMLConfigBuilder parseConfiguration(说明:下面方法读取到的信息都填充到config中)
    propertiesElement(root.evalNode("properties"));
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    mapperElement(root.evalNode("mappers"));
    
  2. 根据配置信息,得到SqlSession

    根据Environment配置信息(一般会配置事务管理器和数据源),获取事务工厂,JDBC居多,从而获取JdbcTransactionFactory,然后根据数据源 + 事务隔离级别 + 是否自动提交,获取Transaction

    // 提供接口指定事务隔离级别
    SqlSession openSession(TransactionIsolationLevel level)
    
    public enum TransactionIsolationLevel {
      NONE(Connection.TRANSACTION_NONE),
      READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
      READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
      REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
      SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
    
      private final int level;
    }
    
    <transactionManager type="JDBC"/>
    <transactionManager type="MANAGED"/>
    

    上面生成Transaction对象后,用于生成Executor,有三种类型:SIMPLE, REUSE, BATCH

    new XxxExecutor()后,会执行interceptorChain.pluginAll(executor);

    最后返回SqlSession:

    TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    
    Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    
    Executor executor = configuration.newExecutor(tx, execType);
    return new DefaultSqlSession(configuration, executor, autoCommit);
    
  3. 获取Mapper代理接口,执行CRUD

    mapper工厂什么时候填充到knownMappers呢?

    // DefaultSqlSession getMapper
    return configuration.getMapper(type, this);
    
    // Configuration getMapper
    return mapperRegistry.getMapper(type, sqlSession);
    
    // MapperRegistry getMapper(说明:Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>())
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    return mapperProxyFactory.newInstance(sqlSession);
    

6. mapper工厂:mybatis动态代理

MapperRegistry.addMapper -> Configuration.addMapper -> XMLMapperBuilder.addMapper, bindMapperForNamespace, parse -> XMLConfigBuilder.mapperElement, parseConfiguration, parse -> SqlSessionFactoryBuilder.build

在获取SqlSessionFactory的时候,会解析mybatis-config.xml,期间会处理mappers

  1. 从mybatis-config获取mappers结点并遍历,从而获取mapper.xml的流,构造XMLMapperBuilder

  2. XMLMapperBuilder执行parse,首先处理结点信息,存储到成员属性中

  3. new一个mapper.xml对应接口的工厂,用Map存储,最后在XxxMapper mapper = sqlSession.getMapper时生成代理类

    mapperProxyFactory.newInstance(sqlSession);

// XMLMapperBuilder.parse
/*
cache-ref		属性cacheRefMap、builderAssistant
cache			属性caches、builderAssistant
parameterMap
resultMap
sql
select|insert|update|delete
	builderAssistant.addMappedStatement方法:针对CRUD SQL语句
	最终,保存到config.mappedStatements中(Configuration类属性:Map<String, MappedStatement> mappedStatements)
*/
configurationElement();
    
/*
获取namespace,获取对应接口的Class<?>类型,添加到configuration
	mapperRegistry.addMapper(type);
		knownMappers.put(type, new MapperProxyFactory<>(type));
*/
bindMapperForNamespace();
/**
mapperInterface对应mapper.xml的namespace接口的Class对象
methodCache
*/
public class MapperProxyFactory<T> {
  private Class<T> mapperInterface;
  private Map<Method, MapperMethodInvoker> methodCache = 
      new ConcurrentHashMap<>();
}
  1. 使用的时候,通过SqlSession获取XxxMapper代理接口工厂,即MapperProxyFactory

  2. 执行newInstance,生成对应代理类

    MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    

    关键参数:mapperProxy。

    动态代理可以将所有接口的调用重定向到调用处理器InvocationHandler,调用它的invoke方法,即MapperProxy的invoke方法

    cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    

    cachedInvoker(method)用于获取MapperMethodInvoker

    private static class PlainMethodInvoker implements MapperMethodInvoker {
        private final MapperMethod mapperMethod;
    
        public PlainMethodInvoker(MapperMethod mapperMethod) {
            super();
            this.mapperMethod = mapperMethod;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
            return mapperMethod.execute(sqlSession, args);
        }
    }
    
  3. 获取MappedStatement ms
    上述mapperMethod.execute实质根据CRUD类型调用SqlSession执行对应方法

    executor.query(ms, wrapCollection(parameter), rowBounds, 
    	Executor.NO_RESULT_HANDLER);
    

    Executor,openSession默认为SIMPLE,还有BATCH和REUSE

    默认使用StatementType.PREPARED,CRUD中可指定statementType
    SQL执行:调用StatementHandler,然后PreparedStatement statement.execute(sql);

    • SimpleExecutor:
    • BatchExecutor:批处理
    • CallableStatementHandler:存储过程

7. typeHandler

在XMLConfigBuilder执行时,会读取mybatis-config.xml,此时会注册标签"typeHandlers",使用Map存储到typeHandlerMap,后续从typeHandler中,parameter用于setter,result用于getter。

XMLMapperBuilder
	"/mapper/parameterMap"			Configuration的成员属性parameterMaps
	"/mapper/resultMap"					Configuration的成员属性resultMaps

记录

XMLConfigBuilder					"/configuration"
	Configuration  
		variables		"properties"
		vfsImpl		"settings/vfsImpl"
		logImpl		"settings/logImpl"
		typeAliasRegistry	"typeAliases"
		interceptorChain	"plugins"
		environment			"environments"
			(id,transactionFactory,dataSource)
		typeHandlerRegistry	"typeHandlers"
			typeHandlerMap
			作用:parameter用于setter,result用于get
		其他:
			"settings/*"
			"objectFactory"
			"objectWrapperFactory"
			"reflectorFactory"
			"databaseIdProvider"
								
XMLMapperBuilder 				"mappers"   
	"/mapper"										Configuration
		"namespace"								
		"cache-ref"								cacheRefMap
		"cache"										caches
		"/mapper/parameterMap"			parameterMaps
			ParameterMap(id, type, parameterMappings)
				ParameterMapping
					typeHandler(用于setter)
		"/mapper/resultMap"					resultMaps
			ResultMap(id, type, ..)
			说明:type。获取类,然后获取javaType,获取TypeHandler,用于getter
		"/mapper/sql"							
		"select|insert|update|delete"		
			XMLStatementBuilder
				includeParser.applyIncludes(context.getNode());
				作用:解析include refid,应用<sql>
				
				lang和selectKey处理
				
				MappedStatement
				...

总结

得到mapper代理接口

从knownMappers得到MapperProxyFactory
通过Proxy.newProxyInstance生成MapperProxy
返回代理实例

调用链:DefaultSqlSession, MapperRegistry, MapperProxyFactory, MapperProxy


执行查询

委托给MapperProxy

调用链:MapperProxy, DefaultSqlSession, Executor, StatementHandler, PreparedStatement

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值