风一更 -- 事务管理 -- Spring 事务

目标:精通- 事务是构建可靠企业级应用程序的最关键部分之一。


事务管理是横切关注点,不应该在业务逻辑中编码。

最合适的方式是允许以声明方式定义业务需求,让诸如 Spring, JEE 或 AOP之类的框架代替我们织入事务处理逻辑。

Spring 支持声明式和

编程式事务管理(Transaction API JTA)。

Spring 声明式事务,声明必须参与事务的相关方法(类或层中) 以及事务配置的细节.

1. spring 事务抽象层

2.JTA 实现全局事务: 需要跨越多个后端资源的全局事务。JTA  Java Transaction API.


本地事务特定于单个事务资源,如 JDBC连接。 

全局事务由容器管理且可以跨越多个资源。

Java 中,全局事务是通过JTA 实施的。与JTA兼容的事务管理器 通过各自的资源管理器连接到多个事务资源,而这些资源管理器

能够通过 XA 协议(一种定义了分布式事务的开放标准)与事务管理器进行通信。

并使用 2Phase commit ( 2PC ) 机制来确保所有后端数据源被更新 或 完全回滚。

如果任意后端资源失效,整个事务将回滚,因此对其他资源的更新也将回滚。

分布式事务 四部分。

1. 后端资源: RDBMS , MQ, ERP 系统 ;

2. 资源管理器,负责与后端资源交互,连接到 MySQL 数据库时,需要与 MySQL的 Java 连接器

提供的 MysqlXADataSource类进行交互。其他后端资源也都提供了自己的资源管理器.

3. JTA 事务管理器,负责管理,协调和同步参与事务的所有资源管理器的事务状态。我们使用XA协议,这是一种被广泛用于分布式事务处理的

开放标准。 JTA 事务管理器还支持 2PC, 以便所有更改一并提交,并且当任何资源更新失败时,回滚整个事务,从而导致任何资源都没有被更新。

整个机制由 Java Transaction Service (JTS) 规范指定 .

4. 应用程序,可以是应用程序本身,可以是运行应用程序的底层容器 或 Spring 框架,用来管理事务(开始,提交,回滚事务等)同时,应用程序

通过由JEE定义的各种标准与底层后端资源进行交互.  APP 通过JDBC连接到 RDBMS, JMS连接到 MQ , Java EE Conector Architecture (JCA) 连接ERP.

兼容 JEE 的 app server  JBOSS, GlassFish 都支持JTA, 通过JNDI查找提供事务,

对于独立应用程序或 Web容器 tomcat Jetty - Atomikos / Bitronix  提供JTA/XA 支持。


 

PlatformTransactionManager 接口使用 TransactionDefinition 和 TransactionStatus 接口来创建和管理事务。

实现:

CciLocalTransactionManager 类支持 JEE,JCA 和 通用客户端接口 CCI。

DataSourceTransactionManager 类用于通用JDBC 连接。

ORM 很多实现,JPA 类 JpaTransactionManager 类 和 Hibernate 5( HibernateTransactionManger) 

JMS JMSTransactionManager 类支持 JMS2.0

JTA 通用实现 JtaTransactionManager


ACID 原子性,一致性,隔离性,持久性。如果维护这些事务属性取决于事务资源。

无法控制 ACD, 可以控制事务的传播和超时,以及配置事务是否应为只读并指定隔离级别。

Spring将这些设置封装在 TransactionDefinition 接口中.

该接口被用于Spring的事务支持的核心接口,即 PlatformTransactionManager接口,该接口的实现在特定平台(如JDBC 或 JTA)上

执行事务管理。


核心方法 PlatformTransactionManager.getTransaction() 将 TransactionDefinition 接口作为参数,并返回一个TransactionStatus接口。

该接口用于事务执行。设置事务事务结果,检查事务是否完成,或是否是新事务。


TransactionDefinition 接口

 

 

 

 

 

 


 

 

使用 JPA 和 Hibernate 作为实现数据访问逻辑的持久化层,

使用Spring Data JPA 及其 存储库 抽象简化基本数据库操作的开发。


 

使用注解是在Spring中定义事务需求的最常见方法。

优点是:事务需求可以与详细事务属性(超时,隔离级别,传播行为)一起在代码本身内定义,从而使应用程序更容易跟踪和维护。

XML <tx:annotation-drivern> 标记。

@EnableJpaRepository 注解。 启用 Spring Data JPA 的存储库抽象。

专业环境中,将 持久化配置 DAO  与 事务配置(服务)分开。

 

 

 

 

 

@Transaction 不带任何属性的,意味着

事务传播是必需的,隔离是底层资源的默认隔离级别(read commit ) ,超时是默认的, 模式是可读写。

方法级别的属性会覆盖 类级别属性,其他保持不变。


 

启用适当的日志,设置 

 

 

在日志消息中看到与事务处理相关的信息。

在运行方法前,Spring的 JpaTransactionManager 使用默认属性创建了一个新的事务(事务名称与方法的完全限定类名相同),

但事务被设置为只读,然后提交查询,在完成且没有任何错误的情况下,事务被提交。

JpaTM负责处理事务的创建和提交操作.

 

 @Transactional( readOnly = true ) 属性应该被应用于所有查找方法。

主要原因是,大多数持久化程序会对只读事务执行一定程度的优化,Hibernate 不会维护从数据库检索且只读的托管实例的快照.

Save() 方法,完成更新操作后,Spring 的 JpaTransactionManager触发事务提交,从而导致Hibernate刷新持久上下文并将底层JDBC连接提交给数据库。

Spring data 中, 由CrudRepository 接口定义的方法已经用适当的事务属性进行了标记.

 


控制传播


 

 

声明式事务管理的另一种常见方法是使用 Spring 的 AOP 支持,

需要封装不属于自己项目的事务代码,且无法对代码进行编辑以添加 @Transaction 注解.

<context:component-sacen> 用于声明式事务管理的包名,

最重要的标记是 <aop:config> 和 <tx:advice>

<aop:config> 为服务层中的所有操作(xx.service 包中的所有实现类)定义一个切入点。

该通知引用了 ID 为 txAdvice 的bean (由 <tx:advice> 标记定义)。

事务是通过 aop显示完成的, 不需要@Transactional 注解。


 

编程式事务

1. 将一个 PlatformTransactionManager 实例注入到bean中,并直接与事务管理器进行交互.

2.使用Spring 提供的 TransactionTemplate 类。

 


全局事务,分布式事务 

企业级Java应用程序需要访问多个后端资源.

从外部业务合作伙伴受到的一条信息可能需要更新多个系统(CRM, ERP ) 的数据库,

生成一条消息,通过JMS 发送给 MQ,以便公司内部对该信息感兴趣的所有其他应用程序使用。

跨越多个后端资源的事务被称为 全局事务 / 分布式事务

全局事务的一个主要特征是保证了原子性,意味着所涉及的资源要么全部更新,要么都不更新。

还包括应该由 事务管理器处理的 复杂协调 和同步逻辑, JTA  Java Transaction API  是实现全局事务的事实标准.

和本地事务一样, Spring 支持 JTA 事务,并从业务代码中隐藏相关逻辑

在独立应用程序 或 Web 容器环境中使用 JTA 实现全局事务。 Atomikos, 一个被广泛使用的开源 JTA 事务管理器,

用于 非 JEE环境.


使用两个 JPA 实体管理器,  可以使用多个JPA持久化单元来区分后端数据库。

推荐使用 JtaTransactionManager.


 

 

emfBase bean中的属性 hibernate.transaction.factory_class和 hibernate.transaction.jtc.platform 这两个属性非常重要,

Hibernate 使用它们来查找底层的UserTransaction 和 TransactionManager bean, 从而参与正在管理全局事务的持久化上下文,

hibernate.transaction.coordinator_class, 让 Hibernate4 的 Atomikos 类使用 Hibernate5 所必须的。 


 

 

 

 


 

 

配置Mysql 连接属性时,注意 serverTimezone UTC

 

 


Spring boot Atomikos 分布式事务

MySql script

ddl

1 create user 'prospring5_a'@'localhost' identified by 'prospring5_a';
2 create schema MUSICDB_A;
3 GRANT ALL PRIVILEGES ON MUSICDB_A . * TO 'prospring5_a'@'localhost';
4 flush privileges
5 
6 create user 'prospring5_b'@'localhost' identified by 'prospring5_b';
7 create schema MUSICDB_B;
8 GRANT ALL PRIVILEGES ON MUSICDB_B . * TO 'prospring5_b'@'localhost';
9 flush privileges
View Code

dml

 1 CREATE TABLE `musicdb_a`.`SINGER` (
 2     ID INT NOT NULL AUTO_INCREMENT,
 3     FIRST_NAME VARCHAR(60) NOT NULL,
 4     LAST_NAME VARCHAR(40) NOT NULL,
 5     BIRTH_DATE DATE,
 6     VERSION INT NOT NULL DEFAULT 0,
 7     UNIQUE UQ_SINGER_1 (FIRST_NAME,LAST_NAME),
 8     PRIMARY KEY(ID)
 9 );
10 
11 CREATE TABLE `musicdb_a`.`ALBUM` (
12     ID INT NOT NULL AUTO_INCREMENT,
13     SINGER_ID INT NOT NULL,
14     TITLE VARCHAR(100) NOT NULL,
15     RELEASE_DATE DATE,
16     VERSION INT NOT NULL DEFAULT 0,
17     UNIQUE UQ_ALBUM_1 (SINGER_ID,TITLE),
18     CONSTRAINT FK_ALBUM_SNGER_1 foreign key (SINGER_ID)
19     REFERENCES SINGER(ID),
20     PRIMARY KEY(ID)
21 );
22 
23 CREATE TABLE `musicdb_a`.`INSTRUMENT` (
24     INSTRUMENT_ID VARCHAR(20) NOT NULL,
25     PRIMARY KEY(INSTRUMENT_ID)
26 );
27 
28 CREATE TABLE `musicdb_a`.`SINGER_INSTRUMENT` (
29     SINGER_ID INT NOT NULL,
30     INSTRUMENT_ID VARCHAR(20) NOT NULL,
31     PRIMARY KEY(SINGER_ID,INSTRUMENT_ID),
32     CONSTRAINT FK_SINGER_INSTRUMENT_1 foreign key(SINGER_ID)
33     REFERENCES SINGER(ID) ON DELETE CASCADE,
34     CONSTRAINT FK_SINGER_INSTRUMENT_2 foreign key(INSTRUMENT_ID)
35     REFERENCES INSTRUMENT(INSTRUMENT_ID)
36 );
37 
38 CREATE TABLE `musicdb_b`.`SINGER` (
39     ID INT NOT NULL AUTO_INCREMENT,
40     FIRST_NAME VARCHAR(60) NOT NULL,
41     LAST_NAME VARCHAR(40) NOT NULL,
42     BIRTH_DATE DATE,
43     VERSION INT NOT NULL DEFAULT 0,
44     UNIQUE UQ_SINGER_1 (FIRST_NAME,LAST_NAME),
45     PRIMARY KEY(ID)
46 );
47 
48 CREATE TABLE `musicdb_b`.`ALBUM` (
49     ID INT NOT NULL AUTO_INCREMENT,
50     SINGER_ID INT NOT NULL,
51     TITLE VARCHAR(100) NOT NULL,
52     RELEASE_DATE DATE,
53     VERSION INT NOT NULL DEFAULT 0,
54     UNIQUE UQ_ALBUM_1 (SINGER_ID,TITLE),
55     CONSTRAINT FK_ALBUM_SNGER_1 foreign key (SINGER_ID)
56     REFERENCES SINGER(ID),
57     PRIMARY KEY(ID)
58 );
59 
60 CREATE TABLE `musicdb_b`.`INSTRUMENT` (
61     INSTRUMENT_ID VARCHAR(20) NOT NULL,
62     PRIMARY KEY(INSTRUMENT_ID)
63 );
64 
65 CREATE TABLE `musicdb_b`.`SINGER_INSTRUMENT` (
66     SINGER_ID INT NOT NULL,
67     INSTRUMENT_ID VARCHAR(20) NOT NULL,
68     PRIMARY KEY(SINGER_ID,INSTRUMENT_ID),
69     CONSTRAINT FK_SINGER_INSTRUMENT_1 foreign key(SINGER_ID)
70     REFERENCES SINGER(ID) ON DELETE CASCADE,
71     CONSTRAINT FK_SINGER_INSTRUMENT_2 foreign key(INSTRUMENT_ID)
72     REFERENCES INSTRUMENT(INSTRUMENT_ID)
73 );
View Code

pom.xml

 1     <dependencies>
 2         <dependency>
 3             <groupId>org.springframework.boot</groupId>
 4             <artifactId>spring-boot-starter-jdbc</artifactId>
 5         </dependency>
 6         <dependency>
 7             <groupId>org.springframework.boot</groupId>
 8             <artifactId>spring-boot-starter-data-jdbc</artifactId>
 9         </dependency>
10         <dependency>
11             <groupId>org.springframework.boot</groupId>
12             <artifactId>spring-boot-starter-data-jpa</artifactId>
13         </dependency>
14         <dependency>
15             <groupId>org.apache.commons</groupId>
16             <artifactId>commons-lang3</artifactId>
17             <version>3.1</version>
18         </dependency>
19         <dependency>
20             <groupId>com.google.guava</groupId>
21             <artifactId>guava</artifactId>
22             <version>30.1-jre</version>
23         </dependency>
24         <dependency>
25             <groupId>org.hibernate</groupId>
26             <artifactId>hibernate-core</artifactId>
27             <version>6.0.0.Alpha6</version>
28             <type>pom</type>
29         </dependency>
30         <dependency>
31             <groupId>org.hibernate</groupId>
32             <artifactId>hibernate-entitymanager</artifactId>
33             <version>6.0.0.Alpha6</version>
34             <type>pom</type>
35         </dependency>
36         <dependency>
37             <groupId>org.hibernate.orm</groupId>
38             <artifactId>hibernate-ehcache</artifactId>
39             <version>6.0.0.Alpha6</version>
40         </dependency>
41 
42         <dependency>
43             <groupId>org.springframework</groupId>
44             <artifactId>spring-tx</artifactId>
45             <version>5.3.4</version>
46         </dependency>
47 
48         <dependency>
49             <groupId>org.springframework</groupId>
50             <artifactId>spring-aspects</artifactId>
51             <version>5.3.4</version>
52         </dependency>
53 
54         <dependency>
55             <groupId>com.atomikos</groupId>
56             <artifactId>transactions-hibernate4</artifactId>
57             <version>5.0.8</version>
58         </dependency>
59 
60         <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
61         <dependency>
62             <groupId>org.springframework</groupId>
63             <artifactId>spring-context-support</artifactId>
64             <version>5.3.4</version>
65         </dependency>
66         <dependency>
67             <groupId>org.springframework</groupId>
68             <artifactId>spring-orm</artifactId>
69             <version>5.3.4</version>
70         </dependency>
71 
72         <dependency>
73             <groupId>mysql</groupId>
74             <artifactId>mysql-connector-java</artifactId>
75             <scope>runtime</scope>
76         </dependency>
77 
78         <dependency>
79             <groupId>org.springframework.boot</groupId>
80             <artifactId>spring-boot-starter-test</artifactId>
81             <scope>test</scope>
82             <exclusions>
83                 <exclusion>
84                     <groupId>org.junit.vintage</groupId>
85                     <artifactId>junit-vintage-engine</artifactId>
86                 </exclusion>
87             </exclusions>
88         </dependency>
89     </dependencies>
View Code

XAJpaConfig

  1 package org.techroad.repositorydis.demo.config;
  2 
  3 import com.atomikos.jdbc.AtomikosDataSourceBean;
  4 import org.slf4j.Logger;
  5 import org.slf4j.LoggerFactory;
  6 import org.springframework.context.annotation.Bean;
  7 import org.springframework.context.annotation.Configuration;
  8 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  9 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
 10 import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
 11 
 12 import javax.persistence.EntityManager;
 13 import javax.persistence.EntityManagerFactory;
 14 import javax.print.DocFlavor;
 15 import javax.sql.DataSource;
 16 import java.util.Properties;
 17 
 18 /**
 19  * responsibility :
 20  *
 21  * @author : cj
 22  * @date : 2021/3/14
 23  */
 24 
 25 @Configuration
 26 @EnableJpaRepositories
 27 public class XAJpaConfig {
 28 
 29     private static Logger logger = LoggerFactory.getLogger(XAJpaConfig.class);
 30 
 31     @SuppressWarnings("unchecked")
 32     @Bean(initMethod = "init", destroyMethod = "close")
 33     public DataSource dataSourceA() {
 34         return getDataSourceWith("XADBMSA", xaAproperties());
 35     }
 36 
 37     @Bean
 38     public Properties xaAproperties() {
 39         Properties xaAprop = new Properties();
 40         xaAprop.setProperty("databaseName", "musicdb_a");
 41         xaAprop.setProperty("user", "prospring5_a");
 42         xaAprop.setProperty("password", "prospring5_a");
 43         xaAprop.setProperty("serverTimezone", "UTC");
 44         return xaAprop;
 45     }
 46 
 47     @SuppressWarnings("unchecked")
 48     @Bean(initMethod = "init", destroyMethod = "close")
 49     public DataSource dataSourceB() {
 50         return getDataSourceWith("XADBMSB", xaBproperties());
 51     }
 52 
 53     private DataSource getDataSourceWith(String uqResouceName, Properties properties) {
 54         try {
 55             AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
 56             dataSource.setUniqueResourceName(uqResouceName);
 57             dataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
 58             dataSource.setXaProperties(properties);
 59             dataSource.setPoolSize(1);
 60             return dataSource;
 61         } catch (Exception e) {
 62             logger.error("Populator DataSource bean ccannot be created!");
 63             return null;
 64         }
 65     }
 66 
 67     @Bean
 68     public Properties xaBproperties() {
 69         Properties xaBprop = new Properties();
 70         xaBprop.setProperty("databaseName", "musicdb_b");
 71         xaBprop.setProperty("user", "prospring5_b");
 72         xaBprop.setProperty("password", "prospring5_b");
 73         xaBprop.setProperty("serverTimezone", "UTC");
 74         return xaBprop;
 75     }
 76 
 77     @Bean
 78     public Properties hibernateProperties() {
 79         Properties hibernateProp = new Properties();
 80         hibernateProp.put("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
 81         hibernateProp.put("hibernate.transaction.jta.platform", "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");
 82         hibernateProp.put("hibernate.transaction.coordinator_class", "jta");
 83         hibernateProp.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
 84         hibernateProp.put("hibernate.show_sql", true);
 85         hibernateProp.put("hibernate.max_fetch_depth", 3);
 86         hibernateProp.put("hibernate.jdbc.batch_size", 10);
 87         hibernateProp.put("hibernate.jdbc.fetch_size", 50);
 88         return hibernateProp;
 89     }
 90 
 91     @Bean
 92     public EntityManagerFactory emfA() {
 93         return getEMF(dataSourceA(), "emfA");
 94     }
 95 
 96     @Bean
 97     public EntityManagerFactory emfB() {
 98         return getEMF(dataSourceB(), "emfB");
 99     }
100 
101     private EntityManagerFactory getEMF(DataSource dataSource, String unitName) {
102         LocalContainerEntityManagerFactoryBean factoryBean =
103                 new LocalContainerEntityManagerFactoryBean();
104         factoryBean.setPackagesToScan("org.techroad.repositorydis.demo");
105         factoryBean.setDataSource(dataSource);
106         factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
107         factoryBean.setJpaProperties(hibernateProperties());
108         factoryBean.setPersistenceUnitName(unitName);
109         factoryBean.afterPropertiesSet();
110         return factoryBean.getObject();
111     }
112 }
View Code

ServiceConfig

 1 package org.techroad.repositorydis.demo.config;
 2 
 3 import org.springframework.orm.jpa.JpaSystemException;
 4 import org.springframework.stereotype.Repository;
 5 import org.springframework.stereotype.Service;
 6 import org.springframework.transaction.annotation.Transactional;
 7 
 8 import javax.persistence.EntityManager;
 9 import javax.persistence.PersistenceContext;
10 import javax.persistence.PersistenceException;
11 import java.util.List;
12 
13 /**
14  * responsibility :
15  *
16  * @author : cj
17  * @date : 2021/3/14
18  */
19 
20 @Service("singerService")
21 @Repository
22 @Transactional
23 public class SingerServiceImpl implements SingerService {
24 
25     @PersistenceContext(unitName = "emfA")
26     private EntityManager emfA;
27 
28     @PersistenceContext(unitName = "emfB")
29     private EntityManager emfB;
30 
31     @Override
32     public Singer save(Singer singer) {
33         Singer singerB = new Singer();
34         singerB.setFirstName(singer.getFirstName());
35         singerB.setLastName(singer.getLastName());
36         if ((Object) singer.getId() == null) {
37             emfA.persist(singer);
38             emfB.persist(singerB);
39             // throw new JpaSystemException
40             // (new PersistenceException());
41         } else {
42             emfA.merge(singer);
43             emfB.merge(singerB);
44         }
45         return singer;
46     }
47 
48     private static final String FIND_ALL = "select s from Singer s";
49 
50     @Override
51     @Transactional(readOnly = true)
52     public List<Singer> findAll() {
53         List<Singer> singersFromA = findAllInA();
54         List<Singer> singersFromB = findAllInB();
55         if (singersFromA.size() != singersFromB.size()) {
56             //todo check ex
57 /*            throw new AsyncXAResourcesException("" +
58                     "XA resources do not contain the same exception data.");*/
59             throw new JpaSystemException(
60                     new PersistenceException("XA resources do not contain the same exception data."));
61         }
62 
63         Singer sA = singersFromA.get(0);
64         Singer sB = singersFromB.get(0);
65         if (!sA.getFirstName().equals(sB.getFirstName())) {
66 /*            throw new AsyncXAResourcesException("" +
67                     "XA resources do not contain the same exception data.")*/
68             throw new JpaSystemException(
69                     new PersistenceException("XA resources do not contain the same exception data."));
70         }
71         return List.of(sA, sB);
72     }
73 
74     private List<Singer> findAllInA() {
75         return emfA.createQuery(FIND_ALL).getResultList();
76     }
77 
78     private List<Singer> findAllInB() {
79         return emfB.createQuery(FIND_ALL).getResultList();
80     }
81 }
View Code

SingerService

 1 package org.techroad.repositorydis.demo.config;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * responsibility :
 7  *
 8  * @author : cj
 9  * @date : 2021/3/14
10  */
11 
12 public interface SingerService {
13     Singer save(Singer singer);
14     List<Singer> findAll();
15 }
View Code

Singer

  1 package org.techroad.repositorydis.demo.config;
  2 
  3 import javax.persistence.*;
  4 import java.io.Serializable;
  5 import java.util.Date;
  6 import java.util.HashSet;
  7 import java.util.Set;
  8 
  9 import static javax.persistence.GenerationType.IDENTITY;
 10 
 11 /**
 12  * responsibility :
 13  *
 14  * @author : cj
 15  * @date : 2021/3/14
 16  */
 17 
 18 @Entity
 19 @Table(name = "singer")
 20 public class Singer implements Serializable {
 21     @Id
 22     @GeneratedValue(strategy = IDENTITY)
 23     @Column(name = "ID")
 24     public long id;
 25 
 26     @Version
 27     @Column(name = "VERSION")
 28     public int version;
 29 
 30     @Temporal(TemporalType.DATE)
 31     @Column(name = "BIRTH_DATE")
 32     public Date birthDate;
 33 
 34     @Column(name = "FIRST_NAME")
 35     public String firstName;
 36 
 37     @Column(name = "LAST_NAME")
 38     public String lastName;
 39 
 40     @OneToMany(mappedBy = "singer", cascade = CascadeType.ALL, orphanRemoval = true)
 41     private Set<Album> albums = new HashSet<>();
 42 
 43     @ManyToMany
 44     @JoinTable(name = "singer_instrument",
 45             joinColumns = @JoinColumn(name = "SINGER_ID"),
 46             inverseJoinColumns = @JoinColumn(name = "INSTRUMENT_ID"))
 47     private Set<Instrument> instruments = new HashSet<>();
 48 
 49     public Set<Album> getAlbums() {
 50         return albums;
 51     }
 52 
 53     public void setAlbums(Set<Album> albums) {
 54         this.albums = albums;
 55     }
 56 
 57     public boolean addAlbum(Album album){
 58         album.setSinger(this);
 59         return getAlbums().add(album);
 60     }
 61 
 62     public void deleteAlbum(Album album){
 63         getAlbums().remove(album);
 64     }
 65 
 66     public Set<Instrument> getInstruments() {
 67         return instruments;
 68     }
 69 
 70     public void setInstruments(Set<Instrument> instruments) {
 71         this.instruments = instruments;
 72     }
 73 
 74     public long getId() {
 75         return id;
 76     }
 77 
 78     public int getVersion() {
 79         return version;
 80     }
 81 
 82     public Date getBirthDate() {
 83         return birthDate;
 84     }
 85 
 86     public String getFirstName() {
 87         return firstName;
 88     }
 89 
 90     public String getLastName() {
 91         return lastName;
 92     }
 93 
 94     public void setBirthDate(Date birthDate) {
 95         this.birthDate = birthDate;
 96     }
 97 
 98     public void setFirstName(String firstName) {
 99         this.firstName = firstName;
100     }
101 
102     public void setLastName(String lastName) {
103         this.lastName = lastName;
104     }
105 
106     @Override
107     public String toString() {
108         return "Singer{" +
109                 "id=" + id +
110                 ", version=" + version +
111                 ", birthDate=" + birthDate +
112                 ", firstName='" + firstName + '\'' +
113                 ", lastName='" + lastName + '\'' +
114                 '}';
115     }
116 
117     public void removeAlbum(Album album) {
118         getAlbums().remove(album);
119     }
120 }
View Code

Album

 1 package org.techroad.repositorydis.demo.config;
 2 
 3 import javax.persistence.*;
 4 import java.io.Serializable;
 5 import java.util.Date;
 6 
 7 /**
 8  * responsibility :
 9  *
10  * @author : cj
11  * @date : 2021/3/14
12  */
13 
14 @Entity
15 @Table(name="album")
16 public class Album implements Serializable {
17     public Album(){}
18 
19     @Id
20     @GeneratedValue(strategy= GenerationType.IDENTITY)
21     @Column(name="ID")
22     private long id;
23 
24     @Version
25     @Column(name="version")
26     private int version;
27 
28     @Column(name="title")
29     private String title;
30 
31     @Temporal(TemporalType.DATE)
32     @Column(name="RELEASE_DATE")
33     private Date releaseDate;
34 
35     @ManyToOne
36     @JoinColumn(name="SINGER_ID")
37     private Singer singer;
38 
39     public Album(String title,Date releaseDate){
40         this.title = title;
41         this.releaseDate = releaseDate;
42     }
43 
44     public long getId() {
45         return id;
46     }
47 
48     public void setId(long id) {
49         this.id = id;
50     }
51 
52     public int getVersion() {
53         return version;
54     }
55 
56     public void setVersion(int version) {
57         this.version = version;
58     }
59 
60     public String getTitle() {
61         return title;
62     }
63 
64     public void setTitle(String title) {
65         this.title = title;
66     }
67 
68     public Date getReleaseDate() {
69         return releaseDate;
70     }
71 
72     public void setReleaseDate(Date releaseDate) {
73         this.releaseDate = releaseDate;
74     }
75 
76     public Singer getSinger() {
77         return singer;
78     }
79 
80     public void setSinger(Singer singer) {
81         this.singer = singer;
82     }
83 
84     @Override
85     public String toString() {
86         return "Album{" +
87                 "id=" + id +
88                 ", version=" + version +
89                 ", title='" + title + '\'' +
90                 ", releaseDate=" + releaseDate +
91                 '}';
92     }
93 }
View Code

Instrument

 1 package org.techroad.repositorydis.demo.config;
 2 
 3 import javax.persistence.*;
 4 import java.io.Serializable;
 5 import java.util.HashSet;
 6 import java.util.Set;
 7 
 8 /**
 9  * responsibility :
10  *
11  * @author : cj
12  * @date : 2021/3/14
13  */
14 
15 @Entity
16 @Table(name="instrument")
17 public class Instrument implements Serializable {
18     @Id
19     @GeneratedValue(strategy = GenerationType.IDENTITY)
20     @Column(name="INSTRUMENT_ID")
21     private String instrumentId;
22 
23     @ManyToMany
24     @JoinTable(name="singer_instrument",
25             joinColumns = @JoinColumn(name="INSTRUMENT_ID"),
26             inverseJoinColumns = @JoinColumn(name="SINGER_ID"))
27     private Set<Singer> singers = new HashSet<>();
28 
29     public String getInstrumentId() {
30         return instrumentId;
31     }
32 
33     public Set<Singer> getSingers() {
34         return singers;
35     }
36 
37     public void setInstrumentId(String instrumentId) {
38         this.instrumentId = instrumentId;
39     }
40 
41     public void setSingers(Set<Singer> singers) {
42         this.singers = singers;
43     }
44 
45     @Override
46     public String toString() {
47         return "Instrument{" +
48                 "instrumentId='" + instrumentId + '\'' +
49                 '}';
50     }
51 }
View Code

DempApplication

 1 package org.techroad.repositorydis.demo;
 2 
 3 import org.slf4j.Logger;
 4 import org.slf4j.LoggerFactory;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.boot.ApplicationArguments;
 7 import org.springframework.boot.ApplicationRunner;
 8 import org.springframework.boot.SpringApplication;
 9 import org.springframework.boot.autoconfigure.SpringBootApplication;
10 import org.springframework.stereotype.Component;
11 import org.techroad.repositorydis.demo.config.Singer;
12 import org.techroad.repositorydis.demo.config.SingerService;
13 
14 import java.util.Date;
15 import java.util.GregorianCalendar;
16 import java.util.List;
17 
18 @SpringBootApplication
19 public class DemoApplication {
20 
21     private static Logger logger = LoggerFactory.getLogger(DemoApplication.class);
22 
23     public static void main(String[] args) {
24         SpringApplication.run(DemoApplication.class, args);
25     }
26 
27     @Component
28     class TxJtaDemo implements ApplicationRunner{
29 
30         @Autowired
31         SingerService singerService;
32 
33         @Override
34         public void run(ApplicationArguments args) throws Exception {
35             Singer singer = new Singer();
36             singer.setFirstName("Lola");
37             singer.setLastName("prince");
38             singer.setBirthDate(new Date(new GregorianCalendar(1990,2,5)
39             .getTime().getTime()));
40             singerService.save(singer);
41             if((Object)singer.getId() != null){
42                 logger.info("------->  Singer saved successfully");
43             }else {
44                 logger.error("------->  Singer was not saved, check the config!");
45             }
46 
47             List<Singer> bothSideSingers = singerService.findAll();
48             if(bothSideSingers.size() != 6){
49                 logger.error("-------> something went wrong.");
50             }else {
51                 logger.info("------->  singers from both DBs: " + bothSideSingers);
52             }
53         }
54     }
55 
56 }
View Code

 

posted @ 2021-03-14 10:55  君子之行  阅读(24)  评论(0)    收藏  举报