Changing the type of an entity preserving its ID

牧云@^-^@ 提交于 2019-11-30 13:32:53

Hibernate attempts to make persistence as transparent as it possibly can - which means it tries to follow the same principles as normal Java objects. Now, rephrasing your question in Java, you'd get:

How can I convert an instance of B class into an instance of (incompatible) C class?

And you know the answer to that - you can't. You can create a new instance of C and copy necessary attributes, but B will always be B, never C. Thus the answer to your original question is - it cannot be done via JPA or Hibernate API.

However, unlike plain Java, with Hibernate you can cheat :-) InheritanceType.SINGLE_TABLE is mapped using @DiscriminatorColumn and in order to convert B into C you need to update its value from whatever's specified for B into whatever's specified for C. The trick is - you cannot do it using Hibernate API; you need to do it via plain SQL. You can, however, map this update statement as named SQL query and execute it using Hibernate facilities.

The algorithm, therefore, is:

  1. Evict B from session if it's there (this is important)
  2. Execute your named query.
  3. Load what-is-now-known-as-C using former B's id.
  4. Update / set attributes as needed.
  5. Persist C

In this case, "c" is an object which the hibernate session knows nothing about, but it has an ID, so it assumes that the object has already been persisted. In that context, persist() makes no sense, and so it fails.

The javadoc for Hibernate Session.persist() (I know you're not using the Hibernate API, but the semantics are the same, and the hibernate docs are better) says "Make a transient instance persistent". If your object already has an ID, it's not transient. Instead, it thinks it's a detached instance (i.e. an instance that has been persisted, but is not associated with the current session).

I suggest you try merge() instead of persist().

You can use your own id (not generated) and do the following:

  1. Retrive B
  2. open transaction
  3. delete B
  4. commit the transaction
  5. open a new transaction
  6. create C and persist it
  7. Close the second transaction

In this way you will clear the id from the table before re-inserting it as C.

How do you distinguish between the two entities in the table? I assume that there some field value (or values) that you can change to make B into a C?

You could have a method where you load the super-class A and change the distinguishing values and save. Then in your next Hibernate Session your B will be a C.

I think skaffman is right here, once the id has been set it won't persist, and further because the id is generated it expects the sequence to be in charge of assigning the id number.

You could possibly not put the id as @GeneratedValue? or one of the different generator strategy types maybe to avoid the merge generating a new sequence value, but I suspect that would be problematic.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!