What you're really trying to do is to change the class of an Entity object. Obviously this isn't something supported by Java nor by JPA. In EclipseLink it may be possible to hammer a discriminator column, invalidate the affected object in the cache, and then reread it. That could work but with a large number of caveats including ensuring you hold no references to the transformed object in your persistence context.
Instead, I'd suggest that if your domain model includes the ability to change the class of an Entity you refactor your model to use a delegation/role modeling approach rather than using inheritance. This allows you to change roles at runtime and even to have multiple concurrent roles (e.g., person could have a fireman role as well as a spouse role) and would behave accordingly.
Here's a simple example:
public enum PetType {
CAT { String speak() { return "meow"; } },
DOG { String speak() { return "woof"; } };
abstract String speak();
}
@Entity
public class Pet {
@Id
@GeneratedValue
private int id;
private String name;
public Pet() {
}
public Pet(String name) {
super();
this.name = name;
}
@Enumerated
private PetType type = PetType.CAT;
...
public String speak() {
return this.getType().speak();
}
}
em.getTransaction().begin();
Pet pet = new Pet("fluffy");
System.out.println(pet.getName() + " is a " + pet.getType().toString() + " and says " + pet.speak());
em.persist(pet);
em.getTransaction().commit();
em.getTransaction().begin();
pet = em.find(Pet.class, pet.getId());
pet.setType(PetType.DOG);
System.out.println(pet.getName() + " is now a " + pet.getType().toString() + " and says " + pet.speak());
em.getTransaction().commit();
fluffy is a CAT and says meow
[EL Fine]: Connection(27582163)--UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [50, SEQ_GEN]
[EL Fine]: Connection(27582163)--SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [SEQ_GEN]
[EL Fine]: Connection(27582163)--INSERT INTO PET (ID, NAME, TYPE) VALUES (?, ?, ?)
bind => [101, fluffy, 0]
fluffy is now a DOG and says woof
[EL Fine]: Connection(27582163)--UPDATE PET SET TYPE = ? WHERE (ID = ?)
bind => [1, 101]
2 comments:
Well, that works if your subclasses do not define additional state. Unless you are happy with the single-table-per-hierarchy ORM approach.
Yes you do need the same structure or you need to factor variable structure into the role object and move away from using an Enum like I did for this very simple example. Your delegate could be an Entity in a hierarchy itself.
The choice between delegation and inheritance for implementing variable behavior and storage is a very old OO technique and it fits well with the requirement to "change class" at runtime. And it is generally straight forward to implement in ORM.
Post a Comment