Discussions

News: Are the JPA callback methods useful?

  1. Are the JPA callback methods useful? (10 messages)

    Definitely no. The section 3.5 of JPA specification states:
    “In general, the lifecycle method of a portable application should not invoke EntityManager 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.”
    Surely these restrictions has a good technical reason behind them, but from a business application developer perspective they mean that JPA callback methods are practically useless.
    For example, these scenarios are typical:

    • In order to remove some entity we need to verify if some data exists, and we want do it using a JPA query.
    • When an entity is saved, some other entities must be automatically created and saved, and we want to use the JPA EntityManager to do so.


    Unfortunately, it’s difficult to solve such cases using the standard annotations: @PrePersist, @PostPersist, @PreRemove, @PostRemove, @PreUpdate, @PostUpdate or @PostLoad.

    What can we do?

    We have several options such as:

    • Using JDBC from the callback methods: Horror!
    • Create a new EntityManager in the callback method: This works sometimes, but you can have problems with isolation levels. Moreover, you lose the transactional behavior.
    • Put the on-save or on-remove logic in the controller layer, that is in the actions: Of course, this works just fine, but if you access to the entities from other actions, from a batch process, or from a web service, the on-logic or on-remove will not be executed.


    Obviously, these options are dirty and unnatural, and even even worse, they mean more work for us.

    Create your own callback annotations

    In OpenXava, we have opted for the simplest solution for the poor application developer, just creating some new callback annotations that allow to use JPA inside them. OpenXava 4.0.1 includes the next new annotations: @PreCreate, @PostCreate and @PreDelete.
    For example, if we need to create a customer and assign it to an invoice when the customer is not specified, you can write::
    @PreCreate
    public void onPreCreate() {
      // Automatically create a new customer
      if (getCustomer() == null) {
          Customer cust = new Customer();
          cust.setName(getName());
          cust.setAddress(getAddress());
          cust = XPersistence.getManager().merge(cust); // Here we use the EntityManager
          setCustomer(cust); // and here we change a relationship
      }
    }
    If you want to enjoy these annotations just use OpenXava for developing your application. Although if you are not still ready for rapid development, you can create these annotations yourself easily, just use the decorator pattern over the EntityManager or use AOP to refine the behavior of persist() and remove() methods.

    Learn more about these annotations

    Threaded Messages (10)

  2. I absolutely don't like the idea of methods in an entity that are using the entity manager on their own account.

    What I find far more troublesome is that I'm not allowed to itterate over (lazy-loaded) collections in a JPA entity during a call back like @PostLoad.

    Often I would like to do such inspection to e.g. initialize a few non-persistent properties, but now I have to do this after I get the entity back from the entity manager. @PostLoad is thus not really post load, but a-little-before-post-load.

     

  3. I absolutely don't like the idea of methods in an entity that are using the entity manager on their own account.

    I see your point. You do not want any persistence relate code inside the entity. This is practical if you, for example, want to change from JPA to other persistence technology in the future. But if that is not the case, mixing business logic and persistence logic inside the entity can be practical sometimes, because it allows you to write code that can be used in the next way:

    order.createInvoice(); // Create an invoice, saving it in the database, throwing all validations and callback methods if needed

     

  4. EclipseLink native callbacks[ Go to top ]

    My BM is completely stuffed with persistence code; I chose for JPA as the persistency layer and I'm going to use it fully. This means I want my BM to take care of itself as independently as possible; making sure entities are consistent, related entities are modified, independent of whether that call came from the Swing frontend, the EDIFACT communication interface or a webservice. 

    Callbacks really are an important part of that, and I've run into the problems described in the article very often. Depending on where the EM is with its processing, actions in events may or may not be executed. So for example a property change on one entity may be included while another is already processed and the setter is ignored.

    ATM I've bolted the EclipseLink native callbacks to trigger my JPA callbacks. These callback seem to have less problems, but still are not perfect.

    One of the biggeste improvements for JPA (3.0) should be this IMHO.

  5. order.createInvoice(); // Create an invoice, saving it in the database, throwing all validations and callback methods if needed

    invoiceService.createInvoice(order)?

    It's a bit of a stylistic issue though, you can have fat or slim entities. Slim entities are more akin to DTOs. They are very lightweight and can be easily transferred between layers. Fat entities carry their own dependencies with them and are more difficult to transfer to other layers.

    Although neither approach is *bad* (they are just choices each of them having a rationale), I did found that the fat entity pattern can lead to abuses like user.sendInvoice(), user.subscribe(), user.XYZ.. 

    This leads to an overstuffed User object. The idea is then that everything is eventually done for some user, so every operation known by the application is implemented as a method on the User class. Instead of having a SendService that takes a User as a recipient and an invoice as the item to be send, is much more flexible than hardcoding such a send method in the user.

    It also much more closely resembles objects in the real world.

    In the real world, if want to send an invoice to you, I don't ask you to do this (user.sendInvoice()). Instead, I'll type an invoice (Invoice invoice = new Invoice(); ...), then I look up your details (User user = getUser();) and then I go to the post office, where I hand them the invoice and your details (postOffice.send(invoice, user);).

     

  6. 100% agreeing with commnt[ Go to top ]

    A cookie shouldn't be asked to bake itself ;-)

  7. True but[ Go to top ]

    A cookie shouldn't be asked to bake itself ;-)

    True.... but the world would be much simpler if a cookie can bake itself, you wouldn't need to worry about the oven and its setting..

     

    I suppose at the end of the day, like everything else, there is no one true, absolutely correct, all else is a fail, way of doing things.  If there is, we would be training chimps to do it... 

  8.  

     

     

     

     

     

     

    invoiceService.createInvoice(order)?

    For me, that is not true OOP. I wrote code like that with C, or using Stateless Session beans and DTOs, but that is not OOP. In OOP the methods to manage some data must be in the same class of the data.

    Anyways, JPA should not preclude you to use one or other approach.

     

  9. Oop[ Go to top ]

    invoiceService.createInvoice(order)?

    For me, that is not true OOP. I wrote code like that with C, or using Stateless Session beans and DTOs, but that is not OOP.

    As I mentioned, there are two possible sides to this and neither has to be really bad. Fat entities may lead to overstuffed classes. As Yannick Majoros indicated, cookies just don't bake them selves. Unless developers are very careful (most aren't) they very often mess up responsibilities in trying to be "true OOP".

    On the other hand, slim entities do emphasize C style code a little at first glance. Slim entities sometimes are not that much more than glorified Structs.

    In OOP the methods to manage some data must be in the same class of the data.

    This is not entirely true. There are many patterns in OOP on how objects collaborate. Just think what a well-known pattern as the visitor pattern is about. This is all about separating the algorithm from the objects it works on. This too is absolutely OOP.

    OOP is also about polymorphism, hiding dependencies and contracts.

    While stateless session beans operating on entities may at first glance look like procedural C code operating on structs, it actually embodies quite a lot of intricate OOP machinery.

    For starters, I don't work with something like a static method when I call a method on a stateless session bean. I get in fact an interface (typical OOP element). This interface separates the contract from the actual implementation (OOP). My call first goes to a dynamic proxy (how more OOP can you get?) that decorates (local) or marshalls to (remote) the actual bean.

    The actual bean has references (dependencies) to other resources. Such resources are things like the EntityManager, other EJBs, etc. Those resources are completely hidden from the client (encapsulation, another OOP element). Although my client calls a single public method, this single public method may call private methods on itself. Those methods are of course shielded from the client (another OOP element) and those methods access the instance state from the stateless session bean (again, OOP). (Stateless in Stateless session beans only applies to what the client can assume between invocations, not that the bean doesn't actually have state).

    The bean can implement multiple interfaces, to expose different behavior to different clients (yet again, OOP), and the bean can inherit from another bean (OOP again).

    Slim entities on their turn can perfectly well be part of an inheritance hierarchy (OOP). JPA supports inheritance fully. I can have a List<Customer> (OOP), where every Customer may actually be a ReturningCustomer or an InternalCustomer. An InternalCustomer may be a RedCustomer or a GreenCustomer. Etc etc. This is perfectly well OOP.

    I can pass this List<Customer> to my session bean, which can persist it using the entity manager, without knowing the exact type of each Customer (OOP). Individual Customer types may have different ways to get a discount percentage, but my service code can call customer.getDiscountPercentage without knowing about the exact details (OOP).

    So as you see, even without needing a method customer.sendInvoice(), that has internal dependencies on some mailing system, I still have a *lot* of intricate OOP-stuff going on that I would *not* be able to do such elegantly in C code with Structs.

    Anyways, JPA should not preclude you to use one or other approach.

    It maybe shouldn't, and you're in luck as JPA 2.1 won't preclude you to use the other approach anymore ;)

     

  10. Good news from JSR-338 (JPA 2.1)[ Go to top ]

    The proposal for JPA 2.1 specification includes:

    "Additional event listeners and callback methods; availability of entity manager to callbacks."

  11. What about call an EJB[ Go to top ]

    In order to solve this problem, what about calling an EJB which content the business logic. we know that the callback method share the same transaction context with the caller. if we pass data to save to the EJB method with madatory transaction attribute, the data can be save in the same time as the entity to which the event was fired