Skip to content

Commit fec4f2a

Browse files
committed
Upgrade to Java 21 and Jakarta EE 10.0
1 parent c031148 commit fec4f2a

9 files changed

Lines changed: 531 additions & 8 deletions

File tree

framework-libraries-bom/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@
152152
<artifactId>test-utils-core</artifactId>
153153
<version>${framework-libraries.version}</version>
154154
</dependency>
155+
<dependency>
156+
<groupId>uk.gov.justice.utils</groupId>
157+
<artifactId>test-utils-hibernate</artifactId>
158+
<version>${framework-libraries.version}</version>
159+
</dependency>
155160
<dependency>
156161
<groupId>uk.gov.justice.utils</groupId>
157162
<artifactId>test-utils-framework-api</artifactId>

framework-utilities/test-utils/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<modules>
1818
<module>test-utils-core</module>
1919
<module>test-utils-framework-api</module>
20+
<module>test-utils-hibernate</module>
2021
<module>test-utils-logging-jdk</module>
2122
<module>test-utils-logging-log4j</module>
2223
<module>test-utils-logging-simple</module>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>uk.gov.justice.utils</groupId>
9+
<artifactId>test-utils</artifactId>
10+
<version>21.0.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>test-utils-hibernate</artifactId>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>uk.gov.justice.utils</groupId>
18+
<artifactId>test-utils-core</artifactId>
19+
<version>${project.version}</version>
20+
</dependency>
21+
22+
<dependency>
23+
<groupId>jakarta.persistence</groupId>
24+
<artifactId>jakarta.persistence-api</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.hibernate.orm</groupId>
28+
<artifactId>hibernate-core</artifactId>
29+
</dependency>
30+
31+
<!-- Test dependencies -->
32+
<dependency>
33+
<groupId>org.junit.jupiter</groupId>
34+
<artifactId>junit-jupiter-api</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.mockito</groupId>
38+
<artifactId>mockito-junit-jupiter</artifactId>
39+
<scope>test</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.hamcrest</groupId>
43+
<artifactId>hamcrest</artifactId>
44+
<scope>test</scope>
45+
</dependency>
46+
<dependency>
47+
<groupId>com.h2database</groupId>
48+
<artifactId>h2</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
<dependency>
52+
<groupId>com.fasterxml.jackson.core</groupId>
53+
<artifactId>jackson-databind</artifactId>
54+
<scope>test</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.slf4j</groupId>
58+
<artifactId>slf4j-simple</artifactId>
59+
<scope>test</scope>
60+
</dependency>
61+
</dependencies>
62+
63+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package uk.gov.justice.services.test.utils.persistence;
2+
3+
import static java.lang.String.format;
4+
import static uk.gov.justice.services.test.utils.core.reflection.ReflectionUtil.setField;
5+
6+
import uk.gov.justice.services.test.utils.core.reflection.ReflectionException;
7+
8+
import jakarta.persistence.EntityManager;
9+
import jakarta.persistence.EntityManagerFactory;
10+
import jakarta.persistence.Persistence;
11+
12+
import org.junit.jupiter.api.extension.AfterAllCallback;
13+
import org.junit.jupiter.api.extension.AfterEachCallback;
14+
import org.junit.jupiter.api.extension.BeforeEachCallback;
15+
import org.junit.jupiter.api.extension.ExtensionContext;
16+
17+
/**
18+
* Test utility that bootstraps a standalone Hibernate {@link EntityManager} from a named
19+
* persistence unit. Intended to be used in unit/integration tests for repository classes
20+
* that inject an {@link EntityManager} via {@code @PersistenceContext}.
21+
*
22+
* <p>Typical usage pattern in a JUnit 5 test:
23+
* <pre>
24+
* private final HibernateTestEntityManagerProvider provider =
25+
* new HibernateTestEntityManagerProvider("my-persistence-unit");
26+
*
27+
* {@literal @}BeforeEach
28+
* void setUp() {
29+
* provider.openEntityManager();
30+
* provider.injectEntityManagerInto(myRepository);
31+
* }
32+
*
33+
* {@literal @}AfterEach
34+
* void tearDown() {
35+
* provider.closeEntityManager();
36+
* }
37+
*
38+
* {@literal @}AfterAll
39+
* static void tearDownFactory() {
40+
* provider.closeEntityManagerFactory();
41+
* }
42+
* </pre>
43+
*
44+
* <p>Each call to {@link #openEntityManager()} begins a new transaction. {@link
45+
* #closeEntityManager()} rolls back any active transaction and closes the manager, leaving
46+
* the database clean for the next test.
47+
*/
48+
public class HibernateTestEntityManagerProvider implements BeforeEachCallback, AfterEachCallback, AfterAllCallback {
49+
50+
private final EntityManagerFactory entityManagerFactory;
51+
private EntityManager entityManager;
52+
53+
/**
54+
* Creates the provider and immediately builds the {@link EntityManagerFactory} for the
55+
* named persistence unit. The factory is expensive to create, so a single instance
56+
* should be shared across all tests in a class (e.g. stored in a {@code static} field
57+
* and initialised once with {@code @BeforeAll}).
58+
*
59+
* @param persistenceUnitName the name of the persistence unit as declared in
60+
* {@code META-INF/persistence.xml}
61+
*/
62+
public HibernateTestEntityManagerProvider(final String persistenceUnitName) {
63+
entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
64+
}
65+
66+
@Override
67+
public void beforeEach(final ExtensionContext context) {
68+
openEntityManager();
69+
}
70+
71+
@Override
72+
public void afterEach(final ExtensionContext context) {
73+
closeEntityManager();
74+
}
75+
76+
@Override
77+
public void afterAll(final ExtensionContext context) {
78+
closeEntityManagerFactory();
79+
}
80+
81+
82+
/**
83+
* Opens a new {@link EntityManager} and begins a transaction. Must be called before
84+
* any repository interaction in a test.
85+
*/
86+
public void openEntityManager() {
87+
entityManager = entityManagerFactory.createEntityManager();
88+
entityManager.getTransaction().begin();
89+
}
90+
91+
/**
92+
* Rolls back the active transaction (if any) and closes the {@link EntityManager}.
93+
* Call this in {@code @AfterEach} so every test starts with a clean database state.
94+
*/
95+
public void closeEntityManager() {
96+
if (entityManager != null) {
97+
if (entityManager.getTransaction().isActive()) {
98+
entityManager.getTransaction().rollback();
99+
}
100+
if (entityManager.isOpen()) {
101+
entityManager.close();
102+
}
103+
}
104+
}
105+
106+
/**
107+
* Closes the underlying {@link EntityManagerFactory}. Call this in {@code @AfterAll}
108+
* once all tests in the class have finished.
109+
*/
110+
public void closeEntityManagerFactory() {
111+
if (entityManagerFactory != null && entityManagerFactory.isOpen()) {
112+
entityManagerFactory.close();
113+
}
114+
}
115+
116+
/**
117+
* Returns the current {@link EntityManager}. Valid only between calls to
118+
* {@link #openEntityManager()} and {@link #closeEntityManager()}.
119+
*
120+
* @return the active {@link EntityManager}
121+
*/
122+
public EntityManager getEntityManager() {
123+
return entityManager;
124+
}
125+
126+
/**
127+
* Injects the current {@link EntityManager} into the field named {@code entityManager}
128+
* on the supplied repository object. Equivalent to calling
129+
* {@link #injectEntityManagerInto(Object, String)} with {@code "entityManager"}.
130+
*
131+
* @param repository the repository instance to inject into
132+
* @throws NoEntityManagerFieldFoundException if the field cannot be found
133+
*/
134+
public void injectEntityManagerInto(final Object repository) {
135+
injectEntityManagerInto(repository, "entityManager");
136+
}
137+
138+
/**
139+
* Injects the current {@link EntityManager} into the named field of the supplied
140+
* repository object. Uses reflection so that no special interface or constructor is
141+
* required on the repository.
142+
*
143+
* @param repository the repository instance to inject into
144+
* @param entityManagerFieldName the name of the field that holds the {@link EntityManager}
145+
* @throws NoEntityManagerFieldFoundException if the field cannot be found
146+
*/
147+
public void injectEntityManagerInto(final Object repository, final String entityManagerFieldName) {
148+
try {
149+
setField(repository, entityManagerFieldName, entityManager);
150+
} catch (final ReflectionException e) {
151+
throw new NoEntityManagerFieldFoundException(
152+
format("Failed to inject Hibernate EntityManager into %s. No EntityManager field found with the name '%s'",
153+
repository.getClass().getName(), entityManagerFieldName));
154+
}
155+
}
156+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package uk.gov.justice.services.test.utils.persistence;
2+
3+
public class NoEntityManagerFieldFoundException extends RuntimeException {
4+
5+
public NoEntityManagerFieldFoundException(final String message) {
6+
super(message);
7+
}
8+
}

0 commit comments

Comments
 (0)