EJB - retrieving the entity you've just persisted (with autogenerated key)

S

ses

I assume this must be a fairly common task but I'm struggling to find
the best way to achieve this.

How do you best retrieve the entity you've just persisted with
entityManager.persist(myEntity), so that you can then use it in the
next part of a transaction.

I'm referring to an entity for which the container or database manages
a generated primary key. Therefore you want to retrieve what you've
just persisted so that you can for example establish another
relationship - e.g. anotherEntity.add(myEntity) then
entityManager.merge(anotherEntity).

I am trying to do this in a session bean method and carry it out all
as one transaction. The way I am retrieving myEntity after persisting
is simply selecting the entity with the highest key value (is simple
auto increment in MySQL) but there is some problem with this. It
retrieves the entity fine with its newly generated key, however when I
try to merge it or merge another entity with which I establish a
relationship to myEntity I get the following exception:

"null primary key encountered in unit of work"

Any help would be much appreciated, I'm really asking if there is a
better way to do this (persist-find-merge in a transactional EJB
session bean method doesn't seem to be a good pattern, or at least
doesn't seem to be working for me!)
 
D

Daniele Futtorovic

I assume this must be a fairly common task but I'm struggling to find
the best way to achieve this.

How do you best retrieve the entity you've just persisted with
entityManager.persist(myEntity), so that you can then use it in the
next part of a transaction.

I'm referring to an entity for which the container or database manages
a generated primary key. Therefore you want to retrieve what you've
just persisted so that you can for example establish another
relationship - e.g. anotherEntity.add(myEntity) then
entityManager.merge(anotherEntity).

I am trying to do this in a session bean method and carry it out all
as one transaction. The way I am retrieving myEntity after persisting
is simply selecting the entity with the highest key value (is simple
auto increment in MySQL) but there is some problem with this. It
retrieves the entity fine with its newly generated key, however when I
try to merge it or merge another entity with which I establish a
relationship to myEntity I get the following exception:

"null primary key encountered in unit of work"

Any help would be much appreciated, I'm really asking if there is a
better way to do this (persist-find-merge in a transactional EJB
session bean method doesn't seem to be a good pattern, or at least
doesn't seem to be working for me!)

Not sure about EJB, but in pure Hibernate I'd use #merge(Object) for
this -- and then work on the returned instance.

HTH,
 
S

ses

Not sure about EJB, but in pure Hibernate I'd use #merge(Object) for
this -- and then work on the returned instance.

HTH,

I don't think you can merge in JPA before you've called persist, or do
you mean call merge after persist?
 
D

Daniele Futtorovic

On Feb 24, 6:35 pm, Daniele Futtorovic<da.futt.n...@laposte-dot-
I don't think you can merge in JPA before you've called persist, or do
you mean call merge after persist?

As I said, I have only a vague notion of the technology you're
describing, but in Hibernate, the interface of which looks very similar
to this EntityManager, I would use merge _instead of_ (the equivalent
of) persist. #merge is supposed to persist if the entity does not yet
exist, and update it if it does. Merging a 'blank' entity returns me a
(copy of a) persisted entity with the newly assigned identifier set.
 
L

Lew

ses said:
I don't think you can merge in JPA before you've called persist, or do
you mean call merge after persist?

Sure you can. I've done it many times and it's worked.

There are subtleties of the semantics, but you can do it.
 
A

Arved Sandstrom

I don't think you can merge in JPA before you've called persist, or do
you mean call merge after persist?

For a good discussion, see
http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/.
I particularly recommend "The Pattern" section at the bottom. I'd reword
the second piece of advice as

"When updating an existing *managed* entity, we do not invoke any
EntityManager method; the JPA provider will automatically update the
database at flush or commit time."

This ties in better with the third guideline. Main point is, and this is
something that practically all of us have done when starting out with
JPA, you don't need to call merge() or persist() to "save" changes to a
managed entity. That's one of the main reasons we've got the EntityManager.

The persistence specifications (JSR 220 and/or JSR 317) are your
authoritative references. They are very readable specs.

I'm not sure I understand your specific problem. You're in the same
transaction - there's no way in your code of passing the newly-persisted
and now managed entity to the next spot that needs to do something with
it? What I'm saying is, I don't get the "find" part of
persist-find-merge...in fact I'm not even convinced you need the "merge"
in your scenario.

As an aside, it seems you're using IDENTITY as your ID generation
strategy. Is that so?

AHS
 
S

ses

For a good discussion, seehttp://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-d....
I particularly recommend "The Pattern" section at the bottom. I'd reword
the second piece of advice as

"When updating an existing *managed* entity, we do not invoke any
EntityManager method; the JPA provider will automatically update the
database at flush or commit time."

This ties in better with the third guideline. Main point is, and this is
something that practically all of us have done when starting out with
JPA, you don't need to call merge() or persist() to "save" changes to a
managed entity. That's one of the main reasons we've got the EntityManager.

The persistence specifications (JSR 220 and/or JSR 317) are your
authoritative references. They are very readable specs.

I'm not sure I understand your specific problem. You're in the same
transaction - there's no way in your code of passing the newly-persisted
and now managed entity to the next spot that needs to do something with
it? What I'm saying is, I don't get the "find" part of
persist-find-merge...in fact I'm not even convinced you need the "merge"
in your scenario.

As an aside, it seems you're using IDENTITY as your ID generation
strategy. Is that so?

AHS

Thanks for that link, it is very useful. Because I'm using a detached
entity I think merge will be suitable for what I need.

In actual fact it is a relationship that is causing the error - I have
a structure like this:

User --> Location --> Booking

Where --> depicts a one-to-many unidirectional relationship. I am
trying to persist a new Location (which has zero Bookings), then add
it to a User and merge the User. The problem is the relationship
between Location and Booking - if I remove this relationship (the
field and the get and set from the Location entity) then it works
fine. But obviously I want this relationship to remain so I can in the
future add bookings to the location.

To reiterate, the exception is:

"null primary key encountered in unit of work"

I thought the problem was Location had a null primary key / id after
persisting, but I've proved this not to be the case by logging the id
and it seems clear to me now it is the Location --> Booking
relationship that is the issue.
 
S

ses

Thanks for that link, it is very useful. Because I'm using a detached
entity I think merge will be suitable for what I need.

In actual fact it is a relationship that is causing the error - I have
a structure like this:

User --> Location --> Booking

Where --> depicts a one-to-many unidirectional relationship. I am
trying to persist a new Location (which has zero Bookings), then add
it to a User and merge the User. The problem is the relationship
between Location and Booking - if I remove this relationship (the
field and the get and set from the Location entity) then it works
fine. But obviously I want this relationship to remain so I can in the
future add bookings to the location.

To reiterate, the exception is:

"null primary key encountered in unit of work"

I thought the problem was Location had a null primary key / id after
persisting, but I've proved this not to be the case by logging the id
and it seems clear to me now it is the Location --> Booking
relationship that is the issue.

I think I have now resolved the issue, it was simply that I hadn't
added the @GeneratedValue annotation to the id columns with
@GeneratedValue(GenerationStrategy.IDENTITY)

I didn't realise this was required in order for the persistence
manager to retrieve the ID in the case that the database generates it
(rather than the container).
 
A

Arved Sandstrom

On 11-02-25 10:47 AM, ses wrote:
[ SNIP ]
I think I have now resolved the issue, it was simply that I hadn't
added the @GeneratedValue annotation to the id columns with
@GeneratedValue(GenerationStrategy.IDENTITY)

I didn't realise this was required in order for the persistence
manager to retrieve the ID in the case that the database generates it
(rather than the container).

That's sort of why I asked whether you were using IDENTITY; I had my
suspicions as soon as you mentioned "null primary key".

http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequence_Strategies
is a decent discussion. You'll note upon reading here, that if you're
using IDENTITY it has some things to look out for, one of them being
that you won't have an ID until after you flush or commit.

Not having a @GeneratedValue at all is OK, which is what you started out
with. One app I help maintain has a few audit-type entities set up this
way; the @Id values are GUIDs created in a @PrePersist method. One way
or the other, either by the application or by a @GeneratedValue, you
obviously have to set (or have set) the ID somehow.

AHS
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top