[JPA_SPEC-61] Retrieving primary key of lazy relationship Created: 18/Jun/13  Updated: 15/Apr/14

Status: Open
Project: jpa-spec
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major
Reporter: mkarg Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

JPA 2.0



 Description   

My server passes entities to clients as XML representations, simply by marshalling it using JAXB. This works well but there is one case where it fails: LAZY relationships. As I just want to include a URL for each reference (so clients can decide on their own whether to load this in turn), I need access to the primary key. But, as it is a LAZY reference, the attribute itself is NULL, and if I use the getter(), then the complete entity is instantiated – which I do not need and do not want. So it would be really great, if there would be solution, which makes JAXB and JPA work more closely. Like an API to get the primary keys of LAZY relationships, or even better, an automatic integration.



 Comments   
Comment by mkeith [ 18/Jun/13 ]

Markus,

With JPA 2.1 you could create an entity fetch graph for the related object and only load the PK. You would effectively have an unloaded object (except that it would have the PK) with its remaining state set to lazily load. There are some differences between this and having the whole object be lazily loaded since if the object ends up getting triggered then, depending upon the implementation, each of the attributes may end up getting loaded separately, but it could be configured to be fairly close I think. Of course, this would be a bit of a pain if you needed to do this for every lazy loaded attribute, but the advantage would be that it would also work pretty well for collection-oriented relationships.

-Mike

Comment by mkarg [ 19/Jun/13 ]

Unfortunately didn't find a tutorial on this on the web.

Currently I am doing this...

Foo f = em.find(Foo.class, myFooPK);
jaxbMarshaller.marshal(f);

...to get the root. Can you provide an example how to use that "fetch graph" thing to get the PKs of f's LAZY relations?

Thanks!
-Markus

Comment by mkeith [ 19/Jun/13 ]

From your first comment it sounded like you already had something (e.g. an XMLAdapter or some such thing?) on the relationship attribute to control marshalling a URL instead of the target Bar related object. Assuming that is the case (you need to stop the JAXB marshaller from continuing to traverse the Bar object) then you just need to access the PK of the Bar, right? If this is your usecase then you could do the following:

Define a named entity graph for Bar:
@Entity
public class Bar {
@Id long id;
public int getId()

{ return id; }


...
}

@XmlRootElement
@Entity
public class Foo

{ ... @XmlJavaTypeAdapter(MyBarToUrlAdapter.class) @OneToOne Bar bar; ... }

and then to read the Foo instance, you would do:

Map<String,Object> props = new HashMap<String,Object>();
props.put("javax.persistence.fetchgraph", em.createEntityGraph(Bar.class));
Foo f = em.find(Foo.class, myFooPK, props);
...

From within your adapter, or whatever you use to convert a Bar to a URL, you could get the id of the bar by calling getId() and the rest of the object will likely not be loaded (likely, because lazy does not imply not loaded). If this doesn't describe your problem then we can take this discussion offline.

Comment by arjan tijms [ 22/Jun/13 ]

It would be great if there was a way to just grab the PK of any entity, regardless of how it was loaded.

After all, we know the PK must be there as JPA uses it to lazy load the associated entity. It's just that the default mapping that we normally do with JPA doesn't give us access to this PK.

Maybe PersistenceUnitUtil would be an ideal place to put a utility method that can do this. It already contains functionality that's in the same category.

E.g.

Given

@Entity
public class Foo {

    @Id
    Long id;

    @OneToOne(fetch = LAZY)
    Bar bar;

    // + getters/setters
}

@Entity
public class Bar {

    @Id
    Long id;

    // + getters/setters
}

We could then grab the PK of Bar given an instance of Foo and an EntityManager em, as follows:

PersistenceUnitUtil persistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();

Long barId;
if (persistenceUnitUtil.isLoaded(foo, "bar") {
    barId = foo.getBar().getId();
} else {
    barId = persistenceUnitUtil.getId(foo, "bar");
}

Note that the if/else is just for the example here. PersistenceUnitUtil#getId should be able to grab the Id of a given relation independent of its loaded status.

An alternative would be to explicitly map an extra instance field in Foo to the FK column in the table to which Foo is mapped, and then use a proprietary "read only" annotation, e.g.

@Entity
public class Foo {

    @Id
    Long id;

    @OneToOne(fetch = LAZY)
    Bar bar;

    @Column(name = "bar_id")
    @ReadOnly // made-up proprietary annotation, but many providers have something like this
    Long barId;

    // + getters/setters
}

I've seen this workaround actually being used in practice, but it's not so nice of course.

Comment by c.beikov [ 15/Apr/14 ]

@arjan: Which persistence provider are you using? In hibernate this is no problem and I also don't see why other persistence providers would load the entity just because you access the id. I don't know if the spec says anything about the behavior in that case but it would definitely be nice for portability reasons to have a defined behavior.

Generated at Sun Mar 29 10:11:55 UTC 2015 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.