mybatis 动态代理 事务 初探
背景:
1 我们有一个自己的事务代理工厂
2 工厂内原先为connection.xxx()
3 mybatis怎么start transaction(set autocommit false),commit,rollback
https://blog.csdn.net/suifeng629/article/details/103232904 mybatis中的事务管理
public static void main(String[] args) { SqlSession sqlSession = MybatisSqlSessionFactory.openSession(); try { StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student = new Student(); student.setName("yy"); student.setEmail("email@email.com"); student.setDob(new Date()); student.setPhone(new PhoneNumber("123-2568-8947")); studentMapper.insertStudent(student); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); } finally { sqlSession.close(); } }
解决了第3点,还提到了sqlSession.close中resetAutocommit,myorm【重点】中碰到过这个坑
面对上面这段代码,我们不禁好奇,connection.close()之前,居然调用了一个resetAutoCommit(),含义为重置autoCommit属性值。
connection.close()含义为销毁conn,既然要销毁conn,为何还多此一举的调用一个resetAutoCommit()呢?消失之前多喝口水,真的没有必要。
其实,原因是这样的,connection.close()不意味着真的要销毁conn,而是要把conn放回连接池,供下一次使用,既然还要使用,自然就需要重置AutoCommit属性了。
通过生成connection代理类,来实现重回连接池的功能。如果connection是普通的Connection实例,那么代码也是没有问题的,双重支持。
4 mapper被上层框架注入guice,获取时inject.get(xxxMapper.class),怎么样得到sqlSession
https://blog.csdn.net/rocklee/article/details/88762361 Mybatis从Mapper实例获取Sqlsession对象
在一个事务中,若想利用mapper实例同一个sqlsession来同时做其他事,因为mapper字面的定义为interface,没有任何其他可用属性,但在运行过程中发现其实这个mapper实例已经被动态代理实例化了,具体可参考这位大神的分析:https://www.bbsmax.com/A/nAJvPBw3dr/ 因为mapper实例的真实处理代理类是MapperProxy, 所以就写了一个function直接从MapperProxy中取得private final属性的sqlsession, 用了反射功能
/*** * 从Mapper实例获取Sqlsession对象 * @param pvMapper * @return */ private static SqlSession getSqlSessionFromMapperInstance(Object pvMapper) { if (!Proxy.isProxyClass(pvMapper.getClass()))return null; InvocationHandler lvInvHandler=Proxy.getInvocationHandler(pvMapper); if (!(lvInvHandler instanceof MapperProxy)) return null; MapperProxy lvT= (MapperProxy)lvInvHandler ; try { Field lvSessField=lvT.getClass().getDeclaredField("sqlSession"); lvSessField.setAccessible(true); try { return (SqlSession) lvSessField.get(lvT); } catch (IllegalArgumentException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (NoSuchFieldException | SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
5 我们要求同一个dbsource,所有mapper共用一个sqlsession?
https://blog.csdn.net/Message_lx/article/details/89533045
sqlSession调用Threadlocal确保每个线程一个连接,连接同样跟thread绑定
在6中,明确了,同一个source的mappers共用一个sqlSession(sqlSessionManager)
6 实践:
InvocationHandler lvInvHandler0 = Proxy.getInvocationHandler(odsTransactionMapper); 空mapper
ManagedMapperProvider managedMapperProvider0 = (ManagedMapperProvider)lvInvHandler0;
SqlSessionManager sqlSessionManager0 = managedMapperProvider0.getSqlSessionManager();
InvocationHandler lvInvHandler1 = Proxy.getInvocationHandler(auditTrailMapper);
ManagedMapperProvider managedMapperProvider1 = (ManagedMapperProvider)lvInvHandler1;
SqlSessionManager sqlSessionManager1 = managedMapperProvider1.getSqlSessionManager();
InvocationHandler lvInvHandler2 = Proxy.getInvocationHandler(communicationMapper);
ManagedMapperProvider managedMapperProvider2 = (ManagedMapperProvider)lvInvHandler2;
SqlSessionManager sqlSessionManager2 = managedMapperProvider2.getSqlSessionManager();
InvocationHandler lvInvHandler3 = Proxy.getInvocationHandler(xRefMapper);
ManagedMapperProvider managedMapperProvider3 = (ManagedMapperProvider)lvInvHandler3;
SqlSessionManager sqlSessionManager3 = managedMapperProvider3.getSqlSessionManager();
SqlSessionManager sqlSessionManager = sqlSessionManager0;
sqlSessionManager.startManagedSession(false);
communicationMapper.insert(communicationDto); 没有
sqlSessionManager.commit(); 有
communicationMapper.insert(communicationDto); connection is closed
sqlSessionManager.close();
communicationMapper.insert(communicationDto); 有
System.out.println();
测试:
1 managedMapperProvider,0 1 2 3 各不相同
2 SqlSessionManager 0 1 2 相同 3不同
3 这个方案并不能保证sqlSessionManager close之后,Connection如果池化的,不确定是否resetAutocommit
在mybatis Guice 事务源码解析中,我们阅读源码后确定了,会经过Transaction调用
但这个方案最终失败了
整个失败的原因其中之一有:
框架使用
CoreMyBatisTransaction implements Transaction
CoreMyBatisTransactionFactory implements TransactionFactory
在CoreMyBatisTransaction中又使用一个类,该类实现了spring PlatfromTransactionManager
在这个过程中,transaction(CoreByBatisTransaction).rollback,不同于mybatis自己的JdbcTransaction.rollback直接操作Connection.rollback了,它继续走到了PlatformTransactionManager.rollback,绕啊绕啊,本身有bug,导致rollback上面有一个永远为false的判断