Skip to content

Commit

Permalink
Merge pull request #868 from stuartwdouglas/tx-scoped-em
Browse files Browse the repository at this point in the history
Fixes #867 Make sure then entity manager works when no request scope …
  • Loading branch information
Sanne committed Feb 13, 2019
2 parents 266cdde + f645cbb commit 1ebf7ae
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import org.jboss.shamrock.jpa.runtime.DefaultEntityManagerProducer;
import org.jboss.shamrock.jpa.runtime.JPAConfig;
import org.jboss.shamrock.jpa.runtime.JPADeploymentTemplate;
import org.jboss.shamrock.jpa.runtime.RequestScopedEntityManagerHolder;
import org.jboss.shamrock.jpa.runtime.TransactionEntityManagers;
import org.jboss.shamrock.jpa.runtime.boot.scan.ShamrockScanner;

Expand Down Expand Up @@ -134,7 +135,7 @@ void handleNativeImageImportSql(BuildProducer<SubstrateResourceBuildItem> resour

@BuildStep
void registerBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans, CombinedIndexBuildItem combinedIndex, List<PersistenceUnitDescriptorBuildItem> descriptors) {
additionalBeans.produce(new AdditionalBeanBuildItem(false, JPAConfig.class, TransactionEntityManagers.class));
additionalBeans.produce(new AdditionalBeanBuildItem(false, JPAConfig.class, TransactionEntityManagers.class, RequestScopedEntityManagerHolder.class));

if (descriptors.size() == 1) {
// There is only one persistence unit - register CDI beans for EM and EMF if no
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.shamrock.jpa.runtime;

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

import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;


/**
* Bean that is used to manage request scoped entity managers
*/
@RequestScoped
public class RequestScopedEntityManagerHolder {

private final Map<String, EntityManager> entityManagers = new HashMap<>();

public EntityManager getOrCreateEntityManager(String name, EntityManagerFactory factory) {
return entityManagers.computeIfAbsent(name, (n) -> factory.createEntityManager());
}

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;

import org.jboss.shamrock.jpa.runtime.entitymanager.TransactionScopedEntityManager;

@RequestScoped
public class TransactionEntityManagers {

@Inject
Expand All @@ -40,6 +38,9 @@ public class TransactionEntityManagers {
@Inject
JPAConfig jpaConfig;

@Inject
Instance<RequestScopedEntityManagerHolder> requestScopedEntityManagers;

private final Map<String, TransactionScopedEntityManager> managers;

public TransactionEntityManagers() {
Expand All @@ -48,14 +49,7 @@ public TransactionEntityManagers() {

public EntityManager getEntityManager(String unitName) {
return managers.computeIfAbsent(unitName,
un -> new TransactionScopedEntityManager(tm, tsr, jpaConfig.getEntityManagerFactory(un)));
}

@PreDestroy
public void destroy() {
for (TransactionScopedEntityManager manager : managers.values()) {
manager.requestDone();
}
un -> new TransactionScopedEntityManager(tm, tsr, jpaConfig.getEntityManagerFactory(un), unitName, requestScopedEntityManagers));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Map;

import javax.enterprise.inject.Instance;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
Expand All @@ -38,24 +39,26 @@
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;

import org.jboss.shamrock.jpa.runtime.RequestScopedEntityManagerHolder;

public class TransactionScopedEntityManager implements EntityManager {

private final TransactionManager transactionManager;
private final TransactionSynchronizationRegistry tsr;
private final EntityManagerFactory emf;
private final String unitName;
private static final Object transactionKey = new Object();
private EntityManager fallbackEntityManager;
private final Instance<RequestScopedEntityManagerHolder> requestScopedEms;

public TransactionScopedEntityManager(TransactionManager transactionManager, TransactionSynchronizationRegistry tsr, EntityManagerFactory emf) {
public TransactionScopedEntityManager(TransactionManager transactionManager,
TransactionSynchronizationRegistry tsr,
EntityManagerFactory emf,
String unitName, Instance<RequestScopedEntityManagerHolder> requestScopedEms) {
this.transactionManager = transactionManager;
this.tsr = tsr;
this.emf = emf;
}

public void requestDone() {
if(fallbackEntityManager != null) {
fallbackEntityManager.close();
}
this.unitName = unitName;
this.requestScopedEms = requestScopedEms;
}

EntityManagerResult getEntityManager() {
Expand All @@ -81,10 +84,11 @@ public void afterCompletion(int i) {
});
return new EntityManagerResult(newEm, false);
} else {
if(fallbackEntityManager == null) {
fallbackEntityManager = emf.createEntityManager();
}
return new EntityManagerResult(emf.createEntityManager(), false);
//this will throw an exception if the request scope is not active
//this is expected as either the request scope or an active transaction
//is required to properly managed the EM lifecycle
RequestScopedEntityManagerHolder requestScopedEms = this.requestScopedEms.get();
return new EntityManagerResult(requestScopedEms.getOrCreateEntityManager(unitName, emf), false);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.shamrock.jpa.tests.configurationless;

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

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
Expand All @@ -9,8 +12,6 @@
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;

/**
* @author Emmanuel Bernard emmanuel@hibernate.org
Expand Down Expand Up @@ -38,8 +39,7 @@ public Map<String, String> create() {
em.clear();
gift = em.find(Gift.class, gift.getId());
map.put("jpa", "Roller coaster".equals(gift.getName()) ? "OK" : "Boooo");
}
catch (Exception e) {
} catch (Exception e) {
map.put("exception message", e.getMessage());
}
return map;
Expand All @@ -60,13 +60,11 @@ public Map<String, String> createUserTransaction() {
gift = em.find(Gift.class, gift.getId());
transaction.commit();
map.put("jpa", "Roller coaster".equals(gift.getName()) ? "OK" : "Boooo");
}
catch (Exception e) {
} catch (Exception e) {
map.put("exception message", e.getMessage());
try {
transaction.rollback();
}
catch (Exception ne) {
} catch (Exception ne) {
//swallow the bastard
}
}
Expand All @@ -85,4 +83,12 @@ public Map<String, String> get() {
return map;
}

@GET
@Produces(MediaType.TEXT_PLAIN)
@Transactional
@Path("/cake")
public String getCake() {
Cake c = (Cake) em.createQuery("from Cake").getSingleResult();
return c.getType();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.shamrock.jpa.tests.configurationless;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "typeConstraint", columnNames = "type"))

public class Cake {
private Long id;
private String type;

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="cakeSeq")
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}


public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.shamrock.jpa.tests.configurationless;

import javax.enterprise.context.Dependent;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.jboss.shamrock.runtime.StartupEvent;

/**
* creates a chocolate cake on startup to make sure JPA works in the startup event
*/
@Dependent
public class StartupCakeManager {

@Inject
EntityManager entityManager;

@Transactional
public void startup(@Observes StartupEvent startupEvent) {
Cake c = new Cake();
c.setType("Chocolate");
entityManager.persist(c);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.shamrock.jpa.tests.configurationless;

import static org.hamcrest.core.StringContains.containsString;

import org.jboss.shamrock.test.junit.ShamrockTest;
import org.junit.jupiter.api.Test;

import io.restassured.RestAssured;

/**
* @author Emmanuel Bernard emmanuel@hibernate.org
*/
@ShamrockTest
public class JPAOnStartupTest {

@Test
public void testStartupJpa() {
RestAssured.when().get("/jpa-test/cake").then()
.body(containsString("Chocolate"));
}
}

0 comments on commit 1ebf7ae

Please sign in to comment.