Skip to content

Commit

Permalink
Add Hibernate StatelessSession support
Browse files Browse the repository at this point in the history
Closes: #7148
  • Loading branch information
geoand committed Feb 24, 2023
1 parent 1b8fc61 commit 7c08056
Show file tree
Hide file tree
Showing 11 changed files with 1,207 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private static DotName createConstant(String fqcn) {
public static final DotName SESSION_FACTORY = createConstant("org.hibernate.SessionFactory");
public static final DotName ENTITY_MANAGER = createConstant("jakarta.persistence.EntityManager");
public static final DotName SESSION = createConstant("org.hibernate.Session");
public static final DotName STATELESS_SESSION = createConstant("org.hibernate.StatelessSession");

public static final DotName INTERCEPTOR = createConstant("org.hibernate.Interceptor");
public static final DotName STATEMENT_INSPECTOR = createConstant("org.hibernate.resource.jdbc.spi.StatementInspector");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassType;
Expand Down Expand Up @@ -47,6 +48,7 @@ public class HibernateOrmCdiProcessor {
private static final List<DotName> SESSION_FACTORY_EXPOSED_TYPES = Arrays.asList(ClassNames.ENTITY_MANAGER_FACTORY,
ClassNames.SESSION_FACTORY);
private static final List<DotName> SESSION_EXPOSED_TYPES = Arrays.asList(ClassNames.ENTITY_MANAGER, ClassNames.SESSION);
private static final List<DotName> STATELESS_SESSION_EXPOSED_TYPES = List.of(ClassNames.STATELESS_SESSION);

private static final Set<DotName> PERSISTENCE_UNIT_EXTENSION_VALID_TYPES = Set.of(
ClassNames.TENANT_RESOLVER,
Expand Down Expand Up @@ -148,6 +150,15 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder,
.createWith(recorder.sessionSupplier(persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
.done());

// same for StatelessSession
syntheticBeanBuildItemBuildProducer
.produce(createSyntheticBean(persistenceUnitName,
true, true,
StatelessSession.class, STATELESS_SESSION_EXPOSED_TYPES, false)
.createWith(recorder.statelessSessionSupplier(persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
.done());
}
return;
}
Expand Down Expand Up @@ -179,6 +190,15 @@ void generateDataSourceBeans(HibernateOrmRecorder recorder,
.createWith(recorder.sessionSupplier(persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
.done());

// same for StatelessSession
syntheticBeanBuildItemBuildProducer
.produce(createSyntheticBean(persistenceUnitName,
true, true,
StatelessSession.class, STATELESS_SESSION_EXPOSED_TYPES, false)
.createWith(recorder.statelessSessionSupplier(persistenceUnitName))
.addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSessions.class)))
.done());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
import io.quarkus.hibernate.orm.runtime.JPAConfig;
import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil;
import io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder;
import io.quarkus.hibernate.orm.runtime.RequestScopedStatelessSessionHolder;
import io.quarkus.hibernate.orm.runtime.TransactionSessions;
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition;
import io.quarkus.hibernate.orm.runtime.boot.scan.QuarkusScanner;
Expand Down Expand Up @@ -674,6 +675,7 @@ void registerBeans(HibernateOrmConfig hibernateOrmConfig,
unremovableClasses.add(TransactionSessions.class);
}
unremovableClasses.add(RequestScopedSessionHolder.class);
unremovableClasses.add(RequestScopedStatelessSessionHolder.class);
unremovableClasses.add(QuarkusArcBeanContainer.class);

additionalBeans.produce(AdditionalBeanBuildItem.builder().setUnremovable()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.hibernate.orm.stateless;

import static org.junit.jupiter.api.Assertions.assertEquals;

import jakarta.inject.Inject;

import org.hibernate.StatelessSession;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.Arc;
import io.quarkus.hibernate.orm.MyEntity;
import io.quarkus.hibernate.orm.naming.PrefixPhysicalNamingStrategy;
import io.quarkus.test.QuarkusUnitTest;

public class StatelessSessionWithinRequestScopeTest {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(MyEntity.class, PrefixPhysicalNamingStrategy.class)
.addAsResource(EmptyAsset.INSTANCE, "import.sql")
.addAsResource("application-physical-naming-strategy.properties", "application.properties"));

@Inject
StatelessSession statelessSession;

@BeforeEach
public void activateRequestContext() {
Arc.container().requestContext().activate();
}

@Test
public void test() throws Exception {
Number result = (Number) statelessSession.createNativeQuery("SELECT COUNT(*) FROM TBL_MYENTITY").getSingleResult();
assertEquals(0, result.intValue());
}

@AfterEach
public void terminateRequestContext() {
Arc.container().requestContext().terminate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.quarkus.hibernate.orm.stateless;

import static org.assertj.core.api.Assertions.*;

import java.util.List;

import jakarta.inject.Inject;
import jakarta.transaction.UserTransaction;

import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.orm.enhancer.Address;
import io.quarkus.test.QuarkusUnitTest;

public class StatelessSessionWithinTransactionTest {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClass(Address.class))
.withConfigurationResource("application.properties");

@Inject
StatelessSession statelessSession;

@Inject
Session session;

@Inject
UserTransaction transaction;

@Test
public void test() throws Exception {
transaction.begin();
Address entity = new Address("high street");
session.persist(entity);
transaction.commit();

transaction.begin();
List<Object> list = statelessSession.createQuery("SELECT street from Address").getResultList();
assertThat(list).containsOnly("high street");
transaction.commit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.eclipse.microprofile.config.ConfigProvider;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.boot.archive.scan.spi.Scanner;
import org.hibernate.engine.spi.SessionLazyDelegator;
import org.hibernate.integrator.spi.Integrator;
Expand Down Expand Up @@ -126,6 +127,23 @@ public Session get() {
};
}

public Function<SyntheticCreationalContext<StatelessSession>, StatelessSession> statelessSessionSupplier(
String persistenceUnitName) {
return new Function<SyntheticCreationalContext<StatelessSession>, StatelessSession>() {

@Override
public StatelessSession apply(SyntheticCreationalContext<StatelessSession> context) {
TransactionSessions transactionSessions = context.getInjectedReference(TransactionSessions.class);
return new StatelessSessionLazyDelegator(new Supplier<StatelessSession>() {
@Override
public StatelessSession get() {
return transactionSessions.getStatelessSession(persistenceUnitName);
}
});
}
};
}

public void doValidation(String puName) {
Optional<String> val;
if (puName.equals(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.hibernate.orm.runtime;

import java.util.HashMap;
import java.util.Map;

import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.RequestScoped;

import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;

/**
* Bean that is used to manage request scoped stateless sessions
*/
@RequestScoped
public class RequestScopedStatelessSessionHolder {

private final Map<String, StatelessSession> sessions = new HashMap<>();

public StatelessSession getOrCreateSession(String name, SessionFactory factory) {
return sessions.computeIfAbsent(name, (n) -> factory.openStatelessSession());
}

@PreDestroy
public void destroy() {
for (Map.Entry<String, StatelessSession> entry : sessions.entrySet()) {
entry.getValue().close();
}
}

}
Loading

0 comments on commit 7c08056

Please sign in to comment.