首页 文章 精选 留言 我的

精选列表

搜索[JPA],共1052篇文章
优秀的个人博客,低调大师

spring+atomikos+mytatis+jpa实现分布式事务管理实战篇

最近在工作中,由于业务的发展需求,需要搭建一个类似中台的项目(拥有独立的数据库),那么跟我们项目中原本存在库,一共两个库,而数据库服务器为MYSQL,这时候就涉及到分布式事务的管理了。我也在网上找了很多解决方案,但貌似aomikos+mybatis+jta的解决方案比较少,只能参考网上的案例来集成代码到我们的项目当中,运行起来发现不定时抛出一些莫名其妙的异常,凭着自己的感觉一步步把这些坑给修补了,跑了两天也没看到异常抛出,程序也正常执行,注:我是在dubbo服务接口层以及spring quartz应用中都集成了atomikos+jta。下面来看一下集成的步骤 1)首先引入JAR包,我使用的是MAVEN来管理项目以及JAR包 <!-- transaction --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-api</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> </dependency> <!-- transaction --> 2)在你的项目class包中加入jta.properties配置文件,由于MAVEN JAVA项目,src/main/resources下面存放的在编译时,会放到class里,所以我们放在 而jta.properties的配置内容如下,按需调整吧 com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory com.atomikos.icatch.console_file_name = ${log.dir}jta/tm.out com.atomikos.icatch.log_base_name = tmlog com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm com.atomikos.icatch.console_log_level=ERROR com.atomikos.icatch.enable_logging=false 配置完这个之后,下面就是数据源以及事务的配置了,数据源配置如下: <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true"> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="poolSize" value="10" /> <!--min-pool-size 最小连接数--> <property name="minPoolSize" value="10" /> <!--max-pool-size 最大连接数 --> <property name="maxPoolSize" value="30" /> <!--获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回--> <property name="borrowConnectionTimeout" value="60" /> <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候, 一旦超过5分钟,就会抛出类似 Resultset is close 的错误 --> <property name="reapTimeout" value="20" /> <!-- max-idle-time 最大闲置时间,超过最小连接池连接的连接将将关闭 --> <property name="maxIdleTime" value="60" /> <!-- maintenance-interval 连接回收时间 --> <property name="maintenanceInterval" value="60" /> <!-- login-timeout java数据库连接池,最大可等待获取datasouce的时间 --> <property name="loginTimeout" value="60" /> <property name="testQuery" value="SELECT 1" /> <!-- max-lifetime 连接最大存活时间 --> <property name="maxLifetime" value="60"></property> </bean> <!-- WS数据源配置, 使用DBCP数据库连接池 --> <bean id="saleDataSource" parent="abstractXADataSource"> <property name="uniqueResourceName" value="saleDB" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="url">${jdbc.url}</prop> <prop key="password">${jdbc.password}</prop> <prop key="user">${jdbc.username}</prop> <prop key="autoReconnect">true</prop> <prop key="pinGlobalTxToPhysicalConnection">true</prop> </props> </property> </bean> <!-- 主数据源配置, 使用DBCP数据库连接池 --> <bean id="masterDataSource" parent="abstractXADataSource"> <property name="uniqueResourceName" value="masterDB" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="url">${csmjdbc.url}</prop> <prop key="password">${jdbc.password}</prop> <prop key="user">${jdbc.username}</prop> <prop key="autoReconnect">true</prop> <prop key="pinGlobalTxToPhysicalConnection">true</prop> </props> </property> </bean> <!-- 主库MyBatis配置 --> <bean id="sqlSessionFactoryMain" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="masterDataSource" /> <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 --> <property name="typeAliasesPackage" value="${type.aliases.package:com.csair}" /> <!-- 显式指定Mapper文件位置 --> <property name="mapperLocations" value="classpath*:/main/dao/mapper/**/*.xml" /> <property name="typeHandlersPackage" value="${type.handlers.package:com.csair.diamond.repository.mybatis.handler}" /> <property name="configLocation" value="classpath:/META-INF/mybatis-config.xml" /> </bean> <!-- WS库的MyBatis配置 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="saleDataSource" /> <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 --> <property name="typeAliasesPackage" value="${type.aliases.package:com.csair}" /> <!-- 显式指定Mapper文件位置 --> <property name="mapperLocations" value="classpath*:/dao/mapper/**/*.xml" /> <property name="typeHandlersPackage" value="${type.handlers.package:com.csair.diamond.repository.mybatis.handler}" /> <property name="plugins"> <list> <ref bean="mybatisSqlInjectionHandlerInterceptor" /> <ref bean="mybatisStatementHandlerInterceptor" /> <ref bean="mybatisResultSetHandlerInterceptor" /> </list> </property> <property name="configLocation" value="classpath:/META-INF/mybatis-config.xml" /> </bean> <bean id="mybatisStatementHandlerInterceptor" class="com.csair.diamond.repository.mybatis.interceptor.StatementHandlerInterceptor"> <property name="dialectClass" value="com.csair.diamond.repository.mybatis.MySqlDialect" /> </bean> <bean id="mybatisResultSetHandlerInterceptor" class="com.csair.diamond.repository.mybatis.interceptor.ResultSetHandlerInterceptor"> </bean> <bean id="mybatisSqlInjectionHandlerInterceptor" class="com.csair.diamond.repository.mybatis.interceptor.SqlInjectionHandlerInterceptor"> </bean> <!-- 扫描basePackage下所有以@Repository标识的 接口 --> <bean name = "mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="${mapper.scanner.base.package:com.csair.csm.dao}" /> <property name="annotationClass" value="com.csair.diamond.repository.annotation.Repository" /> <!-- <property name="sqlSessionFactory" ref="sqlSessionFactory" /> --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <bean name = "mainMapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.csair.csm.main.dao" /> <property name="annotationClass" value="com.csair.diamond.repository.annotation.Repository" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryMain" /> </bean> 事务的配置如下 <!-- atomikos事务管理器 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <!-- close()时是否强制终止事务 --> <property name="forceShutdown"> <value>true</value> </property> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300" /> </bean> <!-- spring 事务管理器 --> <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager" /> <property name="userTransaction" ref="atomikosUserTransaction" /> <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default --> <property name="allowCustomIsolationLevels" value="true"/> </bean> <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" /> spring-quartz配置文件如下: <bean id="quartzSchedulerSupplier" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource"> <ref bean="saleDataSource" /> </property> <property name="applicationContextSchedulerContextKey" value="applicationContextKey" /> <property name="configLocation" value="classpath:quartz.properties" /> <property name="triggers"> <list> <ref bean="triggerSupplier" /> <ref bean="triggerProduct" /> <ref bean="triggerPicture" /> </list> </property> </bean> 我是使用事务的注解形式回滚事务,我原本是采用切入点来定义某个包下面的所有类以哪些方法开头是有事务并且回滚,哪些是没有事务的,但是集成spring quartz会发现事务开启不一致的问题,spring quartz也有自己的数据源,一套自己的事务,如果不用事务注解,定时任务会开启自己的本地事务,而定时任务调用服务又是一个XA的事务,这时候就会抛事务不统一。 Caused by: com.atomikos.datasource.ResourceException: XA resource 'mysql/first': resume for XID '3137322E31362E33312E38332E746D30303030313030303131:3137322E31362E33312E38332E746D31' raised -9: the XA resource is currently involved in a local (non-XA) transaction 解决方案:采用事务注解,并且在定时任务入口类标注上注解。 如果发现以下这种错误异常问题,主要是系统有两个事务,一个事务提交,一个事务未提交,导致事务冲突,建议采用事务注解在最外层方法标注上。 Cannot call method 'commit' while a global transaction is runing(因为我是继承了定时任务,在方法内部手动开启事务,当我方法内部事务还未提交时,定时任务自己内部的事务就提交了,这时候就会抛这个异常,如果集成定时任务,建议不要手动开启事务) 如果在运行期抛[ERROR][2016-11-03 10:17:30,771][com.atomikos.recovery.imp.CachedRepository]Corrupted log file - restart JVM com.atomikos.recovery.LogReadException: java.lang.ArrayIndexOutOfBoundsException: 1 at ,解决方案就是把atomikos的jar包版本升级到4.0.4即可。 如果在运行期发现一开始是正常,后期频繁抛如下的错误,根据我的填坑经验,这个是数据库连接池选型不对的问题,以及配置连接池属性跟数据库的配置不一致。 2019-05-06 17:57:37.865 [ Atomikos:2 ] - [ WARN ] [com.atomikos.recovery.xa.XaResourceRecoveryManager : 40] - Error while retrieving xids from resource - will retry later... com.mysql.jdbc.jdbc2.optional.MysqlXAException: No operations allowed after connection closed. at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.mapXAExceptionFromSQLException(MysqlXAConnection.java:608) at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.recover(MysqlXAConnection.java:335) at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.recover(MysqlXAConnection.java:255) at com.atomikos.datasource.xa.RecoveryScan.recoverXids(RecoveryScan.java:32) at com.atomikos.recovery.xa.XaResourceRecoveryManager.retrievePreparedXidsFromXaResource(XaResourceRecoveryManager.java:158) at com.atomikos.recovery.xa.XaResourceRecoveryManager.recover(XaResourceRecoveryManager.java:67) at com.atomikos.datasource.xa.XATransactionalResource.recover(XATransactionalResource.java:451) at com.atomikos.icatch.imp.TransactionServiceImp.performRecovery(TransactionServiceImp.java:490) at com.atomikos.icatch.imp.TransactionServiceImp.access$000(TransactionServiceImp.java:56) at com.atomikos.icatch.imp.TransactionServiceImp$1.alarm(TransactionServiceImp.java:471) at com.atomikos.timing.PooledAlarmTimer.notifyListeners(PooledAlarmTimer.java:95) at com.atomikos.timing.PooledAlarmTimer.run(PooledAlarmTimer.java:82) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 一开始xaDataSourceClassName属性值我是采用com.alibaba.druid.pool.xa.DruidXADataSource <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true"> <property name="xaDataSourceClassName" value="com.alibaba.druid.pool.xa.DruidXADataSource" /> <property name="poolSize" value="10" /> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="30" /> <property name="borrowConnectionTimeout" value="60" /> <property name="reapTimeout" value="20" /> <property name="maxIdleTime" value="60" /> <property name="maintenanceInterval" value="60" /> <property name="loginTimeout" value="60" /> <property name="testQuery" value="SELECT 1" /> </bean> 后来采用xaDataSourceClassName为com.mysql.jdbc.jdbc2.optional.MysqlXADataSource <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true"> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="poolSize" value="10" /> <!--min-pool-size 最小连接数--> <property name="minPoolSize" value="10" /> <!--max-pool-size 最大连接数 --> <property name="maxPoolSize" value="30" /> <!--获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回--> <property name="borrowConnectionTimeout" value="60" /> <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候, 一旦超过5分钟,就会抛出类似 Resultset is close 的错误 --> <property name="reapTimeout" value="20" /> <!-- max-idle-time 最大闲置时间,超过最小连接池连接的连接将将关闭 --> <property name="maxIdleTime" value="60" /> <!-- maintenance-interval 连接回收时间 --> <property name="maintenanceInterval" value="60" /> <!-- login-timeout java数据库连接池,最大可等待获取datasouce的时间 --> <property name="loginTimeout" value="60" /> <property name="testQuery" value="SELECT 1" /> <!-- max-lifetime 连接最大存活时间 --> <property name="maxLifetime" value="60"></property> </bean>

优秀的个人博客,低调大师

还在用 JPA 吗?mybatis 序列下最好用的 ORM 框架:xbatis 没有之一

1.9.7 更新内容 1:VO 字段注解支持 set 方法注解覆盖 2:数据库适配优化,支持自定义 DbType 3:getById 增加 Optional 获取的对应方法 getOptionalById 4:QueryChain 增加 Optional 方法,对应的是 get 方法 5:增加 uuidv7 主键生成器 1.8.7 更新内容: 1:为了更好的 JAVA+XML 结合,query 和 where 增加 tableAs (实体类,别名) 方法,用于自定义表名别名 2:XbatisConfig 改为 XbatisGlobalConfig 3:增加逻辑删除拦截器 4:updatedelete增加 原生 RETURNING (原生) 功能 5:增加原生 sql 查询方法和update delete RETURNING 功能 6:增加了一个Mapper 方法拦截器 7:增加exists/not exists 简易写法 通用 SQL 扩展: //类型支持 实体类,VO和普通POJO SysUser user = sysUserMapper.select(SysUser.class, "select * from t_sys_user where id =?", 1); //支持增删改,且支持返回数据 String user_name = sysUserMapper.executeAndReturning(String.class, "update t_sys_user set user_name=? where id=1 RETURNING user_name", "xxx"); //ORM写法 删除并返回被删除的数据(数据库原生操作) List<SysUser> list = DeleteChain.of(sysUserMapper) .in(SysUser::getId, 1, 2) .returning(SysUser.class) .returnType(SysUser.class) .executeAndReturningList(); //ORM写法 修改并返回修改后的数据(数据库原生操作)适合金额加减操作返回剩余金额 SysUser sysUser = UpdateChain.of(sysUserMapper) .eq(SysUser::getId, 1) .set(SysUser::getUserName, "abc2") .returning(SysUser.class) .returnType(SysUser.class) .executeAndReturning(); 分表配置 @Data @SplitTable(SysUserSplitter.class) public class SysUser { @TableId private Integer id; @SplitTableKey private Integer groupId; private String nickname; private String username; } public class SysUserSplitter implements TableSplitter { @Override public boolean support(Class<?> type) { return type == Integer.class ** type == int.class; } @Override public String split(String sourceTableName, Object splitValue) { Integer groupId = (Integer) splitValue; //分成10个表 return sourceTableName + "_" + groupId % 10; } } 分表就是这么简单,其他操作和常规无异!!! 1.7.7 更新内容: 1:QueryChain,DeleteChain,InsertChain,UpdateChain 支持 BasicMapper 方法 2:支持通用 BasicMapper,可不需要创建多个实体类 Mapper;一个 BasicMapper 即可使用所有功能 3:正式支持单 Mapper (写一个 Mapper 即可) 为什么推荐 xbatis?: xbatis 是一款超级强大的 ORM 框架 1:可多表 join(不再只能单表了) 2:代码分页,xml 还可以分页(可以不用 pagehelper 了) 3:良好的扩展能力:orm+sql 模板 (让 ORM 框架不再死板,扩展性极强) 4:强大的各种数据库适配,可在一套代码中 实现多个数据库适配;真正的 ORM hibernate 都做不到 6:极简的 api 设计,让开发者 不再迷糊 1.单表 +@Fetch 注解 + fetchFilter 方法 @Data @ResultEntity(SysUser.class) public class SysUserVo { private Integer id; private String userName; private String password; private Integer roleId; private LocalDateTime create_time; @Fetch(source = SysUser.class, property = "roleId", target = SysRole.class, targetProperty = "id") private List<SysRoleVo> sysRoles; } List<SysUserVO> list = QueryChain.of(sysUserMapper) .from(SysUser.class) .fetchFilter(SysUserVO::getRoles,where->where.eq(SysRole::getStatus,1)) .returnType(SysUserVO.class) .list(); fetchFilter 方法是对 @Fetch 注解的增强,没有特殊要求一般,可忽略 2. 单表查询 SysUser sysUser = QueryChain.of(sysUserMapper) .eq(SysUser::getId, 1) .eq(SysUser::getUserName,'admin') .get(); 3.VO 映射 @Data @ResultEntity(SysUser.class) public class SysUserVo { private Integer id; private String userName; //字段名字不一样时 @ResultEntityField(property = "password") private String pwd; } SysUserVO sysUserVO = QueryChain.of(sysUserMapper) .eq(SysUser::getId, 1) .eq(SysUser::getUserName,'admin') .returnType(SysUserVO.class) .list(); 4. join 查询 @Data @ResultEntity(SysUser.class) public class SysUserVo { private Integer id; private String userName; //字段名字不一样时 @ResultEntityField(property = "password") private String pwd; //映射一个对象 1对1 @NestedResultEntity(target = SysRole.class) prviate SysRole sysRole; //映射多个对象 1对多 @NestedResultEntity(target = SysRole.class) prviate List<SysRole> sysRoles; } List<SysUserRoleVO> list = QueryChain.of(sysUserMapper) .from(SysUser.class) .join(SysUser.class, SysRole.class) .returnType(SysUserRoleVO.class) .list(); 还有很多很多超级方便有趣的写法,欢迎大家来使用https://xbatis.cn 例如: 1 . 多表 join A 内嵌 B B 内嵌 C 都可以 2 . 不使用 join 使用 @Fetch 注解 + fetchFilter 方法实现 将 A JOIN B 变成 query A + query B 3 . 使用 @Paging 注解 实现你的 xml 自动分页 4 . 使用 SQL 模板,让你 ORM 更简单更容易扩展,再也不怕被框架限制了

资源下载

更多资源
Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册