Skip to content

Commit 6daed51

Browse files
committed
Add Hibernate StatelessSession support
Closes: quarkusio#7148
1 parent 1b8fc61 commit 6daed51

File tree

11 files changed

+1207
-0
lines changed

11 files changed

+1207
-0
lines changed

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ClassNames.java

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private static DotName createConstant(String fqcn) {
5151
public static final DotName SESSION_FACTORY = createConstant("org.hibernate.SessionFactory");
5252
public static final DotName ENTITY_MANAGER = createConstant("jakarta.persistence.EntityManager");
5353
public static final DotName SESSION = createConstant("org.hibernate.Session");
54+
public static final DotName STATELESS_SESSION = createConstant("org.hibernate.StatelessSession");
5455

5556
public static final DotName INTERCEPTOR = createConstant("org.hibernate.Interceptor");
5657
public static final DotName STATEMENT_INSPECTOR = createConstant("org.hibernate.resource.jdbc.spi.StatementInspector");

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java

+20
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.hibernate.Session;
1313
import org.hibernate.SessionFactory;
14+
import org.hibernate.StatelessSession;
1415
import org.jboss.jandex.AnnotationTarget.Kind;
1516
import org.jboss.jandex.AnnotationValue;
1617
import org.jboss.jandex.ClassType;
@@ -47,6 +48,7 @@ public class HibernateOrmCdiProcessor {
4748
private static final List<DotName> SESSION_FACTORY_EXPOSED_TYPES = Arrays.asList(ClassNames.ENTITY_MANAGER_FACTORY,
4849
ClassNames.SESSION_FACTORY);
4950
private static final List<DotName> SESSION_EXPOSED_TYPES = Arrays.asList(ClassNames.ENTITY_MANAGER, ClassNames.SESSION);
51+
private static final List<DotName> STATELESS_SESSION_EXPOSED_TYPES = List.of(ClassNames.STATELESS_SESSION);
5052

5153
private static final Set<DotName> PERSISTENCE_UNIT_EXTENSION_VALID_TYPES = Set.of(
5254
ClassNames.TENANT_RESOLVER,
@@ -148,6 +150,15 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder,
148150
.createWith(recorder.sessionSupplier(persistenceUnitName))
149151
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
150152
.done());
153+
154+
// same for StatelessSession
155+
syntheticBeanBuildItemBuildProducer
156+
.produce(createSyntheticBean(persistenceUnitName,
157+
true, true,
158+
StatelessSession.class, STATELESS_SESSION_EXPOSED_TYPES, false)
159+
.createWith(recorder.statelessSessionSupplier(persistenceUnitName))
160+
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
161+
.done());
151162
}
152163
return;
153164
}
@@ -179,6 +190,15 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder,
179190
.createWith(recorder.sessionSupplier(persistenceUnitName))
180191
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
181192
.done());
193+
194+
// same for StatelessSession
195+
syntheticBeanBuildItemBuildProducer
196+
.produce(createSyntheticBean(persistenceUnitName,
197+
true, true,
198+
StatelessSession.class, STATELESS_SESSION_EXPOSED_TYPES, false)
199+
.createWith(recorder.statelessSessionSupplier(persistenceUnitName))
200+
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
201+
.done());
182202
}
183203
}
184204
}

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java

+2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
import io.quarkus.hibernate.orm.runtime.JPAConfig;
141141
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
142142
import io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder;
143+
import io.quarkus.hibernate.orm.runtime.RequestScopedStatelessSessionHolder;
143144
import io.quarkus.hibernate.orm.runtime.TransactionSessions;
144145
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition;
145146
import io.quarkus.hibernate.orm.runtime.boot.scan.QuarkusScanner;
@@ -674,6 +675,7 @@ void registerBeans(HibernateOrmConfig hibernateOrmConfig,
674675
unremovableClasses.add(TransactionSessions.class);
675676
}
676677
unremovableClasses.add(RequestScopedSessionHolder.class);
678+
unremovableClasses.add(RequestScopedStatelessSessionHolder.class);
677679
unremovableClasses.add(QuarkusArcBeanContainer.class);
678680

679681
additionalBeans.produce(AdditionalBeanBuildItem.builder().setUnremovable()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.quarkus.hibernate.orm.stateless;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import jakarta.inject.Inject;
6+
7+
import org.hibernate.StatelessSession;
8+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
9+
import org.junit.jupiter.api.AfterEach;
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.extension.RegisterExtension;
13+
14+
import io.quarkus.arc.Arc;
15+
import io.quarkus.hibernate.orm.MyEntity;
16+
import io.quarkus.hibernate.orm.naming.PrefixPhysicalNamingStrategy;
17+
import io.quarkus.test.QuarkusUnitTest;
18+
19+
public class StatelessSessionWithinRequestScopeTest {
20+
21+
@RegisterExtension
22+
static QuarkusUnitTest runner = new QuarkusUnitTest()
23+
.withApplicationRoot((jar) -> jar
24+
.addClasses(MyEntity.class, PrefixPhysicalNamingStrategy.class)
25+
.addAsResource(EmptyAsset.INSTANCE, "import.sql")
26+
.addAsResource("application-physical-naming-strategy.properties", "application.properties"));
27+
28+
@Inject
29+
StatelessSession statelessSession;
30+
31+
@BeforeEach
32+
public void activateRequestContext() {
33+
Arc.container().requestContext().activate();
34+
}
35+
36+
@Test
37+
public void test() throws Exception {
38+
Number result = (Number) statelessSession.createNativeQuery("SELECT COUNT(*) FROM TBL_MYENTITY").getSingleResult();
39+
assertEquals(0, result.intValue());
40+
}
41+
42+
@AfterEach
43+
public void terminateRequestContext() {
44+
Arc.container().requestContext().terminate();
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.quarkus.hibernate.orm.stateless;
2+
3+
import static org.assertj.core.api.Assertions.*;
4+
5+
import java.util.List;
6+
7+
import jakarta.inject.Inject;
8+
import jakarta.transaction.UserTransaction;
9+
10+
import org.hibernate.Session;
11+
import org.hibernate.StatelessSession;
12+
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.extension.RegisterExtension;
14+
15+
import io.quarkus.hibernate.orm.enhancer.Address;
16+
import io.quarkus.test.QuarkusUnitTest;
17+
18+
public class StatelessSessionWithinTransactionTest {
19+
20+
@RegisterExtension
21+
static QuarkusUnitTest runner = new QuarkusUnitTest()
22+
.withApplicationRoot((jar) -> jar
23+
.addClass(Address.class))
24+
.withConfigurationResource("application.properties");
25+
26+
@Inject
27+
StatelessSession statelessSession;
28+
29+
@Inject
30+
Session session;
31+
32+
@Inject
33+
UserTransaction transaction;
34+
35+
@Test
36+
public void test() throws Exception {
37+
transaction.begin();
38+
Address entity = new Address("high street");
39+
session.persist(entity);
40+
transaction.commit();
41+
42+
transaction.begin();
43+
List<Object> list = statelessSession.createNativeQuery("SELECT street from address").getResultList();
44+
assertThat(list).containsOnly("high street");
45+
transaction.commit();
46+
}
47+
}

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java

+18
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.eclipse.microprofile.config.ConfigProvider;
1515
import org.hibernate.Session;
1616
import org.hibernate.SessionFactory;
17+
import org.hibernate.StatelessSession;
1718
import org.hibernate.boot.archive.scan.spi.Scanner;
1819
import org.hibernate.engine.spi.SessionLazyDelegator;
1920
import org.hibernate.integrator.spi.Integrator;
@@ -126,6 +127,23 @@ public Session get() {
126127
};
127128
}
128129

130+
public Function<SyntheticCreationalContext<StatelessSession>, StatelessSession> statelessSessionSupplier(
131+
String persistenceUnitName) {
132+
return new Function<SyntheticCreationalContext<StatelessSession>, StatelessSession>() {
133+
134+
@Override
135+
public StatelessSession apply(SyntheticCreationalContext<StatelessSession> context) {
136+
TransactionSessions transactionSessions = context.getInjectedReference(TransactionSessions.class);
137+
return new StatelessSessionLazyDelegator(new Supplier<StatelessSession>() {
138+
@Override
139+
public StatelessSession get() {
140+
return transactionSessions.getStatelessSession(persistenceUnitName);
141+
}
142+
});
143+
}
144+
};
145+
}
146+
129147
public void doValidation(String puName) {
130148
Optional<String> val;
131149
if (puName.equals(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.quarkus.hibernate.orm.runtime;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import jakarta.annotation.PreDestroy;
7+
import jakarta.enterprise.context.RequestScoped;
8+
9+
import org.hibernate.SessionFactory;
10+
import org.hibernate.StatelessSession;
11+
12+
/**
13+
* Bean that is used to manage request scoped stateless sessions
14+
*/
15+
@RequestScoped
16+
public class RequestScopedStatelessSessionHolder {
17+
18+
private final Map<String, StatelessSession> sessions = new HashMap<>();
19+
20+
public StatelessSession getOrCreateSession(String name, SessionFactory factory) {
21+
return sessions.computeIfAbsent(name, (n) -> factory.openStatelessSession());
22+
}
23+
24+
@PreDestroy
25+
public void destroy() {
26+
for (Map.Entry<String, StatelessSession> entry : sessions.entrySet()) {
27+
entry.getValue().close();
28+
}
29+
}
30+
31+
}

0 commit comments

Comments
 (0)