Hibernate, alter identifier/primary key

I receive the following exception when I'm trying to alter my @ID in an @Entity.

identifier of an instance of com.google.search.pagerank.ItemEntity was altered from 1 to 2.

I know that I'm altering the primary key in my table. I'm using JPA-annotations.

I solved this by using this single HQL query: update Table set name=:newName where name=:oldName

Instead of using the more OO approach:

beginTransaction();
T e = session.load(...);
e.setName(newName);
session.saveOrUdate(e);
commit();

Any idea what the diff is?


Solution 1:

Actually, according to the JPA specification it is forbidden to change a primary key:

The application must not change the value of the primary key[8]. The behavior is undefined if this occurs.[9]

(from EJB 3 persistence (JPA) specification, paragraph 2.1.4)

Solution 2:

I can't imagine why you'd want to do that. At all. Why would you change an entity's identity? You'd also need to update all the foreign keys in other tables that point to it. Seems like a pain, with no gain. You're probably better off making this a "business key" (plain property) and using a more permanent surrogate key. I have a feeling that you're going about this all wrong, but if you insist...

Essentially what you're doing is creating a new Customer and deleting the old one, and that's how I'd accomplish it in Hibernate.

[pseudocode]

Begin Transaction

// create new customer from old
newC = Session.Load<Customer>(42)
Session.Evict(newC)
newC.Id = 1492
Session.Save(newC)

// update other relationships to point to newC
// ....

// delete old customer
oldC = Session.Load<Customer>(42)
Session.Delete(oldC)

Commit Transaction

However, you're probably better off just doing it in all at once in a plain single SQL transaction, and in either case you risk having parallel processes that already have an instance of the "old" Customer, which might cause some errors.

Solution 3:

My friend, you are right. There is no way (or I don't know a way) to change primary key without using HQL statements. It's not normal, but it's true.

Solution 4:

This is not a solution, as I believe that has been answered above.

However, I wanted to offer what I considered to be a valid scenario for wanting to do this. (Comments welcome)

1) Table USER_ELECTRONICS has a many-to-many with Table ELECTRONICS_DEF

2) This many-to-many is an ordered list. Each USER_ELECTRONIC_ID has a prioritized list of ELECTRONICS_DEF_IDs. For example, I have an IPhone, an IPad, Cell Phone, and I rank them in order of how much I like them. To accomplish this, I have USER_ELEC_PRIORITY_MAP table, that looks like this -

USER_ELEC_ID ELEC_DEF_ID PRIORITY

1 IPAD 1
1 IPHONE 2
1 CELL 3

USER_ELEC_ID, and ELEC_DEF_ID are PFKs. PRIORITY is Composite FK.

3) When I want to reoder the table in terms of my preferences (My new IPad2 is top on the list, and iPad moves to #4), I need to update part of the Composite FK (PRIORITY).