简体   繁体   中英

Spring Data MongoDB Cross-Store plugin and JPA PrePersist and PreUpdate

We have an application with the following set up:

Java 6.0 Spring Data JPA 1.1.0.RELEASE Spring Data MongoDB 1.0.2.RELEASE Spring Data MongoDB Cross-Store 1.0.2.RELEASE Hibernate JPA 2.0

We have several classes in this application that use the JPA PrePersist, PreUpdate, PostPersist and PostUpdate annotations. An example is given below.

@Entity
public class Person
{
    private String password;

    @PrePersist
    @PreUpdate
    public void beforeSave()
    {
        if(!Security.isEncrypted(this.password))
        {
            this.password = Security.encrypt(this.password);
        }
    }
}

As soon as we turn on AspectJ weaving for the cross-store plugin, the Spring application context fails to load with the error:

Caused by: javax.persistence.PersistenceException: You can only annotate one callback method with javax.persistence.PrePersist in bean class: org.example.domain.Person
    at org.hibernate.ejb.event.CallbackResolver.resolveCallback(CallbackResolver.java:110)
    at org.hibernate.ejb.event.EntityCallbackHandler.addCallback(EntityCallbackHandler.java:123)
    at org.hibernate.ejb.event.EntityCallbackHandler.add(EntityCallbackHandler.java:61)
    at org.hibernate.ejb.event.JpaIntegrator.integrate(JpaIntegrator.java:151)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:306)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1744)
    at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:94)
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:905)
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:890)
    at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)

I have found out that the root cause for the error is that the Aspect MongoDocumentBacking weaves additional PrePersist and PreUpdate methods into entity classes. Since the classes already have methods with these annotations, Hibernate Entity Manager fails to validate these classes.

Is there any guidance on how the cross-store plugin should be used with applications that have existing code that use JPA annotations?

I was facing the same issue with @PreUpdate and @PostLoad.

There is a bug opened in springsource about this: https://jira.springsource.org/browse/DATAMONGO-519

They have given the solution below:

  1. Create JPA entity event listener classes and move PrePersist, PreUpdate, etc. code into these listeners.
  2. Change the aspects to first search whether an entity class has any field annotated as RelatedDocument.
  3. If an entity class has one or more fields annotated as RelatedDocument, check whether the class already has EventListeners annotation.
  4. If the EventListeners annotation is already present, add cross-store event listeners to the list. If not, add the EventListeners annotation to the class.

I moved all my annotations to an entity listener and it worked straight away, no need to change anything else. Have a look at the following link, it seems that only one annotation of the same time can be added at the entity level, but many can be added using entity listeners: http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/listeners.html

@Entity
@EntityListeners(ProductEntityListener.class)
public class Product {
}

public class ProductEntityListener {
    @PrePersist
    @PreUpdate
    protected void prePersist(Product entity) {
    }
    @PostLoad
    protected void postLoad(Product entity){
    }
}

We had the same issue here:

an EntityListener implemented an Interface with a generic parameter. After debugging the Hibernate code that threw the exception it turned out that it is not possible to use Generics in this listener interface for the entity type. After removing the generic parameter it worked.

Reason: When using a generic parameter in the interface the java compiler creates (for example) one postUpdate(...) -method for the implementing class (concrete type) and one for the interface (super type). Hibernate detects those 2, but expects only one. As a consequence it throws the exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM