博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring-data-jpa通过Atomikos实现JTA事务
阅读量:4047 次
发布时间:2019-05-25

本文共 9760 字,大约阅读时间需要 32 分钟。

最近刚搭建一个Atomikos实现的JTA,数据库持久层用的spring-data-jpa,底层实现是Hibernate。

一.环境

spring 3.2.6.RELEASE

hibernate 4.2.8.Final

spring-data-jpa 1.4.3.RELEASE

atomikos.version 3.9.2

 

永久链接: http://sgq0085.iteye.com/blog/2001918

 

atomikos在Maven环境中导入依赖如下

com.atomikos
transactions-jdbc
3.9.2
com.atomikos
transactions-hibernate3
3.9.2
javax.transaction
jta
1.1

 

atomikos依赖结构如下

[INFO] +- com.atomikos:transactions-jdbc:jar:3.9.2:compile[INFO] |  \- com.atomikos:transactions-jta:jar:3.9.2:compile[INFO] |     \- com.atomikos:transactions:jar:3.9.2:compile[INFO] |        \- com.atomikos:transactions-api:jar:3.9.2:compile[INFO] |           \- com.atomikos:atomikos-util:jar:3.9.2:compile[INFO] +- com.atomikos:transactions-hibernate3:jar:3.9.2:compile[INFO] +- javax.transaction:jta:jar:1.1:compile

 

 

二.配置

1.web.xml

  spring.profiles.default为了配合配置文件applicationContext.xml中<beans profile="development">标签,用于管理多套环境

 

spring.profiles.default
development
contextConfigLocation
classpath*:/applicationContext.xml
openEntityManagerInViewFilter
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
entityManagerFactoryBeanName
entityManagerFactory1
openEntityManagerInViewFilter
/*
 

 

2.persistence.xml

 

 

 这里JPA 不在 persistence.xml 文件中配置每个Entity实体类,在后面的applicationContext.xml中通过<property name="packagesToScan" value="com.myapp" />去扫描所有标记@Entity注解的PO。

 

3.application.properties

数据库一个Oracle,一个H2

 

#developmentdev.jdbc.driver=oracle.jdbc.driver.OracleDriverdev.jdbc.url=jdbc:oracle:thin:@192.168.3.129:1521:gtfdev.jdbc.username=my_appdev.jdbc.password=my_appdev.jdbc.driver2=org.h2.jdbcx.JdbcDataSourcedev.jdbc.url2=jdbc:h2:file:~/.h2/myapp;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSEdev.jdbc.username2=sadev.jdbc.password2=databasePlatform.oracle=org.hibernate.dialect.Oracle10gDialectdatabasePlatform.h2=org.hibernate.dialect.H2Dialect
 

 

4.applicationContext.xml

Spring公共配置
false
false
after_statement
org.hibernate.cfg.ImprovedNamingStrategy
true
none
oracle.jdbc.xa.client.OracleXADataSource
jta
com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
false
false
after_statement
org.hibernate.cfg.ImprovedNamingStrategy
true
none
oracle.jdbc.xa.client.OracleXADataSource
jta
com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
${dev.jdbc.url}
${dev.jdbc.username}
${dev.jdbc.password}
${dev.jdbc.url2}
${dev.jdbc.username2}
${dev.jdbc.password2}

 

 

 

 

注意:

1.Entity可以被两个EntityManagerFactory同时扫描到

2.通过jpa:repositories的base-package来区分两个EntityManagerFactory维护的持久层在这里这个用dao包,另一个用repository

 

三.测试

 

-------------- 表1 ------------create table jta_pu1 (	id char(32),	description varchar2(255),    primary key (id));-------------- 表2 ------------create table jta_pu2 (	id char(32),	description varchar2(255),    primary key (id));
 

 

在Oracle中建表1

在H2中表1和表2都建

 

三个Entity类

 

package com.myapp.jta.entity;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.MappedSuperclass;import org.hibernate.annotations.GenericGenerator;@MappedSuperclasspublic abstract class BaseEntity implements Serializable {	private static final long serialVersionUID = 1L;	protected String id;	@Id	@Column(name = "id", nullable = false)	@GeneratedValue(generator = "system-uuid")	@GenericGenerator(name = "system-uuid", strategy = "uuid")	public String getId() {		return id;	}	public void setId(String id) {		this.id = id;	}}
 

 

 

package com.myapp.jta.entity;import javax.persistence.Entity;import javax.persistence.Table;@Entity@Table(name = "JTA_PU1")public class PU1 extends BaseEntity {    private String description;    public String getDescription() {        return description;    }    public void setDescription(String description) {        this.description = description;    }}
 

 

 

package com.myapp.jta.entity;import javax.persistence.Entity;import javax.persistence.Table;@Entity@Table(name = "JTA_PU2")public class PU2 extends BaseEntity {    private String description;    public String getDescription() {        return description;    }    public void setDescription(String description) {        this.description = description;    }}
 

 

三个数据库持久层类

对应数据源一

 

package com.myapp.jta.dao;import org.springframework.data.jpa.repository.JpaRepository;import com.myapp.jta.entity.PU1;public interface PU1Dao extends JpaRepository
{}
 

 

对应数据源二

 

package com.myapp.jta.repository;import org.springframework.data.jpa.repository.JpaRepository;import com.myapp.jta.entity.PU1;public interface PU1Repository extends JpaRepository
{}
 
package com.myapp.jta.repository;import org.springframework.data.jpa.repository.JpaRepository;import com.myapp.jta.entity.PU2;public interface PU2Repository extends JpaRepository
{}
 

 

注意源一对应的包名是dao,源二对应的包名是repository

 

Service比较简单只有一个save,第一次不抛出异常,第二次抛出异常来测试事务回滚。

package com.myapp.jta.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import com.myapp.jta.dao.PU1Dao;import com.myapp.jta.entity.PU1;import com.myapp.jta.entity.PU2;import com.myapp.jta.repository.PU1Repository;import com.myapp.jta.repository.PU2Repository;@Service@Transactionalpublic class JTAService {    @Value("${property.value}")    private String propertyValue;    @Autowired    private PU1Dao pu1Dao;    @Autowired    private PU1Repository pu1Repository;    @Autowired    private PU2Repository pu2Repository;    public void save() {        PU1 pu = new PU1();        pu.setDescription(propertyValue);        pu1Dao.save(pu);        pu1Repository.save(pu);        PU2 pu2 = new PU2();        pu2.setDescription("pu2");        pu2Repository.save(pu2);        // throw new RuntimeException();    }}

 

JUnit4的测试用例

package com.myapp.jta.service;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ActiveProfiles;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import org.springframework.test.context.transaction.TransactionConfiguration;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = { "/applicationContext.xml"})@ActiveProfiles("development")@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)public class JTAServiceTest {    @Autowired    private JTAService jtaService;    @Test    public void savePU1() {        jtaService.save();    }}

 

说明,同一个EntityPU1分别被保存到数据源一和数据源二中,但通过查询数据库可以看到第一次保存的时候id被赋值,第二次保存的时候id没有再做修改。但查询数据库,发现第二次的id与第一次的不同。所以真有同一个Entity保存到两个数据库中的需求时,推荐在保存之前深拷贝一份出来,分别保存。 

 

永久链接: http://sgq0085.iteye.com/blog/2001918

 

注意两个离奇错误

错误1: 数据库必须是DBA,否则出错 (具体哪个权限没有去排查,感兴趣找到了告诉我)

错误2:用maven tomcat7-maven-plugin插件时小心useTestClasspath需要是false

 

DEMO中在使用jdbcdslog的基础上,修改Atomikos核心类,实现JTA管理数据源下SQL的输出。

注意:该方法未经过大量测试,只适用于开发环境,生产环境不可以使用。

转载地址:http://fmyci.baihongyu.com/

你可能感兴趣的文章
指导教师的shooow
查看>>
leetcode面试题01.06.字符串压缩,超出时间限制,样例通过31/32
查看>>
机器学习实战、第二章KNN算法详解、AttributeError: ‘dict‘ object has no attribute ‘iteritems‘
查看>>
leetcode 535 TinyURL 的加密与解密 暴力 年轻人不讲武德—shooter7的博客
查看>>
课程设计(毕业设计)—基于机器学习KNN算法手写数字识别系统—计算机专业课程设计(毕业设计)
查看>>
leetcode1792第232场周赛第三题,以及二维数组根据某一列进行排序——优先队列
查看>>
学生网上选课管理系统的设计与实现—计算机类专业课程设计(毕业设计)
查看>>
新建动态web工程项目红叉报错,以及Could not publish server configuration for Tomcat v9.0 Server at localhost.
查看>>
机器学习SVM的车牌识别系统—计算机专业课程设计(毕业设计)
查看>>
leetcode 80. 删除有序数组中的重复项 II
查看>>
课程设计(毕业设计)—学生宿舍管理系统—计算机类专业
查看>>
毕业设计(课程设计)—SpringBoot网上订餐系统的设计与实现—计算机类专业课程设计(毕业设计)
查看>>
毕业设计(课程设计)—个人博客系统(微博)的设计与实现—计算机类专业课程设计(毕业设计)
查看>>
牛客(中兴捧月)—B-切绳子
查看>>
剑指Offer 13.机器人的运动范围——DFS和BFS
查看>>
Java中GUI编程总结—AWT中的Frame容器、panel面板、布局管理
查看>>
剑指offer12.矩阵中的路径—DFS+剪枝
查看>>
Java中GUI编程总结—事件监听、TextField监听、画笔、(鼠标、窗口、键盘)监听
查看>>
Java中GUI编程总结—Swing(窗口、面板、弹窗、标签、按钮、列表、文本框)
查看>>
Java中map容器分别根据键key和值value进行排序的总结
查看>>