JPA EntityManager not working when using Guice's PrivateModule - java

I have a service with a persistence setup using JPA, Hibernate and Guice (if it's useful, I'm not using Spring). This is the first, working version of my code:
public class BookDao {
#Inject
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) {
getEntityManager().persist(book);
}
}
public class MyAppModule extends AbstractModule {
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
final JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
But now I need to configure multiple persistence units. I'm following the advice in this mailing list, and according to them, I should move my module logic to a private module. I did as suggested and created a second version of the same code, the changes are commented below:
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ FIELD, PARAMETER, METHOD })
public #interface ProductionDataSource {} // defined this new annotation
public class BookDao {
#Inject
#ProductionDataSource // added the annotation here
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) throws Exception {
getEntityManager().persist(book);
}
}
public class MyAppModule extends PrivateModule { // module is now private
#Override
protected void configure() {
initializePersistence();
// expose the annotated entity manager
Provider<EntityManager> entityManagerProvider = binder().getProvider(EntityManager.class);
bind(EntityManager.class).annotatedWith(ProductionDataSource.class).toProvider(entityManagerProvider);
expose(EntityManager.class).annotatedWith(ProductionDataSource.class);
}
private void initializePersistence() {
JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
The newly annotated EntityManager is being correctly injected by Guice and is non-null, but here's the fun part: some of my unit tests started failing, for example:
class BookDaoTest {
private Injector injector;
private BookDao testee;
#BeforeEach
public void setup() {
injector = Guice.createInjector(new MyAppModule());
injector.injectMembers(this);
testee = injector.getInstance(BookDao.class);
}
#Test
public void testPersistBook() throws Exception {
// given
Book newBook = new Book();
assertNull(newBook.getId());
// when
newBook = testee.persist(newBook);
// then
assertNotNull(newBook.getId()); // works in the first version, fails in the second
}
}
In the first version of my code the last line above just works: the entity is persisted and has a new id. However, in the second version of my code (using a PrivateModule and exposing an annotated EntityManager from it) the persist() operation doesn't work anymore, the entity is without an id. What could be the problem? I didn't do any other configuration changes in my environment, and I don't see error messages in the logs. Let me know if you need more details.

It turns out that the problem was the #Transactional annotation. In the first version of my code, Guice automatically adds interceptors for managing the transaction. By doing a debug, I found out that before executing my persist(Book book) method, Guice calls the following method from the com.google.inject.internal.InterceptorStackCallback package:
public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)
In the second version of my code, when I exposed the persistence unit from a private module the above interceptor was no longer called, leaving my persist operation without transaction handling. This is a known issue and is by design.
As a workaround I had to implement transactions by hand, making my code more verbose. I also had to change the way the entity manager is injected. This solution worked for me:
public class BookDao {
#Inject
#Named(PROD_PERSISTENCE_UNIT_NAME)
private EntityManagerFactory entityManagerFactory;
private EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
public void persist(Book book) throws Exception {
EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.persist(book);
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
throw e;
} finally {
em.close();
}
}
}
public class MyAppModule extends PrivateModule {
public static final String PROD_PERSISTENCE_UNIT_NAME = "prod";
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
// persistence unit set to prod DB
final JpaPersistModule jpaPersistModule = new JpaPersistModule(PROD_PERSISTENCE_UNIT_NAME);
// connection properties set to suitable prod values
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
// expose bindings to entity manager annotated as "prod"
bind(JPAInitializer.class).asEagerSingleton();
bind(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).to(PersistService.class).asEagerSingleton();
expose(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManagerFactory.class));
expose(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManager.class));
expose(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(UnitOfWork.class));
expose(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
}
}
As a lesson, be very watchful around annotations and other such "magic" that modifies your code under the hood, finding bugs becomes quite difficult.

Related

Commit failed silently with Spring JpaTransactionManager

I work on a project developed by a team mate but we are facing a strange issue that drive me crazy: database is never updated and there's no exception in the logs. Here's the code:
#Service
#Transactional
public class InterventionProjectResultIntegrator implements IInterventionProjectResultIntegrator {
private static final ILogger logger = ComponentLogger.getInstance(InterventionProjectResultIntegrator.class);
private Dao dao;
private String APPLICATION = "APP";
#Autowired
public void setDao(Dao dao){
this.dao = dao;
}
#Override
public void integrateResponse() {
try {
List<ResponseEntity> responseListByStatus = dao.findAllResponseByStatus(Dao.STATUS_EN_COURS, APPLICATION);
for (ResponseEntity response: responseListByStatus ) {
response.setStatus(Dao.STATUS_OK);
dao.mergeResponseEntity(response);
}
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
throw ex;
}
}
}
As you can see the function is pretty simple:
Getting objects from database
Loop over the objects
Update Each object status
At the end of the loop commit changes
Everything run fine except that the objects are not updated on database and that there is no exception.
The Dao come from a maven dependency that worked fine in another project so I assume that the problem is related to the new one.
I can see following logs in console:
org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder#6dcee890] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#713e49c3] bound to thread org.hibernate.event.internal.AbstractSaveEventListener - Persistent instance of: com.domain.ResponseEntity
org.hibernate.event.internal.DefaultMergeEventListener - Ignoring persistent instance
org.hibernate.action.internal.UnresolvedEntityInsertActions - No entity insert actions have non-nullable, transient entity dependencies.
Did you already face similar issue ?
Regards.
[EDIT 1]
As pointed out in comment, I replaced manual transaction handling with #Transactional annotation. See the code updated.
So now I have a new line in the logs, but the same result, object is not save in database.
org.springframework.transaction.interceptor.TransactionAspectSupport - Completing transaction for [com.response.InterventionProjectResultIntegrator.integrateResponse]
As asked the DAO source. This code is not under my responsability, and worked like a charm in another context.
#Repository
public class Dao {
public static final ILogger logger = ComponentLogger.getInstance(Dao.class);
public static final String STATUS_EN_COURS = "PENDING";
public static final String STATUS_OK = "OK";
public static final String STATUS_ERROR = "ERROR";
#PersistenceContext
protected EntityManager entityManager;
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void mergeMvzResponseEntity(ResponseEntity responseEntity) {
if(entityManager != null) {
this.entityManager.merge(responseEntity);
} else {
logger.error("Entity manager not initialized");
}
}
As suggested, I have reworked the source code to use #Transactional annotation, and to let Spring handle transaction stuff:
#Service
#Transactional
public class InterventionProjectResultIntegrator implements IInterventionProjectResultIntegrator {
private static final ILogger logger = ComponentLogger.getInstance(InterventionProjectResultIntegrator.class);
private Dao dao;
private String APPLICATION = "APP";
#Autowired
public void setDao(Dao dao){
this.dao = dao;
}
#Override
public void integrateResponse() {
try {
List<ResponseEntity> responseListByStatus = dao.findAllResponseByStatus(Dao.STATUS_EN_COURS, APPLICATION);
for (ResponseEntity response: responseListByStatus ) {
response.setStatus(Dao.STATUS_OK);
dao.mergeResponseEntity(response);
}
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
throw ex;
}
}
}
Then added this line to my xml configuration file:
<tx:annotation-driven/>
Now it works like a charm. Thanks to #M. Deinum to have pointed it out.

Spring Entities Not Saved To Database

We have set up the Spring Framework like this:
#Eager
public interface CatalogElementRepository extends PagingAndSortingRepository<CatalogElementEntity, Long> {
}
#Service
public class CatalogImpl implements CatalogManager {
#Inject
CatalogElementRepository catalogElementRepository;
#Override
public CatalogElement createCatalogElement(CatalogElementEntity catalogElement) {
return this.catalogElementRepository.save(catalogElement);
}
}
#Stateless
#Remote(CatalogManager.class)
public class CatalogManagerBean implements CatalogManager {
#Inject
CatalogManager delegate;
#Override
public CatalogElement createCatalogElement(CatalogElementEntity catalogElement) {
return this.delegate.createCatalogElement(catalogElement);
}
}
So whenever someone calls the method on the remote interface createCatalogElement, I'd assume the entity gets stored in the database. It does not (weirdly enough, findOne still returns the very same entity, but it can't be found via findByProperty).
Other questions said to add #Transactional, so I added #javax.transaction.Transactional and org.springframework.transaction.annotation.Transactional on the methods and classes to be on the safe side, nothing worked.
What could be the problem?
I don't see any configuration files for the Spring Framework, but it's a legacy project, so they might just be hidden very well.
For some reason using this class as a producer for the EntityManager helped:
public class SpringConfig {
#PersistenceUnit
EntityManagerFactory emf;
#PersistenceContext
EntityManager em;
#Produces
#ApplicationScoped
public EntityManagerFactory createEntityManagerFactory() {
return this.emf;
}
#Produces
public EntityManager createEntityManager() {
return this.em;
}
public void close(#Disposes EntityManagerFactory entityManagerFactory) {
entityManagerFactory.close();
}
public void close(#Disposes EntityManager entityManager) {
entityManager.close();
}
}

JUnit: #Rule's overridden apply method not executed under test

I created a JUnit rule to rollback a javax.persistence transaction when any exception occurs (otherwise all further tests will fail since the transaction is in an inconsistent condition). The problem is that my rule will never be executed when the test starts, strictly speaking: the apply method is never executed. it does not even work when I put the #Rule declaration into the concrete class and initialize the transactionRule within the test. Here is how it looks like:
THE RULE
public class TransactionRule implements TestRule
{
private EntityManager entityManager;
public TransactionRule(EntityManager entityManager)
{
this.entityManager = entityManager;
}
#Override
public Statement apply(Statement base, Description description)
{
return new TransactionStatement(base);
}
public class TransactionStatement extends Statement
{
private final Statement runningTest;
public TransactionStatement(Statement runningTest)
{
this.runningTest = runningTest;
}
#Override
public void evaluate() throws Throwable
{
try
{
runningTest.evaluate();
}
catch (Exception e)
{
if (entityManager.getTransaction().isActive())
{
entityManager.getTransaction().rollback();
}
}
}
}
}
THE ABSTRACT CLASS THAT USES THE RULE
public abstract class AbstractIntegrationTest
{
//more members vars
#Rule
public TransactionRule transactionRule;
#BeforeClass
public static void setUpBeforeClass()
{
loadProperties();
entityManagerFactory = Persistence.createEntityManagerFactory("MyCuisinePersistenceTestUnit", connectionProperties);
entityManager = entityManagerFactory.createEntityManager();
}
#Before
public void setUp()
{
transactionRule = new TransactionRule(entityManager);
entityManager.clear();
}
//more code
}
THE TEST CLASS WITH THE DEFECTIVE TEST
public class RecipePersistenceITest extends AbstractIntegrationTest
{
//more tests
#Test
public void persistenceOfRecipeWithUserCategorySuccessful()
{
//Test that fails
}
}
Any ideas?
Test rules are invoked before methods annotated with #Before, so trying to assign a rule in #Before will have no effect (although I would have expected it to throw an exception instead).
Instead assign the rule on definition (and make the field final), and do any additional config (if necessary) in your #Before.
Note that each test method is executed in a new instance of the test class, so defining a rule as a final field is no problem.

How do I utilize supportsNullCreation() in Jersey?

I have an injectable provider that may or may return null. I am getting an exception when it is null. I registered the provider as a Singleton, can I possibly register it as a type of SingletonContext that I customize to return true for supportsNullCreation()? I think if I can do that then even if findOrCreate() returns null, my code will still run which is what I want.
#ApplicationPath("rest")
public class MyApplication extends ResourceConfig
{
public MyApplication()
{
...
// Provider of DB
this.register( new AbstractBinder()
{
#Override
public void configure()
{
bindFactory(DbManager.class).to(EntityManagerFactory.class).in(Singleton.class);
}
});
}
Then it is used like this:
#Singleton
#Path("myservice")
public class WebServiceClass
{
// NOTE: Right now I have to comment this to run without a DB
#Inject
private EntityManagerFactory entityManagerFactory = null;
...
The exception I get is this...
java.lang.IllegalStateException: Context
org.jvnet.hk2.internal.SingletonContext#6cae5847 findOrCreate returned a null for
descriptor SystemDescriptor(
implementation=com.db.DbManager
contracts={javax.persistence.EntityManagerFactory}
scope=javax.inject.Singleton
qualifiers={}
descriptorType=PROVIDE_METHOD
descriptorVisibility=NORMAL
metadata=
rank=0
loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2#7050f2b1
proxiable=null
proxyForSameScope=null
analysisName=null
id=145
locatorId=0
identityHashCode=863132354
reified=true)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2075)
...
I would recommend changing the design a bit. Using the EntityManagerFactory in the resource class is not very great design. You are left with code like
public class Resource {
private EntityManagerFctory emf;
#POST
public Response get(Entity e) {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(e);
em.getTransaction().commit();
em.close();
}
}
There are a lot of things wrong with this picture. For one you are breaking the [Single Responsibility Principle][1]. Secondly this doesn't allow you to elegantly handle the null EMF, even if it was possible. You have this all over the place
if (emf != null) {
// do code above
} else {
// do something else.
}
Also it is no great for testing. The common pattern is to use a DAO layer. Personally I even add a service layer in between the DAO and the REST layer, but you can get away with just a DAO layer.
For example what I would do is create a common abstraction interface for the data access calls.
public interface DataService {
Data getData();
}
Then create an implementation for db access
public class WithDbService implements DataService {
private EntityManagerFactory emf;
public WithDbService(EntityManagerFactory emf) {
this.emf = emf;
}
#Override
public Data getData() {
...
}
}
Then create another implementation without db access.
public class WithoutDbService implements DataService {
#Override
public Data getData() {}
}
Then you can use a Factory to create the DataService. What you will do is use the ServiceLocator to try and find the EMF. If it is not null, return the WithDbService else return the WithoutDbService
public class DataServiceFatory implements Factory<DataService> {
private DataService dataService;
#Inject
public DataServiceFactory(ServiceLocator locator) {
// abbreviated for brevity
EMF emf = locator.getService(EMF.class);
if (emf != null) {
dataService = new WithDbService(emf);
} else {
dataService = new WithoutDbService();
}
}
#Override
public DataService provider() { return dataService; }
}
[...]
bindFactory(DataServiceFactory.class).to(DataService.class).in(..);
Then you can just inject DataService every where. As long as the two implementations follow the contract, it should work just fine.
There may be some design improvements, but it is a big step up from using the EMF directly in the resource class.

HibernateUtil with JPA

I can't figure out what HibernateUtil is ...
Is it required with JPA?
I use JPA with GWT , is this implementation sufficient?
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public final class EMF {
private static final EntityManagerFactory emfInstance =
Persistence.createEntityManagerFactory("default");
private EMF() {}
public static EntityManagerFactory get() {
return emfInstance;
}
}
And at the use:
public class AccountDao {
public static final EntityManager entityManager() {
return Emf.get().createEntityManager();
}
public void createAccount(Account account) {
EntityManager em = entityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.persist(account);
tx.commit();
}
catch (Throwable t) {
t.printStackTrace();
tx.rollback();
}
finally {
em.close();
}
}
}
See this post (Gilead JPA configuration) please. I can't understand yet, how to use HibernateUtil, or HibernateJpaUtil, or PersistentBeanManager stuff ...
To use Gilead with GWT, first change your GWT-RPC service implementations from
public class MyServiceImpl extends RemoteServiceServlet implements MyService {
....
}
into:
public class MyServiceImpl extends PersistentRemoteService implements MyService {
....
}
Then, in the constructor of these classes, call the method setBeanManager(beanManager). Perform the setup as I described in my other answer. Here's the entire code snippet for reference:
public class MyServiceImpl extends PersistentRemoteService implements MyService {
public MyServiceImpl() {
EntityManagerFactory emf = EMF.get();
HibernateJpaUtil hibernateJpaUtil = new HibernateJpaUtil();
hibernateJpaUtil.setEntityManagerFactory(emf);
PersistentBeanManager persistentBeanManager =
GwtConfigurationHelper.initGwtStatelessBeanManager(hibernateJpaUtil);
setBeanManager(persistentBeanManager);
}
// Service methods follow here
}
This is sufficient for the setup - Gilead then uses the bean manager (and HibernateJpaUtils) automatically under the covers, you don't have to interact directly with it. All you have to do is to make sure, that your entities extend net.sf.gilead.pojo.gwt.LightEntity.
Your implementation is pretty sufficient. I would put the factory in the servlet context, rather than making it static though.
But note an important thing here. The above code will work if you are using it purely on the server-side.
Since you are using GWT, it is possible (although I don't think it is rational) to use hibernate "stuff" on the client-side. For that you'd need gilead, where you will need the forementioned utilities.

Categories

Resources