mybatis Guice 事务源码解析
http://mybatis.org/guice/getting-started.html
https://blog.csdn.net/bingospunky/article/details/79541494
https://zhuanlan.zhihu.com/p/133807626?utm_source=wechat_session&utm_medium=social&utm_oi=1003056052560101376&utm_content=sec
在攻读了 mybatis-guice源码后,得以准确切入,制造事务代理:mybatis guice 事务代理切面
通过调试跟踪,guice-mybatis,整个过程最核心的,同数据源所有mapper生成mapperProxy对象各不相同,但在mapperProxyFactory.newInstance时都会在mapperProxy注入同一个sqlSessionManager --mapperProxyFactory.newInstance(sqlSessionManager) SqlSessionManager 实现了SqlSession,存在MapperProxy里面的sqlSession字段,后来在mybatis guice 事务代理切面中,我们用反射取之来操作事务
然后,就变成了:
mapper1/mapper2.xxx -> sqlSessionManager.xxx -> sqlSessionProxy.xxx -> sqlSessinManager.interceptor -> localSqlSession / openSession -> DefaultSqlSesion.xxx
与一般过程容易搞混:new DefaultSqlSession -> defaultSqlSession.getMapper(Mapper.class) -> mapperRegistry.getMapper(Mapper.class, defaultSqlSession) -> mapperProxyFactory.newInstance(defaultSqlSession) DefaultSqlSession 也实现了SqlSession
问题:
1 mybatis何时用事务con,何时用非事务con
2 SqlSession与Connection的对应关系
3 mybatis 事务是有传播,如何传播?
4 mapper的sqlSession被注入了一个sqlSessionManager?yet可能是mybatis guice框架干的,可结合mybatis-spring的干法
<dependency> <groupId>com.digitalpetri.modbus</groupId> <artifactId>modbus-master-tcp</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-guice</artifactId> <version>3.12</version> </dependency> <!-- https://mvnrepository.com/artifact/com.google.inject/guice --> <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>4.2.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency>
public class Main { public static void main(String []f) throws Exception { // System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); Properties myBatisProperties = new Properties(); myBatisProperties.setProperty("mybatis.environment.id", "test"); myBatisProperties.setProperty("JDBC.driver", "com.mysql.jdbc.Driver"); myBatisProperties.setProperty("JDBC.username", "root"); myBatisProperties.setProperty("JDBC.password", "memories"); myBatisProperties.setProperty("JDBC.url", "jdbc:mysql://127.0.0.1:53306/mytest"); myBatisProperties.setProperty("JDBC.autocommit", "true"); Injector injector = Guice.createInjector( new MyBatisModule() { @Override protected void initialize() { Names.bindProperties(binder(), myBatisProperties); bindDataSourceProviderType(PooledDataSourceProvider.class); // No implementation for org.apache.ibatis.transaction.TransactionFactory was bound. bindTransactionFactoryType(JdbcTransactionFactory.class); // bindTransactionInterceptors(); addMapperClass(MyMapper.class); addMapperClass(MyMapper2.class); } }); SqlSessionFactory sessionFactory = injector.getInstance(SqlSessionFactory.class); SqlSessionManager sessionManager = injector.getInstance(SqlSessionManager.class); MyMapper myMapper = injector.getInstance(MyMapper.class); MyMapper2 myMapper2 = injector.getInstance(MyMapper2.class); // myMapper.list(); // myMapper2.list(); MyService myService = injector.getInstance(MyService.class); // try { // myService.insertTran(); // } catch (Exception e) { // e.printStackTrace(); // } // myService.insertNonTran(); myService.trannon(); }
结论:
1 在SqlSessionManager.SqlSessionInterceptor中,判断threadLocal中有无sqlSession
SqlSession sqlSession = (SqlSession)SqlSessionManager.this.localSqlSession.get(); if(sqlSession != null) { try { return method.invoke(sqlSession, args); } catch (Throwable var19) { throw ExceptionUtil.unwrapThrowable(var19); } } else { SqlSession autoSqlSession = SqlSessionManager.this.openSession(); Throwable var6 = null; Object var8; try { try { Object t = method.invoke(autoSqlSession, args);
2
DefaultSqlSession-BaseExecutor-Transaction-Connection
一对一,值得注意的是Connection是个接口,本例就是com.mysql.jdbc.JDBC4Connection@2a62b5bc
resetAutoCommit 我们在myorm【重点】也吃过药
3 TransactionalMethodInterceptor中isSessionInherited,直接使用局部变量
boolean isSessionInherited = this.sqlSessionManager.isManagedSessionStarted();
public boolean isManagedSessionStarted() { return this.localSqlSession.get() != null; }
} finally { if(!isSessionInherited) { try { if(needsRollback) { if(this.log.isDebugEnabled()) { this.log.debug(debugPrefix + " - SqlSession of thread: " + Thread.currentThread().getId() + " rolling back"); } this.sqlSessionManager.rollback(true); } else { if(this.log.isDebugEnabled()) { this.log.debug(debugPrefix + " - SqlSession of thread: " + Thread.currentThread().getId() + " committing"); } this.sqlSessionManager.commit(transactional.force());