├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── example
│ │ ├── data
│ │ ├── ModelRepository.java
│ │ ├── catalog
│ │ │ └── ProductRepository.java
│ │ └── directory
│ │ │ └── PersonRepository.java
│ │ └── domain
│ │ ├── Model.java
│ │ ├── catalog
│ │ └── Product.java
│ │ └── directory
│ │ └── Person.java
└── resources
│ ├── logback.xml
│ └── springContext.xml
└── test
└── java
└── org
└── example
└── Test.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .gradle/
4 | .idea/
5 | .settings/
6 | build/
7 | target/
8 | *~
9 | *.db
10 | *.iml
11 | *.lck
12 | *.log
13 | *.tlog
14 | *.tmp
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 1. Overview [](https://coveralls.io/github/manish-in-java/spring-data-jta?branch=master)
2 | This sample application demonstrates the use of an external [JTA](http://en.wikipedia.org/wiki/Java_Transaction_API)
3 | transaction manager in a Spring Data JPA application. It uses two database schemas,
4 | saving data to and reading from these schemas, as needed to demonstrate and test the
5 | use of JTA transactions.
6 |
7 | The steps below outline how the JTA transaction manager is configured:
8 |
9 | 1. One or more XA-compliant data source that will manage the underlying data store.
10 | Most libraries provide XA-compliant versions of their drivers. For example, the class
11 | `org.h2.Driver` is the regular (non-XA) JDBC driver for the H2 in-memory database.
12 | The XA-compliant data source for the same database is provided by the class
13 | `org.h2.jdbcx.JdbcDataSource`. Similarly, where `com.mysql.jdbc.Driver` is the regular
14 | JDBC driver class for the MySQL database, `com.mysql.jdbc.jdbc2.optional.MysqlXADataSource`
15 | provides the XA-compliant data source.
16 | 1. Point the `EntityManagerFactory` instance to the XA `DataSource` instance.
17 | This makes sure that the database connections participate in JTA transactions.
18 | 1. Declare an XA transaction manager and an XA user transaction.
19 | 1. Wrap the XA transaction manager in a Spring `JtaTransactionManager`.
20 | 1. Use the Spring `JtaTransactionManager`.
21 |
22 | # 2. JTA providers
23 |
24 | ## 2.1. Atomikos Transaction Essentials
25 |
26 | [Atomikos TransactionEssentials](http://www.atomikos.com/Main/TransactionsEssentials) is
27 | a JTA transaction manager that comes in both open-source and commercial flavours. The
28 | open-source version of Atomikos is included with this application. To see Atomikos in action,
29 | run the bundled integration test as `mvn clean test -D"spring.profiles.active=atomikos"`.
30 |
31 | The open-source version of Atomikos used for this application is available under the
32 | Apache License v2.0. Full licensing details are available on the
33 | [Atomikos website](https://www.atomikos.com/Main/WhichLicenseApplies).
34 |
35 | ## 2.2. Bitronix Transaction Manager
36 |
37 | [Bitronix Transaction Manager](https://github.com/bitronix/btm) is a fully open-source
38 | JTA transaction manager. Run the bundled integration tests as
39 | `mvn clean test -D"spring.profiles.active=bitronix"` to see Bitronix in action.
40 |
41 | The Bitronix version used for this application is available under the
42 | GNU Lesser General Public License v3.0. Full licensing details are available
43 | on Bitronix's [Github repository](https://github.com/bitronix/btm).
44 |
45 | ## 2.3. JBoss Transaction Server
46 |
47 | *JBoss Transaction Server (JBossTS)* was an open-source JTA transaction manager that
48 | used to ship as part of the [JBoss J2EE Application Server](http://jbossas.jboss.org).
49 | Run the bundled integration tests as
50 | `mvn clean test -D"spring.profiles.active=jbossts"` to see JBossTS in action.
51 |
52 | The JBossTS version used for this application is available under the
53 | GNU Lesser General Public License v2.1.
54 |
55 | # 3. License
56 | This sample application and its associated source code in its entirety is being made
57 | available under the following licensing terms.
58 |
59 | Permission is hereby granted, free of charge, to any person obtaining a copy of
60 | this software and associated documentation files (the "Software"), to deal in the
61 | Software without restriction, including without limitation the rights to use, copy,
62 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
63 | and to permit persons to whom the Software is furnished to do so, subject to the
64 | following conditions:
65 |
66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
67 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
68 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
69 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
70 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
71 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
72 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | org.example
6 | spring-data-jta
7 | 1.0
8 | jar
9 | Spring Data JTA
10 | A sample application that demonstrates the use of an external JTA transaction manager with Spring Data JPA.
11 |
12 |
13 |
14 |
15 | org.apache.maven.plugins
16 | maven-compiler-plugin
17 | 3.1
18 |
19 | -Xlint:none
20 | ${java.version}
21 | ${java.version}
22 |
23 |
24 |
25 | org.codehaus.mojo
26 | cobertura-maven-plugin
27 | 2.7
28 |
29 | true
30 |
31 | true
32 |
33 |
34 | html
35 | xml
36 |
37 |
38 |
39 |
40 | org.eluder.coveralls
41 | coveralls-maven-plugin
42 | 3.1.0
43 |
44 |
45 |
46 |
47 |
48 | src/main/resources
49 | true
50 |
51 | **/*.properties
52 | **/*.xml
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | ch.qos.logback
61 | logback-classic
62 | ${logback.version}
63 |
64 |
65 | org.slf4j
66 | slf4j-api
67 |
68 |
69 |
70 |
71 |
72 | com.atomikos
73 | transactions-hibernate4
74 | ${atomikos.version}
75 | runtime
76 |
77 |
78 | com.atomikos
79 | transactions-jdbc
80 | ${atomikos.version}
81 | runtime
82 |
83 |
84 | com.atomikos
85 | transactions-jta
86 | ${atomikos.version}
87 | runtime
88 |
89 |
90 |
91 | org.codehaus.btm
92 | btm
93 | ${btm.version}
94 | runtime
95 |
96 |
97 |
98 | com.h2database
99 | h2
100 | ${h2.version}
101 | runtime
102 |
103 |
104 |
105 | junit
106 | junit
107 | ${junit.version}
108 | test
109 |
110 |
111 |
112 | org.hibernate
113 | hibernate-entitymanager
114 | ${hibernate.version}
115 |
116 |
117 | org.hibernate
118 | hibernate-validator
119 | ${hibernate.validator.version}
120 |
121 |
122 |
123 | org.jboss.jbossts
124 | jbossjta
125 | ${jbossts.version}
126 |
127 |
128 |
129 | org.postgresql
130 | postgresql
131 | ${postgresql.version}
132 | runtime
133 |
134 |
135 |
136 | org.springframework
137 | spring-context
138 | ${spring.version}
139 | test
140 |
141 |
142 | org.springframework
143 | spring-orm
144 | ${spring.version}
145 | test
146 |
147 |
148 | org.springframework
149 | spring-test
150 | ${spring.version}
151 | test
152 |
153 |
154 | org.springframework
155 | spring-tx
156 | ${spring.version}
157 |
158 |
159 |
160 | org.springframework.data
161 | spring-data-jpa
162 | ${spring.data.jpa.version}
163 |
164 |
165 |
166 |
167 | org.h2.jdbcx.JdbcDataSource
168 | true
169 | true
170 | true
171 | false
172 | false
173 | false
174 | jdbc:h2:mem:catalog;DB_CLOSE_DELAY=-1
175 | jdbc:h2:mem:directory;DB_CLOSE_DELAY=-1
176 | sa
177 |
178 | 1.8
179 | UTF-8
180 |
181 | 4.0.4
182 | 2.1.4
183 | 1.4.192
184 | 5.2.4.Final
185 | 5.2.2.Final
186 | 4.16.6.Final
187 | 4.12
188 | 1.1.7
189 | 9.4-1201-jdbc41
190 | 4.3.20.RELEASE
191 | 1.10.3.RELEASE
192 |
193 |
194 |
--------------------------------------------------------------------------------
/src/main/java/org/example/data/ModelRepository.java:
--------------------------------------------------------------------------------
1 | package org.example.data;
2 |
3 | import org.example.domain.Model;
4 | import org.springframework.data.jpa.repository.JpaRepository;
5 | import org.springframework.data.repository.NoRepositoryBean;
6 |
7 | /**
8 | * Contract for data access operations on an {@link Model}.
9 | */
10 | @NoRepositoryBean
11 | public interface ModelRepository extends JpaRepository
12 | {
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/org/example/data/catalog/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package org.example.data.catalog;
2 |
3 | import org.example.data.ModelRepository;
4 | import org.example.domain.catalog.Product;
5 |
6 | /**
7 | * Contract for data access operations on an {@link Product}.
8 | */
9 | public interface ProductRepository extends ModelRepository
10 | {
11 | /**
12 | * Finds a product by its name.
13 | *
14 | * @param name The product name to find.
15 | * @return A {@link Product} if {@code name}
16 | * is found, {@code null} otherwise.
17 | */
18 | Product findByName(String name);
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/org/example/data/directory/PersonRepository.java:
--------------------------------------------------------------------------------
1 | package org.example.data.directory;
2 |
3 | import org.example.data.ModelRepository;
4 | import org.example.domain.directory.Person;
5 |
6 | /**
7 | * Contract for data access operations on an {@link Person}.
8 | */
9 | public interface PersonRepository extends ModelRepository
10 | {
11 | /**
12 | * Finds a user by its name.
13 | *
14 | * @param name The user name to find.
15 | * @return A {@link Person} if {@code name}
16 | * is found, {@code null} otherwise.
17 | */
18 | Person findByName(String name);
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/org/example/domain/Model.java:
--------------------------------------------------------------------------------
1 | package org.example.domain;
2 |
3 | import org.hibernate.annotations.Generated;
4 | import org.hibernate.annotations.GenerationTime;
5 |
6 | import javax.persistence.GeneratedValue;
7 | import javax.persistence.GenerationType;
8 | import javax.persistence.Id;
9 | import javax.persistence.MappedSuperclass;
10 | import java.io.Serializable;
11 |
12 | /**
13 | * Represents a domain entity.
14 | */
15 | @MappedSuperclass
16 | public abstract class Model implements Serializable
17 | {
18 | @Generated(GenerationTime.INSERT)
19 | @GeneratedValue(strategy = GenerationType.AUTO)
20 | @Id
21 | private Long id;
22 |
23 | /**
24 | * Gets the unique identifier for this entity instance.
25 | *
26 | * @return The unique identifier for this entity instance.
27 | */
28 | public Long getID()
29 | {
30 | return this.id;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/org/example/domain/catalog/Product.java:
--------------------------------------------------------------------------------
1 | package org.example.domain.catalog;
2 |
3 | import org.example.domain.Model;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Entity;
7 | import javax.persistence.Table;
8 | import javax.validation.constraints.NotNull;
9 |
10 | /**
11 | * Represents a product.
12 | */
13 | @Entity
14 | @Table(name = "product")
15 | public class Product extends Model
16 | {
17 | @Column(name = "name")
18 | @NotNull
19 | private String name;
20 |
21 | /**
22 | * Gets the product name.
23 | *
24 | * @return The product name.
25 | */
26 | public String getName()
27 | {
28 | return name;
29 | }
30 |
31 | /**
32 | * Sets the product name.
33 | *
34 | * @param name The product name.
35 | */
36 | public void setName(final String name)
37 | {
38 | this.name = name;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/example/domain/directory/Person.java:
--------------------------------------------------------------------------------
1 | package org.example.domain.directory;
2 |
3 | import org.example.domain.Model;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Entity;
7 | import javax.persistence.Table;
8 | import javax.validation.constraints.NotNull;
9 |
10 | /**
11 | * Represents a person.
12 | */
13 | @Entity
14 | @Table(name = "person")
15 | public class Person extends Model
16 | {
17 | @Column(name = "name")
18 | @NotNull
19 | private String name;
20 |
21 | /**
22 | * Gets the person's ame.
23 | *
24 | * @return The person's name.
25 | */
26 | public String getName()
27 | {
28 | return name;
29 | }
30 |
31 | /**
32 | * Sets the person's name.
33 | *
34 | * @param name The person's name.
35 | */
36 | public void setName(final String name)
37 | {
38 | this.name = name;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | [%6p] %m%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main/resources/springContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/src/test/java/org/example/Test.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import org.example.data.catalog.ProductRepository;
4 | import org.example.data.directory.PersonRepository;
5 | import org.example.domain.catalog.Product;
6 | import org.example.domain.directory.Person;
7 | import org.junit.Assert;
8 | import org.junit.runner.RunWith;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.test.annotation.Rollback;
11 | import org.springframework.test.context.ContextConfiguration;
12 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
13 | import org.springframework.transaction.annotation.Transactional;
14 |
15 | /**
16 | * Integration test for JTA transactions
17 | */
18 | @ContextConfiguration(locations = "classpath:springContext.xml")
19 | @Rollback
20 | @RunWith(SpringJUnit4ClassRunner.class)
21 | @Transactional
22 | public class Test
23 | {
24 | private static final String PERSON_NAME = "yoda";
25 | private static final String PRODUCT_NAME = "Light Sabre";
26 |
27 | @Autowired
28 | private PersonRepository personRepository;
29 | @Autowired
30 | private ProductRepository productRepository;
31 |
32 | /**
33 | * Tests a JTA transaction.
34 | */
35 | @org.junit.Test
36 | public void test()
37 | {
38 | final Person person = new Person();
39 | person.setName(PERSON_NAME);
40 |
41 | personRepository.save(person);
42 |
43 | final Product product = new Product();
44 | product.setName(PRODUCT_NAME);
45 |
46 | productRepository.save(product);
47 |
48 | final Person retrievedPerson = personRepository.findByName(PERSON_NAME);
49 | Assert.assertNotNull(retrievedPerson);
50 | Assert.assertNotNull(retrievedPerson.getID());
51 | Assert.assertEquals(PERSON_NAME, retrievedPerson.getName());
52 |
53 | final Product retrievedProduct = productRepository.findByName(PRODUCT_NAME);
54 | Assert.assertNotNull(retrievedProduct);
55 | Assert.assertNotNull(retrievedProduct.getID());
56 | Assert.assertEquals(PRODUCT_NAME, retrievedProduct.getName());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------