200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > MyBatis原理分析之获取SqlSession

MyBatis原理分析之获取SqlSession

时间:2024-02-05 01:08:54

相关推荐

MyBatis原理分析之获取SqlSession

流程原理分析系列:

MyBatis原理分析之获取SqlSessionFactory

MyBatis原理分析之获取SqlSession

MyBatis原理分析之获取Mapper接口的代理对象

MyBatis原理分析之查询单个对象

获取sqlsession主要是通过SqlSessionFactory的几个重载方法,从configuration中的environment获取datasource与transactionFactory来得到Transaction。然后得到Transaction、Executor与DefaultSqlSession。

mybatis全局配置文件中environments 结点配置如下

<environments default="development"><environment id="development"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/mybatis" /><property name="username" value="root" /><property name="password" value="123456" /></dataSource></environment></environments>

时序图如下所示:

【1】DefaultSqlSessionFactory

SqlSessionFactory 有六个方法创建 SqlSession 实例。通常来说,当你选择其中一个方法时,你需要考虑以下几点:

事务处理:你希望在 session 作用域中使用事务作用域,还是使用自动提交(auto-commit)?(对很多数据库和/或 JDBC 驱动来说,等同于关闭事务支持)数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接?语句执行:你希望 MyBatis 复用 PreparedStatement 和/或批量更新语句(包括插入语句和删除语句)吗?

基于以上需求,有下列已重载的多个 openSession() 方法供使用。

SqlSession openSession()SqlSession openSession(boolean autoCommit)SqlSession openSession(Connection connection)SqlSession openSession(TransactionIsolationLevel level)SqlSession openSession(ExecutorType execType)SqlSession openSession(ExecutorType execType, boolean autoCommit)SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)SqlSession openSession(ExecutorType execType, Connection connection)Configuration getConfiguration();

默认的 openSession() 方法没有参数,它会创建具备如下特性的 SqlSession:

事务作用域将会开启(也就是不自动提交)。将由当前环境配置的 DataSource 实例中获取 Connection 对象。事务隔离级别将会使用驱动或数据源的默认设置。预处理语句不会被复用,也不会批量处理更新。

相信你已经能从方法签名中知道这些方法的区别。向 autoCommit 可选参数传递 true 值即可开启自动提交功能。若要使用自己的 Connection 实例,传递一个 Connection 实例给 connection 参数即可。注意,我们没有提供同时设置 Connection 和 autoCommit 的方法,这是因为 MyBatis 会依据传入的 Connection 来决定是否启用 autoCommit。对于事务隔离级别,MyBatis 使用了一个 Java 枚举包装器来表示,称为 TransactionIsolationLevel,事务隔离级别支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),并且与预期的行为一致。

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

ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。ExecutorType.REUSE:该类型的执行器会复用预处理语句。ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。

提示 在 SqlSessionFactory 中还有一个方法我们没有提及,就是 getConfiguration()。这个方法会返回一个 Configuration 实例,你可以在运行时使用它来检查 MyBatis 的配置。

提示如果你使用过 MyBatis 的旧版本,可能还记得 session、事务和批量操作是相互独立的。在新版本中则不是这样。上述三者都包含在 session 作用域内。你不必分别处理事务或批量操作就能得到想要的全部效果。

关于SqlSessionFactory和SqlSession更多详情参考博文:MyBatis中SqlSessionFactory和SqlSession简解

下面我们分析openSessionFromDataSource

① 核心方法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);// 根据不同的execTYPE创建对应的executor实例final Executor executor = configuration.newExecutor(tx, execType);// 根据configuration executor以及autoCommit 创建DefaultSqlSession实例return new DefaultSqlSession(configuration, executor, autoCommit);} 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();}}

代码解释如下:

① 从configuration获取environment实例,主要有属性datasource、id、transactionFactory

transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit)获取事务实例对象Transaction③Executor executor = configuration.newExecutor(tx, execType)获取执行器实例对象;④ 创建DefaultSqlSession实例对象,new DefaultSqlSession(configuration, executor, autoCommit)

② 核心方法getTransactionFactoryFromEnvironment

configuration实例的TypeAliasRegistry中关于DataSourceTransactionFactory默认配置如下:

jdbc=class org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory, managed=class org.apache.ibatis.transaction.managed.ManagedTransactionFactory, unpooled=class org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory, pooled=class org.apache.ibatis.datasource.pooled.PooledDataSourceFactory, jndi=class org.apache.ibatis.datasource.jndi.JndiDataSourceFactory,

如下方法所示,如果mybatis全局配置文件中environments结点没有配置transactionManager则会默认创建一个ManagedTransactionFactory实例对象。

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {if (environment == null || environment.getTransactionFactory() == null) {return new ManagedTransactionFactory();}return environment.getTransactionFactory();}

③ JdbcTransactionFactory 和JdbcTransaction

public class JdbcTransactionFactory implements TransactionFactory {@Overridepublic Transaction newTransaction(Connection conn) {return new JdbcTransaction(conn);}@Overridepublic Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {return new JdbcTransaction(ds, level, autoCommit);}}

JdbcTransaction 主要属性与构造方法

public class JdbcTransaction implements Transaction {private static final Log log = LogFactory.getLog(JdbcTransaction.class);protected Connection connection;protected DataSource dataSource;//失物隔离级别protected TransactionIsolationLevel level; //是否自动提交protected boolean autoCommit;public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {dataSource = ds;level = desiredLevel;autoCommit = desiredAutoCommit;}//...}

【2】configuration.newExecutor(tx, execType)

这里是很关键的一步,会创建当前sqlsession对应的执行器Executor,并对其进行拦截器链包装(mybatis提供的插件支持)。

四大对象:Executor,StatementHandler,ParameterHandler,ResultSetHandler

四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理 。

newExecutor方法如下所示:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor 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 (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

代码解释如下:

① 获取执行器类型,默认为SIMPLE(有SIMPLE, REUSE, BATCH三种);② 根据executorType创建对应的Executor实例对象;③ 如果开启了二级缓存配置,则创建CachingExecutor;

CachingExecutor有成员属性Executor delegate,主要工作交给delegate来做。CachingExecutor是在②中创建的Executor实例对象基础上做了缓存处理。这里是委派模式的应用

interceptorChain.pluginAll(executor)对最后得到的Executor实例对象进行拦截器链包装

BatchExecutor|ReuseExecutor|SimpleExecutor|CachingExecutor

SimpleExecutor主要属性与构造方法

public class SimpleExecutor extends BaseExecutor {public SimpleExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}//...}

BatchExecutor 主要属性与构造方法

public class BatchExecutor extends BaseExecutor {public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;private final List<Statement> statementList = new ArrayList<>();private final List<BatchResult> batchResultList = new ArrayList<>();private String currentSql;private MappedStatement currentStatement;public BatchExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}//...}

ReuseExecutor 主要属性与构造方法

public class ReuseExecutor extends BaseExecutor {private final Map<String, Statement> statementMap = new HashMap<>();public ReuseExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}//...}

可以发现上述几个Exceutor的构造方法都调用了super(configuration, transaction);,其父类是抽象类BaseExecutor:

public abstract class BaseExecutor implements Executor {private static final Log log = LogFactory.getLog(BaseExecutor.class);//事务protected Transaction transaction;//执行器包装器,比如CachingExecutor 可能是SimpleExecutor的包装对象(也可能是其他)protected Executor wrapper;protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//本地缓存-一级缓存protected PerpetualCache localCache;//OutputParameter该类型的本地缓存protected PerpetualCache localOutputParameterCache;//全局配置实例对象configurationprotected Configuration configuration;protected int queryStack;private boolean closed;protected BaseExecutor(Configuration configuration, Transaction transaction) {this.transaction = transaction;this.deferredLoads = new ConcurrentLinkedQueue<>();this.localCache = new PerpetualCache("LocalCache");this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");this.closed = false;this.configuration = configuration;this.wrapper = this;}//...}

CachingExecutor 主要属性与构造方法

public class CachingExecutor implements Executor {private final Executor delegate;private final TransactionalCacheManager tcm = new TransactionalCacheManager();//委派模式public CachingExecutor(Executor delegate) {this.delegate = delegate;delegate.setExecutorWrapper(this);}//...}

CachingExecutor是在其他Executor 实例对象的基础上做了包装,实例了自己的事务管理器TransactionalCacheManager。CachingExecutor通常将数据行为**委派**给其成员实例Executor delegate处理

这里CachingExecutor 应用了设计模式中的装饰者模式(Decorator Pattern)/包装器模式(Wrapper Pattern)

interceptorChain.pluginAll(executor)

InterceptorChain 拦截器链对象,源码如下

public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

其核心方法Object pluginAll(Object target)就是对target进行拦截器链的层层代理,最后返回一个代理对象。如下代码所示就是对executor进行拦截器链包装然后返回,这也是mybatis对插件开发的支持。

executor = (Executor) interceptorChain.pluginAll(executor);

不只是executor实例化时进行了插件包装,如下图所示ParameterHandler、ResultSetHandler和StatementHandler实例化时都进行了插件包装。

target = interceptor.plugin(target);

这里以mybatisplus的分页插件PaginationInterceptor 为例说明。

如下所示,可以看到PaginationInterceptor 标明了对StatementHandler.classprepare方法感兴趣。那么在实例化StatementHandler时触发Object pluginAll(Object target)就会调用PaginationInterceptor 的plugin方法对StatementHandler进行代理。

@Setter@Accessors(chain = true)@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor {//...}

PaginationInterceptor 的plugin方法

public Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}

可以看到,如果target类型是StatementHandler,将会执行Plugin.wrap(target, this);

这里面target理解为StatementHandler实例,this为当前PaginationInterceptor 实例

Plugin的wrap方法

Plugin主要属性和构造方法

public class Plugin implements InvocationHandler {//目标对象private final Object target;//拦截器对象private final Interceptor interceptor;//拦截器感兴趣的类型与方法private final Map<Class<?>, Set<Method>> signatureMap;private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {this.target = target;this.interceptor = interceptor;this.signatureMap = signatureMap;}

wrap方法获取代理对象

//假设这里面target理解为StatementHandler实例,interceptor为当前PaginationInterceptor 实例public static Object wrap(Object target, Interceptor interceptor) {Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();//type 是target类型 signatureMap是拦截器的//获取interceptor感兴趣的类型,该类型与type的接口类型是同一类型Class<?>[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}

代码解释如下:

① 根据interceptor类上@Intercepts注解获取Map<Class<?>, Set<Method>> signatureMap;② 获取target的Class类型;③ 从①和②中确定interceptor支持的接口类型;④Proxy.newProxyInstance创建代理对象

代理(包装)后的对象示例如下:

这里h表示其是一个InvocationHandler实例对象,target是一个代理对象。当目标方法被调用时会先被hinvoke方法处理,然后再调用目标方法。

参考博文:

MyBatis Plus插件机制与执行流程原理分析

Java中动态代理使用与原理详解

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。