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);

 

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());

 

posted on 2020-08-07 20:57  silyvin  阅读(513)  评论(0编辑  收藏  举报