本文主要介绍Java中,不使用XML和使用XML构建SqlSessionFactory,通过SqlSessionFactory 中获取SqlSession的方法,使用SqlsessionManager管理Sqlsession复用等等..以及相关的示例代码

SqlSession

SqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的各种方法。而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或 Java 配置代码来创建 SqlSessionFactory。

使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通过这个接口来执行命令,获取映射器示例和管理事务。在介绍 SqlSession 接口之前,我们先来了解如何获取一个 SqlSession 实例。

举个例子

public class Ttest {private Long id;private String context;
....
}

TestMapper.java

public interface TestMapper {Ttest getOne(Long id);
}

TestMapper.xml

<mapper namespace="com.liangtengyu.mapper.TestMapper"><select id="getOne" resultType="com.liangtengyu.entity.Ttest">select * from t_test where id  = #{id}</select>
</mapper>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--开启日志输出--><settings><setting name="logImpl" value="STDOUT_LOGGING" /></settings><!--配置类别名,配置后在Mapper配置文件(通常我们将编写SQL语句的配置文件成为Mapper配置文件)中需要使用pojo包中的类时,使用简单类名即可--><typeAliases><package name="com.liangtengyu.entity"/></typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="username" value="root"/><property name="password" value="123456"/><property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/></dataSource></environment></environments><mappers><package name="com.liangtengyu.mapper"/></mappers></configuration>

来个测试方法:

   @Testpublic void testMyBatisBuild() throws IOException {Reader reader = Resources.getResourceAsReader("mybatis-config.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);SqlSession sqlSession = factory.openSession();TestMapper mapper = sqlSession.getMapper(TestMapper.class);Ttest one = mapper.getOne(1L);System.out.println(one);sqlSession.close();}

运行测试方法,控制台打印日志:

Checking to see if class com.liangtengyu.mapper.TestMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 2083117811.   //创建的连接名
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7c29daf3]
==>  Preparing: select * from t_test where id = ?
==> Parameters: 1(Long)
<==    Columns: id, context
<==        Row: 1, 123<==      Total: 1
Ttest{id=1, context='123'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7c29daf3]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7c29daf3]
Returned connection 2083117811 to pool. //用完了又放回连接池中

SqlSessionFactoryBuilder 创建出SqlSessionFactory,然后从SqlSessionFactory中得到SqlSession,最后通过SqlSession得到Mapper接口对象进行数据库操作。

我们打个断点.来跟踪SqlSessionFactoryBuilder的源代码:


F7跟进 发现一堆build 而我们现在用的是传入reader的那个方法


我们可以看到,他帮我们传了2个Null参数给下一个build,我们跟着这个build继续往下跟.


这个build会将xml解析.然后调用parser.parse()方法将xml转化成Configuration,传入下一个build
继续下一个build


这个build终于干了我们最关心的事,他创建了DefaultSqlSessionFactory 返回SqlSessionFactory.
我们进来看看这个类:

public class DefaultSqlSessionFactory implements SqlSessionFactory {//它是SqlSessionFactory的实现类.private final Configuration configuration;//通过传入一个Configuration 来构造public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}...

这样一看,那我们直接给它来个configuration不就可以创建一个SqlSessionFactory吗.

我们来试试

 //使用传入Configuration方式创建SqlSessionFactoryBuilder@Testpublic void testMyBatisBuild1() throws IOException {DataSource datasource = getDatasource();//首先创建数据源Environment e = new Environment("test", new JdbcTransactionFactory(), datasource);//传入datasource和JdbcTransactionFactoryConfiguration configuration = new Configuration();//构建一个Configurationconfiguration.setEnvironment(e);configuration.setLogImpl(StdOutImpl.class);//使用控制台输出日志实现configuration.getTypeAliasRegistry().registerAlias(Ttest.class);configuration.addMapper(TestMapper.class);//传入configuration                               SqlSessionFactory build = new SqlSessionFactoryBuilder().build(configuration);SqlSession sqlSession = build.openSession();TestMapper mapper = sqlSession.getMapper(TestMapper.class);Ttest one = mapper.getOne(1L);System.out.println(one);sqlSession.close();}//获取数据源方法public UnpooledDataSource getDatasource(){UnpooledDataSource unpooledDataSource = new UnpooledDataSource();unpooledDataSource.setUrl("jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8");unpooledDataSource.setDriver("com.mysql.jdbc.Driver");unpooledDataSource.setUsername("root");unpooledDataSource.setPassword("123456");return unpooledDataSource;}

运行结果:

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Opening JDBC Connection
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
==>  Preparing: select * from t_test where id = ?
==> Parameters: 1(Long)
<==    Columns: id, context
<==        Row: 1, 123<==      Total: 1
Ttest{id=1, context='123'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(configuration);
//在构造SqlSessionFactory时实际调用的还是DefaultSqlSessionFactory 所以我们直接使用
DefaultSqlSessionFactory factory = new DefaultSqlSessionFactory(configuration);
//也是一样的效果


那么他的内部是如何创建session的我们来看看源代码
根据断点我们到了factory.opensession()方法 F7进入方法

F8单步 发现调用了openSessionFromDataSource方法


三个参数.第一个参数是configuration.getDefaultExecutorType()
这个参数是Configuration类中定义的默认类型.

ExecutorType

package org.apache.ibatis.session;//还有其它 的类型 如下.
/*** @author Clinton Begin*/
public enum ExecutorType {SIMPLE, REUSE, BATCH
}

大家可能对 ExecutorType 参数感到陌生。这个枚举类型定义了三个值:

ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。

ExecutorType.REUSE:该类型的执行器会复用预处理语句。

ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。这里不再深入讨论

level

是称为 TransactionIsolationLevel,

事务隔离级别支持 JDBC 的五个隔离级别(NONEREAD_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE),并且与预期的行为一致。

autoCommit

向 autoCommit 可选参数传递 true 值即可开启自动提交功能


继续往下 进到openSessionFromDataSource方法

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);//返回DefaultSqlSession} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

返回的DefaultSqlSession实现了SqlSession的所有方法


我们进入到Sqlsession类中查看一下实现它的都有哪些类


一共有两个,而且SqlSessionManager还实现了SqlSessionFactory

SqlSessionManager

public class SqlSessionManager implements SqlSessionFactory, SqlSession {private final SqlSessionFactory sqlSessionFactory;private final SqlSession sqlSessionProxy;//这里使用了代理,来增强sqlsessionprivate final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();//使用Threadlocal管理本线程的sqlsession来复用sqlsessionprivate SqlSessionManager(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),new Class[]{SqlSession.class},new SqlSessionInterceptor());}//这个方法帮我们直接创建了sqlSessionFactory并且将传入的sqlSessionFactory的SqlSession进行了代理public static SqlSessionManager newInstance(Reader reader) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));}.....public static SqlSessionManager newInstance(Reader reader) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));}public static SqlSessionManager newInstance(Reader reader, String environment) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));}public static SqlSessionManager newInstance(Reader reader, Properties properties) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties));}public static SqlSessionManager newInstance(InputStream inputStream) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null));}public static SqlSessionManager newInstance(InputStream inputStream, String environment) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null));}public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) {return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties));}public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {return new SqlSessionManager(sqlSessionFactory);}.....@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //使用Threadlocal管理本线程的sqlsession来复用sqlsessionfinal SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();if (sqlSession != null) {//获取本线程的sqlsessiontry {return method.invoke(sqlSession, args);//实际调用} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}} else {try (SqlSession autoSqlSession = openSession()) {try {final Object result = method.invoke(autoSqlSession, args);autoSqlSession.commit();return result;} catch (Throwable t) {autoSqlSession.rollback();throw ExceptionUtil.unwrapThrowable(t);}}}}

SqlSessionManager
他实现了Session接口。意味着,SqlSessionManager集成了 sqlSessionFactory和session 的功能。通过SqlSessionManager,开发者可以不在理会SqlSessionFacotry的存在,直接面向Session编程。

SqlSessionManager 内部提供了一个sqlSessionProxy,这个sqlSessionProxy提供了所有Session接口的实现,而实现中正是使用了上面提到的本地线程保存的session实例。

这样,在同一个线程实现不同的sql操作,可以复用本地线程session,避免了DefaultSqlSessionFactory实现的每一个sql操作都要创建新的session实例

下面让我们用一个简单的实例来试试

@Testpublic void testMyBatisBuild3() throws IOException {Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// SqlSessionFactory build = new SqlSessionFactoryBuilder().build(reader);
// 不使用SqlSessionFactory 使用SqlSessionManager.newInstance();SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(reader);SqlSession sqlSession = sqlSessionManager.openSession();TestMapper mapper = sqlSession.getMapper(TestMapper.class);Ttest one = mapper.getOne(1L);System.out.println(one);sqlSession.close();}

运行结果:

Reader entry: ����1  getOne0(Ljava/lang/Long;)Lcom/liangtengyu/entity/Ttest;
Find JAR URL: file:/Users/tengyu/IdeaProjects/mybatis-study/target/classes/com/liangtengyu/mapper/TestMapper.xml
Not a JAR: file:/Users/tengyu/IdeaProjects/mybatis-study/target/classes/com/liangtengyu/mapper/TestMapper.xml
Reader entry: <?xml version="1.0" encoding="UTF-8"?>
Checking to see if class com.liangtengyu.mapper.TestMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 1585787493.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
==>  Preparing: select * from t_test where id = ?
==> Parameters: 1(Long)
<==    Columns: id, context
<==        Row: 1, 123<==      Total: 1
Ttest{id=1, context='123'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
Returned connection 1585787493 to pool.

本章主要讲了MyBatis构建SqlSessionFactory方式,过程,和sqlsession的创建,以及使用SqlSessionManager管理session复用的实现方式.

下一篇研究数据源的池化和数据源加载过程.

加群一起学习吧

关注公众号:java宝典

Mybatis源码阅读(二)相关推荐

  1. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  2. Mybatis源码阅读之二——模板方法模式与Executor

    [系列目录] Mybatis源码阅读之一--工厂模式与SqlSessionFactory 文章目录 一. 模板方法模式 二. 同步回调与匿名函数 三. Executor BaseExecutor与其子 ...

  3. mybatis源码阅读(三):mybatis初始化(下)mapper解析

    转载自 mybatis源码阅读(三):mybatis初始化(下)mapper解析 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单. ...

  4. Mybatis 源码阅读环境搭建

    Mybatis源码阅读环境搭建 前言 一.下载mybatis的源码 二.编译源码 三.创建测试项目 前言     mybatis源码阅读环境搭建还是比较简单的,接下来我们讲解一下如何搭建该源码阅读环境 ...

  5. mybatis源码阅读(八) ---Interceptor了解一下

    转载自  mybatis源码阅读(八) ---Interceptor了解一下 1 Intercetor MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis允许 ...

  6. mybatis源码阅读(七) ---ResultSetHandler了解一下

    转载自  mybatis源码阅读(七) ---ResultSetHandler了解一下 1.MetaObject MetaObject用于反射创建对象.反射从对象中获取属性值.反射给对象设置属性值,参 ...

  7. mybatis源码阅读(六) ---StatementHandler了解一下

    转载自  mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler:一个抽象类,只是实 ...

  8. mybatis源码阅读(五) ---执行器Executor

    转载自  mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_ ...

  9. mybatis源码阅读(四):mapper(dao)实例化

    转载自   mybatis源码阅读(四):mapper(dao)实例化 在开始分析之前,先来了解一下这个模块中的核心组件之间的关系,如图: 1.MapperRegistry&MapperPro ...

最新文章

  1. linux配置nginx虚拟目录
  2. android中的定时任务一般有两种机制,android 定时任务
  3. 【Castell】安全联锁提升UPS设备维护管理水平
  4. 安卓APP_ 控件(5)—— ProgressBar
  5. Pycharm社区版配置Django
  6. javascript的阻止默认事件和阻止冒泡事件
  7. rk3399_android7.1调试USB接口的TP记录
  8. Silverlight的资源
  9. 我的世界HMCL安装与使用
  10. 2020-11-10 oracle 数据库sql 之decode函数
  11. 手机通讯录分组名称_个人通讯录如何批量导入/导出
  12. Routeros花生壳域名解析脚本
  13. HECTF2021-WP集合
  14. 计算机网络daytime实现之java/python/c++版本
  15. 基于Unity3D的智能家居仿真系统——户型绘制基本功能介绍
  16. springBoot二维码生成案例
  17. 2022-2027年中国煤制尿素行业市场全景评估及发展战略规划报告
  18. 微信小程序底部弹窗(半屏弹窗)---WeUI组件使用
  19. Linux编程定时执行某函数
  20. Stata: VAR - 模拟、估计和推断

热门文章

  1. java 阈值 告警_处理Java异常告警最佳实践
  2. 五款堪称神器的软件,电脑日常使用必备。
  3. git代码库pull报错:error: Your local changes to the following files would be overwritten by merge
  4. rss和css,RSS 简介
  5. Nginx的简单使用,配置多前端,多端口【微信小程序+前后端分离跨域解决】
  6. 常用dns114.114.114.114与8.8.8.8的区别
  7. 浏览器缓存有哪些,通常缓存有哪几种
  8. Python运维开发(CMDB资产管理系统)——Pycharm部署
  9. 合作对策模型的简单实现
  10. Linux常用命令——最详细!!!!