本文次要介绍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 configuration        PUBLIC "-//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>
来个测试方法:
   @Test    public 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: 1Ttest{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    @Test    public void testMyBatisBuild1() throws IOException {        DataSource datasource = getDatasource();//首先创立数据源        Environment e = new Environment("test", new JdbcTransactionFactory(), datasource);//传入datasource和JdbcTransactionFactory        Configuration configuration = new Configuration();//构建一个Configuration        configuration.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 ConnectionSetting 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: 1Ttest{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;//这里应用了代理,来加强sqlsession  private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();  //应用Threadlocal治理本线程的sqlsession来复用sqlsession  private 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);  }    .....   @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //应用Threadlocal治理本线程的sqlsession来复用sqlsession      final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();      if (sqlSession != null) {//获取本线程的sqlsession        try {          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实例

上面让咱们用一个简略的实例来试试

@Test    public 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.xmlNot a JAR: file:/Users/tengyu/IdeaProjects/mybatis-study/target/classes/com/liangtengyu/mapper/TestMapper.xmlReader 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 ConnectionCreated 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: 1Ttest{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宝典