Hot questions for Using Enterprise JavaBeans in hibernate

Question:

I have this class and I tought three ways to handle detached entity state in case of persistence exceptions (which are handled elsewhere):

@ManagedBean
@ViewScoped
public class EntityBean implements Serializable
{
    @EJB
    private PersistenceService service;

    private Document entity;

    public void update()
    {
        // HANDLING 1. ignore errors
        service.transact(em -> 
        {
            entity = em.merge(entity);

            // some other code that modifies [entity] properties:
            // entity.setCode(...);
            // entity.setResposible(...);
            // entity.setSecurityLevel(...);

        });  // an exception may be thrown on method return (rollback), 
        // but [entity] has already been reassigned with a "dirty" one.

        //------------------------------------------------------------------

        // HANDLING 2. ensure entity is untouched before flush is ok
        service.transact(em -> 
        {
            Document managed = em.merge(entity);

            // some other code that modifies [managed] properties:
            // managed.setCode(...);
            // managed.setResposible(...);
            // managed.setSecurityLevel(...);

            em.flush(); // an exception may be thrown here (rollback)
            // forcing method exit without [entity] being reassigned.

            entity = managed;

        }); // an exception may be thrown on method return (rollback), 
        // but [entity] has already been reassigned with a "dirty" one.

        //------------------------------------------------------------------

        // HANDLING 3. ensure entity is untouched before whole transaction is ok
        AtomicReference<Document> reference = new AtomicReference<>();
        service.transact(em -> 
        {
            Document managed = em.merge(entity);

            // some other code that modifies [managed] properties:
            // managed.setCode(...);
            // managed.setResposible(...);
            // managed.setSecurityLevel(...);

            reference.set(managed);

        }); // an exception may be thrown on method return (rollback), 
        // and [entity] is safe, it's not been reassigned yet.

        entity = reference.get();
    }

    ...
}

PersistenceService#transact(Consumer<EntityManager> consumer) can throw unchecked exceptions.

The goal is to maintain the state of the entity aligned with the state of the database, even in case of exceptions (prevent entity to become "dirty" after transaction fail).

  • Method 1. is obviously naive and doesn't guarantee coherence.

  • Method 2. asserts that nothing can go wrong after flushing.

  • Method 3. prevents the new entity assigment if there's an exception in the whole transaction

Questions:

  1. Is method 3. really safer than method 2.?
  2. Are there cases where an exception is thrown between flush [excluded] and commit [included]?
  3. Is there a standard way to handle this common problem?

Thank you


Note that I'm already able to rollback the transaction and close the EntityManager (PersistenceService#transact will do it gracefully), but I need to solve database state and the business objects do get out of sync. Usually this is not a problem. In my case this is the problem, because exceptions are usually generated by BeanValidator (those on JPA side, not on JSF side, for computed values that depends on user inputs) and I want the user to input correct values and try again, without losing the values he entered before.

Side note: I'm using Hibernate 5.2.1


this is the PersistenceService (CMT)

@Stateless
@Local
public class PersistenceService implements Serializable
{
    @PersistenceContext
    private EntityManager em;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void transact(Consumer<EntityManager> consumer)
    {
        consumer.accept(em);
    }
}

@DraganBozanovic

That's it! Great explanation for point 1. and 2.

I'd just love you to elaborate a little more on point 3. and give me some advice on real-world use case.

However, I would definitely not use AtomicReference or similar cumbersome constructs. Java EE, Spring and other frameworks and application containers support declaring transactional methods via annotations: Simply use the result returned from a transactional method.

When you have to modify a single entity, the transactional method would just take the detached entity as parameter and return the updated entity, easy.

public Document updateDocument(Document doc)
{
    Document managed = em.merge(doc);
    // managed.setXxx(...);
    // managed.setYyy(...);

    return managed;
}

But when you need to modify more than one in a single transaction, the method can become a real pain:

public LinkTicketResult linkTicket(Node node, Ticket ticket)
{
    LinkTicketResult result = new LinkTicketResult();

    Node managedNode = em.merge(node);
    result.setNode(managedNode);

    // modify managedNode

    Ticket managedTicket = em.merge(ticket);
    result.setTicket(managedTicket);

    // modify managedTicket

    Remark managedRemark = createRemark(...);
    result.setRemark(managedemark);

    return result;
}

In this case, my pain:

  1. I have to create a dedicated transactional method (maybe a dedicated @EJB too)
  2. That method will be called only once (will have just one caller) - is a "one-shot" non-reusable public method. Ugly.
  3. I have to create the dummy class LinkTicketResult
  4. That class will be instantiated only once, in that method - is "one-shot"
  5. The method could have many parameters (or another dummy class LinkTicketParameters)
  6. JSF controller actions, in most cases, will just call a EJB method, extract updated entities from returned container and reassign them to local fields
  7. My code will be steadily polluted with "one-shotters", too many for my taste.

Probably I'm not seeing something big that's just in front of me, I'll be very grateful if you can point me in the right direction.


Answer:

  1. Is method 3. really safer than method 2.?

    Yes. Not only is it safer (see point 2), but it is conceptually more correct, as you change transaction-dependent state only when you proved that the related transaction has succeeded.

  2. Are there cases where an exception is thrown between flush [excluded] and commit [included]?

    Yes. For example:

    1. LockMode.OPTIMISTIC:

      Optimistically assume that transaction will not experience contention for entities. The entity version will be verified near the transaction end.

      It would be neither performant nor practically useful to check optimistick lock violation during each flush operation within a single transaction.

    2. Deferred integrity constraints (enforced at commit time in db). Not used often, but are an illustrative example for this case.

    3. Later maintenance and refactoring. You or somebody else may later introduce additional changes after the last explicit call to flush.

  3. Is there a standard way to handle this common problem?

    Yes, I would say that your third approach is the standard one: Use the results of a complete and successful transaction.

    However, I would definitely not use AtomicReference or similar cumbersome constructs. Java EE, Spring and other frameworks and application containers support declaring transactional methods via annotations: Simply use the result returned from a transactional method.

Question:

After we migrate an application from JBoss 4 to JBoss 5 we are getting org.hibernate.LazyInitializationException when lazy loading is done using Bean-Managed Transaction (BMT) Session Beans.

I know it is a known issue when it comes to Web development but we are not doing lazy-loading from a Web component, we are doing it inside the Bean, in one single call.

If we change the bean to CMT everything works fine, but the production class is gigantic, and doing it might not be an option.

Why is it happening and what are the options to solve this problem?

BMT Stateless Session Bean

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class BMTBean implements BMTBeanLocal{

    @PersistenceContext
    private EntityManager em;

    @Override
    public void customerOperation() {

        Customer customer = em.find(Customer.class, 1);     
        List<Address> list = customer.getAddresses();       
        String streetName = list.get(0).getStreet(); // The error happens here!

        System.out.println("Street: " + streetName);
    }
}

Customer entity

@Entity
@Table
public class Customer {

    @Id
    @Column
    private Integer id;

    @Column
    private String name;

    @OneToMany(fetch=FetchType.LAZY, mappedBy="customer")   
    private List<Address> addresses;

}

Address entity which is lazy loaded

@Entity
@Table
public class Address {

    @Id
    @Column
    private Integer id;

    @Column
    private String street;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="CUSTOMER_ID", insertable = false, updatable = false)
    private Customer customer;

}

Detailed Exception

13:12:45,374 ERROR [LazyInitializationException] failed to lazily initialize a collection of role: entity.Customer.addresses, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: entity.Customer.addresses, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
    at org.hibernate.collection.PersistentBag.get(PersistentBag.java:445)
    at bean.BMTBean.customerOperation(BMTBean.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeTarget(MethodInvocation.java:122)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:111)
    at org.jboss.ejb3.EJBContainerInvocationWrapper.invokeNext(EJBContainerInvocationWrapper.java:69)
    at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.invoke(InterceptorSequencer.java:73)
    at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.aroundInvoke(InterceptorSequencer.java:59)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.aop.advice.PerJoinpointAdvice.invoke(PerJoinpointAdvice.java:174)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.fillMethod(InvocationContextInterceptor.java:72)
    at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_fillMethod_328422269.invoke(InvocationContextInterceptor_z_fillMethod_328422269.java)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.setup(InvocationContextInterceptor.java:88)
    at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_setup_328422269.invoke(InvocationContextInterceptor_z_setup_328422269.java)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:62)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:56)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.tx.StatelessBMTInterceptor.handleInvocation(StatelessBMTInterceptor.java:106)
    at org.jboss.ejb3.tx.BMTInterceptor.invoke(BMTInterceptor.java:55)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:68)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:186)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:176)
    at org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:216)
    at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:207)
    at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:164)
    at com.sun.proxy.$Proxy492.customerOperation(Unknown Source)
    at servlets.BMTServlet.doGet(BMTServlet.java:29)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
    at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
    at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
    at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:662)
13:12:45,376 ERROR [[BMTServlet]] Servlet.service() for servlet BMTServlet threw exception
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: entity.Customer.addresses, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
    at org.hibernate.collection.PersistentBag.get(PersistentBag.java:445)
    at bean.BMTBean.customerOperation(BMTBean.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeTarget(MethodInvocation.java:122)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:111)
    at org.jboss.ejb3.EJBContainerInvocationWrapper.invokeNext(EJBContainerInvocationWrapper.java:69)
    at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.invoke(InterceptorSequencer.java:73)
    at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.aroundInvoke(InterceptorSequencer.java:59)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.aop.advice.PerJoinpointAdvice.invoke(PerJoinpointAdvice.java:174)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.fillMethod(InvocationContextInterceptor.java:72)
    at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_fillMethod_328422269.invoke(InvocationContextInterceptor_z_fillMethod_328422269.java)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.setup(InvocationContextInterceptor.java:88)
    at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_setup_328422269.invoke(InvocationContextInterceptor_z_setup_328422269.java)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:62)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:56)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.tx.StatelessBMTInterceptor.handleInvocation(StatelessBMTInterceptor.java:106)
    at org.jboss.ejb3.tx.BMTInterceptor.invoke(BMTInterceptor.java:55)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:68)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:186)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    at org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:176)
    at org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:216)
    at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:207)
    at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:164)
    at com.sun.proxy.$Proxy492.customerOperation(Unknown Source)
    at servlets.BMTServlet.doGet(BMTServlet.java:29)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
    at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
    at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
    at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:662)

Answer:

LazyInitializationException happens when you did not load a relationship of the class and try to access it.

Imagine the class:

@Entity
public class Person {
    @OneToOne
    private Dog dog;

    @OneToMany
    private List<Email> emailList;
}

When you load Person from the database two things will happen:

  1. dog will be loaded by default
  2. emailList will not be loaded by default

The error that you received is because your addresses is not loaded. The JPA rule is:

  1. Every relationship that finishes with "*One" will be loaded by default, it is defined as EAGER
  2. Every relationship that finishes with "*Many" will NOT be loaded by default, it is defined as LAZY

What is Lazy? Lazy means that the relationship will only be loaded after the attribute is accessed. When you do person.getEmailList() the JPA will look for an connection to get the EmailList from the database.

The problem is: what happen if the connection is closed? You will get the error LazyInitializationException.

How to solve it? You have some options:

  1. You can create a query: select p from Person p left join fetch p.emailList e. Like this the JPA will load the email list
  2. You could change the relationship annotation: @OneToMany(fetch = FetchType.EAGER). With this configuration the JPA will always load the relationship. The problem with this approach is that you will always load it. If you have a long list but want only to access the person.name() that could reduce your project perfomance.
  3. Keep your connection opened: it would be like:

    // open the connection // get the entity // access the list // close the conection

    The problem with this approach is that if you forget to close the connection you will have an leak of resources. Other problem with this approach is that you will have the N+1 effect. N+1 is when your system is able to do several requests to the database.

  4. Use the OpenSessionInView pattern. Works like the option 3, but you will have a global handler for the request. You will have a Filter that will handle the connection.

If you want more info about N+1 of this error take a look at this link: http://uaihebert.com/four-solutions-to-the-lazyinitializationexception/

Question:

I've upgraded the version of hibernate to 5.3.10.Final (hibernate-core) to work on java 8 and on WildFly 17.0.1 Final. Most of the app is working fine but there is one problem with entities and recursive loop in hibernate some actions. (There was no such problem before upgrade)

I've tried to change somehow the properties of entities to repair this problem but cannot get the right version. The problem is that when no changes are done to the project then on some action (searching in database) I get the nested loop of hibernate which is caused by:

Caused by: java.lang.StackOverflowError
    Caused by: java.lang.StackOverflowError
    at org.jboss.jca.adapters.jdbc.WrappedConnection.checkException(WrappedConnection.java:2019)
    at org.jboss.jca.adapters.jdbc.WrappedStatement.checkException(WrappedStatement.java:1436)
    at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeQuery(WrappedPreparedStatement.java:509)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:2167)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1930)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1892)
    at org.hibernate.loader.Loader.doQuery(Loader.java:937)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:340)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:310)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2281)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:64)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:54)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4273)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:511)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:481)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:222)
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:119)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:92)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1257)
    at org.hibernate.internal.SessionImpl.immediateLoad(SessionImpl.java:1115)
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:178)
    at org.hibernate.proxy.AbstractLazyInitializer.getIdentifier(AbstractLazyInitializer.java:89)
    at org.hibernate.type.EntityType.getHashCode(EntityType.java:372)
    at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:242)
    at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:61)
    at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:54)
    at org.hibernate.internal.AbstractSharedSessionContract.generateEntityKey(AbstractSharedSessionContract.java:524)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:867)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:718)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:990)
    at org.hibernate.loader.Loader.doQuery(Loader.java:948)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:340)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:310)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2281)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:64)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:54)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4273)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:511)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:481)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:222)
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:119)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:92)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1257)
    at org.hibernate.internal.SessionImpl.immediateLoad(SessionImpl.java:1115)
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:178)
    at org.hibernate.proxy.AbstractLazyInitializer.getIdentifier(AbstractLazyInitializer.java:89)
    at org.hibernate.type.EntityType.getHashCode(EntityType.java:372)
    at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:242)
    at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:61)
    at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:54)
    at org.hibernate.internal.AbstractSharedSessionContract.generateEntityKey(AbstractSharedSessionContract.java:524)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:867)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:718)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:990)
    at org.hibernate.loader.Loader.doQuery(Loader.java:948)
... (the same in loop)

I have noticed there that the first time the doQueryfunction is called it shows 937 line number and when the second time then it is 948 line number in log. Then when I made changes in code below in WorkinfoEntity to remove the cascade MERGE and PERSIST the error with StackOverflow when searching not appears on most entities (it was in the past and cannot get it working in this way again...) but then sometime there wa some problem with not found entities (special one with some attachemnts) (but there are working entities with attachemnts)

Unable to find AttachmentEntity with id AttachmentEntityKey [workInfoAttachmentNumber=1000000351 workInfo=WorkInfoEntity@d198fb73]

(In this project workInfoAttachmentNumber is not unique and is not like the id of attachemnt) I think that the problem may be with any hashCode or equals functions or with the @EmbeddedId class (I've read that they should not be used but they were already in the project and I'm not sure if I can change it). I have tried to change the annotation properties (even I would say somehow randomly) but with no desired effect.

The parts of codes of the classes that I think the problem is in:

@Entity
@Table(name = "ITEM_WORK_INFO")
public class WorkInfoEntity extends AbstractEntryEntity implements Serializable {
    private static final long serialVersionUID = 7681809420366638143L;

    public WorkInfoEntity() {}

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ITEM_ID", nullable = false)
    private AbstractItemEntity item;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "SUBMIT_DATE")
    private Date submitDate;

    @Lob
    @Column(name = "NOTES", columnDefinition = "CLOB")
    private String notes;

    @Column(name = "SUMMARY", length = 4000)
    private String summary;

    @Column(name = "LAST_MODIFIED_BY")
    private String lastModifiedBy;

    @Column(name = "EMPTY_SLOTS")
    private String emptySlots = "";

    @OneToMany(mappedBy = "id.workInfo", cascade = {CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY) 
    // @Fetch(FetchMode.JOIN)
    private Set<AttachmentEntity> workinfoAttachments = new HashSet<AttachmentEntity>();

    ... // setters and getters

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 23;
        result = (prime * result) + ((emptySlots == null) ? 0 : emptySlots.hashCode());
        result = (prime * result) + ((lastModifiedBy == null) ? 0 : lastModifiedBy.hashCode());
        result = (prime * result) + ((submitDate == null) ? 0 : submitDate.hashCode());
        result = (prime * result) + ((summary == null) ? 0 : summary.hashCode());
        result = (prime * result) + ((workinfoAttachments == null) ? 0 : workinfoAttachments.hashCode());
        result = (prime * result) + ((notes == null) ? 0 : notes.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final WorkInfoEntity other = (WorkInfoEntity) obj;
        if (this.submitDate != other.submitDate && (this.submitDate == null || !this.submitDate.equals(other.submitDate))) {
            return false;
        }
        if ((this.notes == null) ? (other.notes != null) : !this.notes.equals(other.notes)) {
            return false;
        }
        if ((this.summary == null) ? (other.summary != null) : !this.summary.equals(other.summary)) {
            return false;
        }
        if ((this.lastModifiedBy == null) ? (other.lastModifiedBy != null) : !this.lastModifiedBy.equals(other.lastModifiedBy)) {
            return false;
        }
        if ((this.emptySlots == null) ? (other.emptySlots != null) : !this.emptySlots.equals(other.emptySlots)) {
            return false;
        }
        if (this.workinfoAttachments != other.workinfoAttachments && (this.workinfoAttachments == null || !this.workinfoAttachments.equals(other.workinfoAttachments))) {
            return false;
        }
        return true;
    }

@Entity
@Table(name = "WORK_INFO_ATTACHMENT")
public class AttachmentEntity implements Serializable, IEntity<AttachmentEntityKey> {
    private static final long serialVersionUID = 6571993598130579018L;
    @EmbeddedId
    AttachmentEntityKey id = new AttachmentEntityKey();

    @Column(name = "FILE_NAME")
    private String fileName;

    @Column(name = "FILE_SIZE")
    private String fileSize;

    public AttachmentEntity() {}

    ... // getters and setters

    @Override
    public AttachmentEntityKey getId() {
        return id;
    }
}
@Embeddable
public class AttachmentEntityKey implements Serializable {

    private static final long serialVersionUID = 2354257422144993554L;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "WORK_INFO_ID", nullable = false)
    private WorkInfoEntity workInfo;

    @Column(name = "WORK_INFO_ATTACHMENT_NO")
    private Integer workInfoAttachmentNumber;

    ... // getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof AttachmentEntityKey)) return false;
        AttachmentEntityKey that = (AttachmentEntityKey) o;
        return getWorkInfo() == that.getWorkInfo();
    }

    @Override
    public int hashCode() {
        int result = 19;
        result = (31 * result) + ((getWorkInfo() != null) ? getWorkInfo().hashCode() : 0);
        return result;
    }
}

The equals and hashCode methods were added by me to AttachmentEntityKey after upgrade because I got warning about not implemented these methods in this class after upgrade. (It was said to be composite id class)

I've been working with it for a few days and cannot find solution so it is really annoying. Do you see anything that should be changed in order to try get this project fully working?

UPDATE When I remove the field workinfoAttachments feom the class and all its usages then the project compiles and evrything is working fine. So I can assusme that this is my problem but I don't know how to solve it properly.


Answer:

I've done a workaround to this problem as it seemed to be the hibernate+WildFly bug as described here https://hibernate.atlassian.net/browse/HHH-13296.

So my workaround was to remove the EmbeddedId and create new IDcolumn in table in database which is a concatenation of 2 used columns in primary key which was EmbeddedId (and set them in proper places in code)

Question:

I want to create an entity ReportEntry which is mapped by an SQL. Here are two tables - user and group.

mysql> desc user;
+------------------------+--------------+------+-----+---------+----------------+
| Field                  | Type         | Null | Key | Default | Extra          |
+------------------------+--------------+------+-----+---------+----------------+
| id                     | int(11)      | NO   | PRI | NULL    | auto_increment |
| firstName              | varchar(255) | YES  |     | NULL    |                |
| lastName               | varchar(255) | YES  |     | NULL    |                |
| active                 | bit(1)       | NO   |     | NULL    |                |
| language               | varchar(20)  | NO   |     | NULL    |                |
| activationDate         | datetime     | YES  |     | NULL    |                |
| group_id               | int(11)      | YES  | MUL | NULL    |                |
|                                                                               |
|                           ...More columns...                                  |
|                                                                               |
+------------------------+--------------+------+-----+---------+----------------+

mysql> desc group;
+----------------------------+--------------+------+-----+---------+----------------+
| Field                      | Type         | Null | Key | Default | Extra          |
+----------------------------+--------------+------+-----+---------+----------------+
| id                         | int(11)      | NO   | PRI | NULL    | auto_increment |
| displayName                | varchar(255) | YES  | MUL | NULL    |                |
| description                | varchar(255) | YES  |     | NULL    |                |
+----------------------------+--------------+------+-----+---------+----------------+

I am joining these tables on group_id using the below SQL query.

SELECT u.* FROM user u JOIN group g ON u.group_id = g.id

For the above query, I want to create an Entity and use the above SQL query as its mapping. How can I achieve this?

I don't want just the mapping, but I also want to use the entity to be able to query for records as well. For example, let's say I am able to create the mapping with entity, RecordEntry, I should be able to get a specific record with query like FROM RecordEntry WHERE id = :id" and I will passid```` as parameter. That way the final query should get executed in native form would be like(for id=1).

SELECT u.* FROM user u JOIN group g ON u.group_id = g.id AND id = 1

Answer:

Instead of mapping the result to a POJO, I would suggest creating entities for both classes and mapping them with the one to one annotation JPA provides:

@Entity
public class User{
  @Id
  private long id;

  @OneToOne
  @JoinColumn(name="GROUP_ID")
  private Group group;
  ...
}


@Entity
public class Group{
  @Id
  private long id;
  ...
}

Update to reflect updated question: You could then use e.g. the Criteria-API (or named queries), to query these entities:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root user = criteriaQuery.from(User.class);
criteriaQuery.where(criteriaBuilder.equal(user.get("id"), criteriaBuilder.parameter(Long.class, "id")));
Query query = entityManager.createQuery(criteriaQuery);
query.setParameter("id", id);
User result = (User)query.getSingleResult();

Source: Wikibooks on JPA

Question:

When the program encounter an exception, The RollBackOnly goes to True.

How can I "Set" this RollBack To False Even it is encountering an exception.

@Resource
protected SessionContext context;

Public void creerNewEntity(final Entity myEntity) {
try {
    this.em.persist(myEntity);
    this.em.flush();

    } catch (final EntityExistsException e) {
      System.out.println((this.context.getRollbackOnly())); // It s has a true value
      throw new MyException("TODO BLABLA", e);
    }
 }  

When the program throw this Exception "MyException", I change the object myEntity by setting for example a new Id then I call again creerNewEntity().

Unfortunately, it doesn't work, I got this exception "javax.persistence.PersistenceException: org.hibernate.HibernateException: proxy handle is no longer valid", I think because the RollBack has a true value, How can I change the rollback to make this works ?

Thanks.


Answer:

There probably isn't a simple way to do this since the whole point of the EJB design is that you don't care about such things. The first error inside of a transaction makes it invalid -> rollback. That's the rule.

If you want something special, then get yourself a database connection from the session and use plain SQL instead of EJB to modify the data. That way, you can try to INSERT a new instance and handle all exceptions yourself. When the insert succeeds, you can use EJB to load the newly created object to add it to the session.

That said, I'm not sure what you're trying to achieve with the code above. Just ignoring when you can't create a new instance in the database feels like "I don't care about quality of my product." Maybe your attempt to work around an error is just a symptom of a bad design of your application. Take a step back and consider what you're doing and why. Maybe if you told us more about the reasons why you want to ignore all errors (even the really, really deadly ones), we would be able to point out a better solution.

EDIT So you get javax.persistence.EntityExistsException which means you tried to save the same entity twice. That can mean any number of things:

  1. You loaded the bean in a different session and now you try to save it in a second one. Since the new session can't know if the bean exists, it tries to create it again.
  2. Instead of loading the bean from the session like you should, you cheated and create a new instance manually. Of course, the session manager now thinks this is a new bean.

The correct solution depends on what you need to achieve. If you modified myEntity and need to save the changes, use em.merge(). The EM will then check if the object already exists and if it does, it will do an SQL UPDATE instead of an INSERT

If you just want to give some other class a valid entity, then you need to get it from the database. If the database returns null, you need to create a new instance and persist it and then return it.

See also: JPA EntityManager: Why use persist() over merge()?

Question:

I'm trying to update a pivot table through an entity where exist as a collection, my pivot table has a version field, when it updates throw an NullPointerException

I'm using Java 8 and Hibernate as JPA provider

Main entity "Carpeta.java"

@JoinColumn(name = "car_subcarpeta_id", referencedColumnName = "sub_id")
    @OneToOne(cascade = CascadeType.ALL)
    @Fetch(FetchMode.JOIN)
    private RjFicha fichaId;

Foreign key of "Carpeta.java" "SubCarpeta.java"

 @OneToMany(mappedBy = "subCarpeta", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @Fetch(FetchMode.SUBSELECT)
    private Collection<item> itemCollection;

Pivot table relate to SubCarpeta "Item.java"

@JoinColumn(name = "i_subcarpeta_id", referencedColumnName = "sub_id")
    @ManyToOne
    private SubCarpeta subCarpeta;

@Column(name = "i_version")
    @Version
    private Integer version;

This is the part of the trace when describe the source of the NullPointerException

Caused by: java.lang.NullPointerException
    at org.hibernate.type.IntegerType.next(IntegerType.java:63)
    at org.hibernate.type.IntegerType.next(IntegerType.java:22)
    at org.hibernate.engine.internal.Versioning.increment(Versioning.java:92)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.getNextVersion(DefaultFlushEntityEventListener.java:383)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:279)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:143)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:38)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1300)
    at org.jboss.as.jpa.container.AbstractEntityManager.flush(AbstractEntityManager.java:459)
    ... 141 more


Answer:

Try primitive int instead of Wrapper class.

@Column(name = "i_version")
@Version
private int version;

Default value of Integer is null and int is 0.

As per IntegerType API Java Doc ,

@Override
    public Integer next(Integer current, SharedSessionContractImplementor session) {
        return current + 1;
    }

if you keep Integer class null+1 throws nullpointerexception.

Question:

I have several repositories extending BaseRepository as follows:

public abstract class IsoRepository<T extends Serializable> {
    @PersistenceContext
    protected EntityManager entityManager;

    public void persist(T obj) {
        entityManager.persist(obj);
    }
}

@Stateless
public class T1Repository extends BaseRepository<T1> {
    // methods depending on repository
}

@Stateless
public class T2Repository extends BaseRepository<T2> {
    public Optional<T2> findByOrderId(String id) {
        return entityManager.createNamedQuery(/* ... */, T2.class)
            .setParameter("id", id).getResultList().stream().findFirst();
    }
}

// and others

EJB bean contains method reponsible for saving transaction:

@Stateless
public class TransactionService {
    @EJB
    private T2Repository t2Repository;

    public void saveTransaction() {
        // Here occurs logic that saves new entities to database via repositories injected with EJB annotation
        // and also as the last operation update is performed:
        T2 t2 = t2Repository.findById(id);
        t2.setProperty(someProperty);
        t2Repository.persist(t2);
    }
}

The problem is that all insert queries are saved in the database, but not this one poor update. I found out that I need to call entityManager.flush() explicitly, as it seems to solve the issue, but I do not really understand why is that happening. I've always thought that after transaction is committed all data is flushed automatically anyways. Do I have do change something in the configuration?


Answer:

According to the JPA-Documentation

"persist(Object entity)
Make an instance managed and persistent." 

means, persist can only be used for inserts. Normally you should get

"EntityExistsException - if the entity already exists. 
(If the entity already exists, the EntityExistsException may be 
thrown when the persist operation is invoked, or the 
EntityExistsException or another PersistenceException may be thrown at 
flush or commit time.)"

as the documentation states. So:

    T2 t2 = t2Repository.findById(id);
    t2.setProperty(someProperty);
    t2Repository.persist(t2);

according to JPA-definition should lead to EntityExistsException in any case, immediately or at the end of the transaction. This end can also be at the end of the method which itself calls TransactionService.persist. This Exception should lead to a rollback in any case, so your update will never be done.

My recommendation:

Get rid of the persist call, since the object becomes managed during find, the change will be done at the end of transaction.

See: does-jpa-hibernate-save-even-when-not-calling-persist

Question:

I made an 'Enterprise application' in NetBeans and I added hibernate as well as some hibernate mapping files to the web application project. Additionally, I need to add a 'HibernateUtil.java', 'hibernate.cfg.xml' and 'hibernate.reveng.xml' in the web application.

However I wanted to manage these entity/mapping classes in my EJB project as they seem to behave like enterprise beans and I wanted to separate them from my web application. I was wondering how this can be done and how I could access the entities ejb's in my web application project through dependency injection? There was also an option in the 'New' menu: 'Create session bean for Entity classes' and wondered if it has any use in relation to the question?

EDIT: Using EJB3


Answer:

You shouldn't have put your JPA-related files to a WAR at the first place. Keep them in EJB JAR and reference your JAR from WAR as a provided runtime dependency in Maven (or use provided scope's analogue for whichever build tool you prefer). Keeping your business logic in EJB JAR will pay off when you'll need more than one WAR in your EAR (different security realms, etc.)

In general, this is as simple as putting all your JPA-related stuff in EJB JAR, they will then be available for dependency injection from WAR-provided managed beans as well.

Also, consider replacing your Hibernate-specific configurations with JPA and let application server to resolve all the stuff for you.

Question:

I am using Glassfish, EJB, JPA and Hibernate as a JPA implementation. I can get a select from a database via JPA but an insert does not work. Looks like a problem with JTA(may be a transaction was not completed or I use an another transaction) Persistence content does not flush to a database. The same code works properly when I use eclipselink as JPA implementation but I have to use hibernate.



    @Stateless
    @LocalBean
    public class BeanISManagedByContainer
    {

        @PersistenceContext(unitName = "com.company_JPATest2-ejb_ejb_1.0PU")
        EntityManager entityManager;

        public String getMessage(int id)
        {
            return entityManager.find(Message.class, id).getText();
        }

        public void addMessaage(String txt)
        {
            Message message = new Message();
            message.setText(txt);
            entityManager.persist(message);
        }
    }


getMessage() method is works properly, but addMessaage() does not insert any data to a database and there are not any logs. Persistence context was not flushed to a database. I have tried to manage a transaction manually but the same result. I do not know what is wrong with a hibernate configuration. Please advise.



    @Stateless
    @LocalBean
    @TransactionManagement(TransactionManagementType.BEAN)
    public class ManualTransactions
    {
        @PersistenceContext(unitName = "com.company_JPATest2-ejb_ejb_1.0PU")
        EntityManager entityManager;

        @Resource
        private UserTransaction transaction;

        public void addMessaage(String txt) throws Exception
        {
            Message message = new Message();
            message.setText(txt);

            transaction.begin();
            Logger.getLogger(ManualTransactions.class.getName())
                    .info("transaction status: " + transaction.getStatus());
            entityManager.persist(message);
            Logger.getLogger(ManualTransactions.class.getName())
                    .info("transaction status: " + transaction.getStatus());
            transaction.commit();
            Logger.getLogger(ManualTransactions.class.getName())
                    .info("transaction status: " + transaction.getStatus());
        }
    }

**logs:**
Info:   transaction status: 0
Info:   transaction status: 0
Info:   transaction status: 6


persistance.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="com.company_JPATest2-ejb_ejb_1.0PU" transaction-type="JTA">
	<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
	<jta-data-source>jdbc/JPATestPool</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
		<property name="hibernate.show_sql" value="true"/>
		<property name="hibernate.format_sql" value="true"/>
		<property name="hibernate.use_sql_comments" value="true"/>
	</properties>
  </persistence-unit>
</persistence>

Answer:

JPA EntityManager needs to interact with the container JTA platform in order to get callbacks so that it can flush the context. Depending on the JPA engine it might or might not be able to detect the JTA TransactionManager of the container.

With Hibernate you need to assist it by providing a property which provides the implementation to be used to detect the JTA infrastructure.

Add the following property to the properties section of persistence.xml

<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" />

org.hibernate.service.jta.platform.internal.SunOneJtaPlatform is specific to Glassfish. You have to change accordingly to match the application server you are using

Question:

I have a little problem with JPA mapping. I want do this:

I have a table 'sale', and it has a id ( ven_cod ).

And i have a table 'credit_sale', its pk shall be a sales pk that means some sales can be credit_sale.

For instance, i have 2 sales, with code 01 and 02. The second one is a credit_sale, than in the table 'credit_sales' i'll have a register with pk 02.

How can i map this with jpa-hibernate ?? i tried this, but didn't work:

@Entity
@Table(name = "venda_credito")
public class VendaCredito {

    private long cod;
    private Cliente cliente;
    private StatusPagamento statusPagamento;
    private Date dataPagamento;

    @Id
    @JoinColumn(name = "ven_cod")
    @OneToOne
    public long getCod() {
        return cod;
    }

    .
    .
    .
@Entity
@Table(name = "venda")
public class Venda  {

    private long cod;


    @Id
    @GeneratedValue
    @Column(name = "ven_cod")
    public long getCod() {
        return cod;
    }

    .
    .
    .

What must i do to this work ?


Answer:

You can use a derived identity.

Change VendaCredito to look like this:

@Entity
@Table(name = "venda_credito")
public class VendaCredito {

    private long cod;
    private Venda venda;
    private Cliente cliente;
    private StatusPagamento statusPagamento;
    private Date dataPagamento;

    @Id
    public long getCod() {
        return cod;
    }

    @MapsId
    @JoinColumn(name = "ven_cod")
    @OneToOne
    public long getCod() {
        return cod;
    }
    .
    .
    .

This is discussed in the JPA 2.1 spec section 2.4.1.3 ex. 4.

Question:

I am trying to upgrade from Hibernate 3.6.10 to 4.3.9 (and JPA 2.0 to 2.1).

Some code in my application uses the org.hibernate.ejb.event.EJB3MergeEventListener class, which used to be in the hibernate-entitymanager jar, but is missing from the 4.3.9 version. From what I can tell, it was removed as of 4.3, but I can't find any information on why this might be or what I should do to work around it. I thought perhaps it was moved to another jar or package (as many other classes were) but I can't find it anywhere.

EDIT: According to the Hibernate user documentation, this class is supposed to be included in version 4.3.


Answer:

I guess you need to add hibernate-entitymanager.jar. you can find this jar here

Question:

I have a stateless bean that observes an event and saves a record:

@Stateless
public class Manager {
    @Inject
    Repository repository;

    Manager() {}

    @Inject
    Manager(Repository repository) {
        this.repository = repository;
    }

    public void EventHandler(@Observes MyEvent myEvent) {
        save(event.obj);
    }

    private save(Object object) {
        repository.add(object);
    }
}

My repository is like this:

@Stateless
public class Repository {
    @PersistenceContext
    EntityManager em;

    Repository() {}

    public void add(Object object) {
        em.persist(object);
        // em.flush();  <---
    }
}

It doesn’t work unless I uncomment the line The thing I don’t understand is why do I need to flush! Shouldn’t the transaction commit automatically?

Could it be that I have an EJB container and the cdi transaction started by Observes never actually ends but do something weird? Or it ends but doesn’t commit because he doesn’t know about EJB?


Answer:

persist() is not meant to insert the row immediately, but rather at flush time, which is usually immediately before transaction commit.

However, if this is not what you are asking, but the row is not inserted even after the transaction has ended, it may be that flush mode for that transaction is set to manual.

Question:

I'm working on project where I use mainly EJB and JPA, but I have problem with ConstraintViolationException that should not happen.

First of all I have MyEntity class with @Id and few unique fields. I have @Stateless MyEntityRepository class with find() method which just returns MyEntity (or null) by calling EntityManager get() method. Another @Stateless bean is SaveEntityBean:

@Stateless
public class SaveEntityBean {

    @Inject
    EntityManager em;
    @Inject
    MyEntityRepository repository;

    public void saveEntity(MyEntity me) {
        if(repository.find(me) == null) {
            //the place with ConstraintViolationException
            em.persist(me);
        }
    }
    public void saveEntities(List<...> entities) {
        for(MyEntity me: entities)
            saveEntity(e);
    }
}

and the method saveEntities(List<...> entities) is called from another bean:

@Stateless
@Startup
public class SaveEntityBean {

    @Inject
    SaveEntityBean saveBean;
    //...

    @Schedule(hour = "*", minute = "*", second="*/5")
    @AccessTimeout(unit = TimeUnit.MINUTES, value = 1)
    public void mainLogicMethod() {
        List<MyEntity> entities = io.calculateAndGetEntities();
        saveBean.saveEntities(entities);
    }
}

where the io.calculateAndGetEntities() method is long IO work. The problem is that sometimes I get org.hibernate.exception.ConstraintViolationException which in my opinion should not occur, because I check MyEntityRepository.find(me) != null condition before calling persist() method. The only idea I have is that there is some delay between commits, so after calling MyEntityRepository.find(me) method in condition check, the commit occurs, and just after that the persist() method throws the exception.

Please give me any suggestions what to read and learn and how to solve the problem.

edit: I found that it is a problem with threading, so probably the solution is with locking write/read.


Answer:

Ok, generally in a high concurrent enviroment it is really hard to solve this problem and simply Locking will not work(and You don't want to use table locks). For now I use Hibernate @SQLInsert annotation from org.hibernate.annotations.SQLInsert so my Entity looks like this:

@SQLInsert(sql = "INSERT INTO my_entity_table(a, b, c) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE a=a;")
public class MyEntity implements Serializable {...}

Of course it will be problematic, when You would like to show some error message to the user, but in my case it is good enough. As far as I know this SQL statement is supported only in MySQL, in other RDBMS you should use different aproach.