Adam Bien's Weblog

Tuesday Mar 25, 2008

Why sometimes a reference to an EntityManager inside JPA Entities is needed

In all of my more complex projects, we had to reference  the EntityManager from inside the JPA-Entities. Especially in rich - so entities with business logic, we had to reference the EntityManager somehow. The requirements for referencings the EntityManager were various - and because of NDAs (I'm working mainly as self-employed consultant) hard to share :-) . However I ran into similar issues in a demo application from http://p4j5.dev.java.net as well. The RunAndBike application manages the training unit. BikingUnits  - as well as RunningUnits (both inherit from Unit).

The JPA-Entity Season represents a Training Season (e.g. all units of the year 2008) and is associated with the User. It holds a reference to N abstract Units:

@Entity
@Table(name="TB_SEASON")
public class Season implements Serializable {
   
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long seasonId;

    @Column(name="season_year")
    private int year;

    @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
    @OrderBy("trainingBegin DESC")
    private List<Unit> trainingUnits;

//lot of code omitted 

}

So you can store BikingUnit:

public class BikingUnit extends Unit {

    private int avgCadence;
    @Enumerated(EnumType.STRING)
    @Column(name="t_type")
    private TrainingType type;
   
    @ManyToOne(cascade=CascadeType.ALL)
    private Bike bike;

//some code omitted
 }

or RunningUnit in it:

@Entity
@NamedQuery(name=RunningUnit.ALL_UNITS,query="Select r From RunningUnit r")
public class RunningUnit extends Unit {
   
    @ManyToOne(cascade=CascadeType.ALL)
    private Shoes shoes;

//some code omitted
 }

 with their specific states and behaviors.

So far, everything works really well. You can manage your units conveniently. Beyond that this solution is extendible, because you could easily introduce just another entity which inherits from the abstract Unit (I got already some request from triathletes for swimming....).

However I just wanted to provide really basic report functionality and compute the average speed. In that particular case it the average speed of Biking and Running Units should be computed separetely (it is not very impressive to have 25km avg speed on a bike, but is noteworthy to be able to run such fast).

To achieve this, I have to filter out all Running/Biking units first in the Season entity: 

    public List<RunningUnit> getRunningUnits() {
        List<RunningUnit> retVal = new ArrayList<RunningUnit>();
        for (Unit unit : getTrainingUnits()) {
            if (unit instanceof RunningUnit)
                retVal.add((RunningUnit) unit);
        }
        Collections.sort(retVal, new UnitComparator());
        return retVal;
    }
It works fine, because you will hardly have more, than 356 units a year - so it is not a huge problem. In case we would have several thousands units - we would run into performance / memory issue with this approach. In that case it would be more appropriate to query the database and not traverse the objects. ...but to access the database, we would need an EntityManager - but it cannot be injected yet. In all of my projects we stored it somehow, mostly in a ThreadLocal.

In real world projects, we needed the access to the EntityManager because of some historization, searches and filtering (mostly because of performance optimization). I always had a bad feeling - because obtaining a reference from an JPA-Entities through a ThreadLocal seemed always a "hack" to me.

Other bloggers ran in the issue as well. In Frank Cornelis wish list, he suggest a query extension:

 

@Entity
@NamedQueries(@NamedQuery(name="byName", query="FROM MyEntity WHERE name = :name"))
public class MyEntity {
@Id
String name;

// ...

public static Query createQueryWhereName(EntityManager em, String name) {
Query query = em.createNamedQuery("byName");
query.setParameter("name", name);
return query;
}
}

The question remains -> how the EntityManager is going to be obtained in his case?

I try to push this issue in my work as EG in JPA 2.0 (JSR-317) -> so if you had similar issues just leave a comment here or write a feedback to the group. You can download the whole application from http://p4j5.dev.java.net (from SVN). The project name is: RunAndBikeApplication (the EAR), and RunAndBikeDB (both Netbeans 6.0 projects, tested on Glassfish v2). The application is available online as well (but slow because of bandwidth).

One solution could be the introduction of a specific LifecycleListener and Lifecycle Call Back methods like:


@PostAttach
public void postAttach(EntityManager manager){

}

@PreDetach
public void preDetach(){}

I will cover the issue of Rich Domain Models and JPA in one of the upcoming issues of the german magazine JavaSPEKTRUM in more detail as well. You can dowload the part 2 for free.

Any thoughts?


[my tweets]  Rss My book: Real World Java EE - Rethinking Best Practices

Kommentare:

Hi Adam,

The solution for strong typed queries would be LINQ for Java ;-)
Do you know if there are any advances to have such functionality in Java?

Greetings, Simon

Gesendet von Simon Martinelli am March 25, 2008 at 11:42 AM CET #

Well, it must be a sort of telepathy (maybe due to the NBDT)? :-) I've just written some code where entities reference the EM, and I was wondering whether it is good or bad.

Indeed my case is different, so I've got still some doubts. Basically I have some entities which represents different properties to be stored in the db. They have many properties in common, with one which is polymorphic, so they are in a hierarchy. One special kind of property is an array, which in turn contains other entities (Composite pattern) as each element of the array must become a record in the database.

When I have to insert a detached entity of course I have to call em.persist(e), but in case of arrays this must be done for all the elements. In the end, I defined a persist() method directly on the entities, which is polymorphic. The EntityManager is retrieved by ThreadLocal.
https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/blueMarine-core/Metadata/MetadataJPAPersistence/src/it/tidalwave/metadata/persistence/jpa/entity

Gesendet von Fabrizio Giudici am March 25, 2008 at 12:00 PM CET #

Hi Simon,

I experiment with http://qlb.dev.java.net -> if you would like to contribute - just let me know :-),

thanks for your comment,

regards,

adam

Gesendet von Adam Bien am March 25, 2008 at 08:35 PM CET #

Hi Fabrizio,

perhaps in your case, you could rely on cascading. You could use the merge method instead of persist as well. In that case it will create non-existing and update existing entities.

See you at J1! && blueMarine is great.

It is not telepathy - it is the chip which they injected us :-),

regards,

adam

Gesendet von Adam Bien am March 26, 2008 at 10:15 AM CET #

Hi. :-) BTW, I missed QLB among your many projects and activities and I'm going to have a look at it, as it could be really useful for me.

Gesendet von Fabrizio Giudici am March 26, 2008 at 01:25 PM CET #

Hi Adam,

I had a look at QLB and I think it's a good aproach.
Do you know Quaere? http://quaere.codehaus.org/

I wrote an article in my blog comparing Quaere with .NET LINQ:
http://simonmartinelli.blogspot.com/2008/03/quaere-linq.html

In my opinion LINQ is an extremely sexy approach an fully type safe! Unfortunately LINQ requires a lot of changes in the programing language.

I think JPA 2.0 will provide a criteria language, thats also pseudo type safe.

Regards, Simon

Gesendet von Simon Martinelli am March 26, 2008 at 02:45 PM CET #

Senden Sie einen Kommentar:
  • HTML Syntax: Ausgeschaltet
Interviews/About
My Recent Book
Java One 2009
CommunityOne East N.Y.C
JavaONE 2008 Interview
Search
...the last 150 posts
...the last 10 comments
greenfire.dev.java.net
Links
my.netbeans.org
Visitors
License