views:

128

answers:

1

My problem is very similar to this one : Injecting fields via Spring into entities loaded by Hibernate

The difference is that , I am using JPA2 entities , not hibernate . While the underlayer is still hibernate (3.5.5).

My spring version is 3.0.4.

What's the corresponding eventListeners in JPA's world ?

Sample code from the original post :

class Student {
   int id; //loaded from DB
   String name; //loaded from DB
   int injectedProperty; //Inject via Spring
   transient Service serviceImpl; //Inject via Spring
}

I know there may be aspectJ's solutions , but I'd rather like a pure-java solution. Thanks.

+1  A: 

What's the corresponding eventListeners in JPA's world ?

JPA has Entity Listeners (and Callback Methods). From the JPA 2.0 specification:

3.5 Entity Listeners and Callback Methods

A method may be designated as a lifecycle callback method to receive notification of entity lifecycle events. A lifecycle callback method can be defined on an entity class, a mapped superclass, or an entity listener class associated with an entity or mapped superclass. An entity listener class is a class whose methods are invoked in response to lifecycle events on an entity. Any number of entity listener classes can be defined for an entity class or mapped superclass.

(...)

The entity listener class must have a public no-arg constructor.

Entity listeners are stateless. The lifecycle of an entity listener is unspecified.

The following rules apply to lifecycle callbacks:

  • Lifecycle callback methods may throw unchecked/runtime exceptions. A runtime exception thrown by a callback method that executes within a transaction causes that transaction to be marked for rollback.
  • Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.
  • In general, the lifecycle method of a portable application should not invoke EntityMan- ager or Query operations, access other entity instances, or modify relationships within the same persistence context. A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.

(...)

3.5.1 Lifecycle Callback Methods

Entity lifecycle callback methods can be defined on an entity listener class and/or directly on an entity class or mapped superclass.

Lifecycle callback methods are annotated with annotations designating the callback events for which they are invoked or are mapped to the callback event using the XML descriptor.

The annotations used for callback methods on the entity class or mapped superclass and for callback methods on the entity listener class are the same. The signatures of individual methods, however, differ.

Callback methods defined on an entity class or mapped superclass have the following signature:

void <METHOD>()

Callback methods defined on an entity listener class have the following signature:

void <METHOD>(Object)

The Object argument is the entity instance for which the callback method is invoked. It may be declared as the actual entity type.

The callback methods can have public, private, protected, or package level access, but must not be static or final.

(...)

Here is an example (from the spec):

@Entity
@EntityListeners(com.acme.AlertMonitor.class)
    public class Account {
    Long accountId;
    Integer balance;
    boolean preferred;
    @Id
    public Long getAccountId() { ... }
    ...
    public Integer getBalance() { ... }
    ...
    @Transient // because status depends upon non-persistent context
    public boolean isPreferred() { ... }
    ...
    public void deposit(Integer amount) { ... }
    public Integer withdraw(Integer amount) throws NSFException {... }

    @PrePersist
    protected void validateCreate() {
        if (getBalance() < MIN_REQUIRED_BALANCE)
            throw new AccountException("Insufficient balance to open an account");
    }

    @PostLoad
    protected void adjustPreferredStatus() {
        preferred = (getBalance() >= AccountManager.getPreferredStatusLevel());
    }
}

public class AlertMonitor {
    @PostPersist
    public void newAccountAlert(Account acct) {
        Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
    }
}

In your case, you'll probably want PostLoad callback methods.

See also the Chapter 6. Entity listeners and Callback methods in the Hibernate Entity Manager documentation.

But to put it simply, what you want to do is not that simple with JPA and using AspectJ class weaving with the @Configurable annotation might be the best option.

References

  • JPA 2.0 Specification
    • Section 3.5 "Entity Listeners and Callback Methods"
Pascal Thivent
Hi , thanks. But I still cannot inject a service object into the 'AlertMonitor' in this example.
smallufo
@smallufo The Entity Listener is not a Spring bean so you'll have to use either the `@Configurable` annotation (so you just moved the problem to the listerner) or perform a "lookup".
Pascal Thivent
@smallufo Just in case, also have a look at http://www.jblewitt.com/blog/?p=129. But to put it simply, what you want to do is not that easy to do with JPA.
Pascal Thivent
Thank you , it seems aspectj is the only solution now , and it's another framework binding to the Entity class ... *sigh* ... Hoping future spec can address this problem.
smallufo