If we have an entity A which contains a List of instances of entity B and entity B contains a List of instances of entity C, something like:
#Entity
class C { ... }
#Entity
class B {
#OneToMany
List<C> c;
}
#Entity
class A {
#OneToMany
List<B> b;
}
Is there a way to delete instances of C and update an instance of C from SimpleJPARepository based on entity A?
Related
I am facing an issue with JPA EntityManager finder method. JPA entities are using inheritance structure as follows:
//Need to persist this table to database
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="Table1")
public class BaseEntity implements Serializable {
#Id
#Column(name="PRIMARY_ID")
private long id;
private String field1;
.......
}
//This table will NOT persist and has parameters only for Sub classs
#MappedSuperclass
public abstract class MappedSuperClassEntity extends BaseEntity {
private String field2;
private String field3;
........
}
//This sub class is persisted and inherits fields form above class including Primary Key using TABLE_PER_CLASS strategy defined in BaseEntity
#Entity
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="SubTable1")
public class Sub1 extends MappedSuperClassEntity {
private String field4;
private String field5;
...............
}
//This sub class is persisted and inherits fields form above class including Primary Key using TABLE_PER_CLASS strategy defined in BaseEntity
#Entity
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="SubTable2")
public class Sub2 extends MappedSuperClassEntity {
private String field4;
private String field5;
..............
}
As you can see, Sub1 and Sub2 are persistable entites and both of which extends MappedSuperClassEntity which is annotated with `#MappedSuperClass'. This class further inherits BaseEntity which has decribed TABLE_PER_CLASS inheritance strategy.
I enabled the hibernate stat collector and found that hibernate is storing sub class objects using key of Parent class. So in the above case it stores the data as follows for Sub1 finder in cache:
14:17:03,941 DEBUG [org.hibernate.cache.TransactionalCache] cache lookup: com.abc.BaseEntity#10
14:17:03,943 DEBUG [org.hibernate.cache.TransactionalCache] cache miss
14:17:03,948 DEBUG [org.hibernate.cache.TransactionalCache] caching: com.abc.BaseEntity#10
Next time if i do a find for Sub2 for same id (10), hibernate thinks it is in Cache as it uses Parent Class as key and returns the Sub1 object as follows:
14:27:54,574 DEBUG [org.hibernate.cache.TransactionalCache] cache lookup: com.abc.BaseEntity#10
14:27:54,575 DEBUG [org.hibernate.cache.TransactionalCache] cache hit
So this is happening when you run the finders for Sub1 and Sub2:
entityManager.find(Sub1.class, id); //returns Sub1 object
entityManager.find(Sub2.class, id); //returns Sub1 object (PROBLEM HERE).
Please help me to fix the issue (I do not want to clear cache in between these calls)
The problem is that you're using a base entity when that doesn't make sense. When you inherit from a base entity, and not just from a mapped superclass, you're not just inheriting fields and methods. You're establishing an is a relationship.
An example where that would make sense is the following: Car and Bike both inherit a base entity Vehicle. In that case, a Car entity is a Vehicle entity. And a Bike entity is a Vehicle entity.
If a car has an ID 42, then a Bike may not also have the ID 42, because you would have two vehicles with the same ID. Imagine a Driver entity with a ManyToOne association with a vehicle (i.e. a driver drives a vehicle). If I store the ID 42 in the vehicle_id column of the driver table, this ID 42 must uniquely identify a vehicle. It could be a car, it could be a bike, and hibernate will look in both tables, but it can't be both at the same time.
You're violating this inheritance concept. BaseEntity should not be annotated with Entity. It should just be a MappedSuperclass, which just allows inheriting fields and methods, but doesn't establish this semantic is a association.
I have three entities, for example A, B, C. Entity A is parent for B, with inheritance type joined. Entity B aggregates entity C with ManyToOne relationship.
Structure looks like next:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
class A {
String str;
String cStr;
}
#Entity
class B extends A {
#ManyToOne
C c;
}
#Entity
class C {
String str;
}
I don't know if it is possible at all on entity level but, I need to link value of str from C to the filed A.cStr. How it should looks like: I create entity B with populated value of c, store it and value from C.str is populating into filed A.cStr. And when I fetch A from datebase I can see A.cStr with same value as C.str has.
No, it's not possible. All you need to do to get the C string from A is to implement a method and override it in B:
in A:
public String getCString() {
return null;
}
in B:
#Override
public String getCString() {
return c.getStr();
}
Of course, the instances of A that are not B instances won't have any CString.
I have three classes look like this.
#Entity
class A {
#Id #GeneratedValue #TableGenerator
private long id;
}
#MappedSuperclass
abstract class B extends A {
}
#Entity
class C extends B {
}
Should above even work? And it seems not work, at least, with EclipseLink.
I got Column 'ID' cannot be null when I tried to persist an instance of C.
I found a bug at Inheritance with abstract intermediate class using #MappedSuperclass fails to populate subclasses but I'm not sure it is exactly the same situation or not.
UPDATE per #James's answer
I'm sorry, I should written more verbosely. Yes I'm intending SINGLE_TABLE inheritance. I don't have any extended property with B nor C. It's just a hierarchical class design.
#DiscriminatorColumn
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Entity
abstract class A {
#Id #GeneratedValue #TableGenerator
private long id;
}
#DiscriminatorValue("B") #Entity // I currently should do like this.
//#MappedSuperclass // error
abstract class B<T extends SomeoneElse> extends A {
}
#DiscriminatorValue("C")
#Entity
class C extends B<James> {
}
I believe that in JPA a #MappedSuperclass must be a superclass not a subclass (as its name infers).
I'm not sure what having an #Entiy subclass as a #MappedSuperclass would mean?
What are you trying to do?
For #Entity inheritance JPA only provides three options, SINGLE_TABLE, JOINED, and TABLE_PER_CLASS. All persistence subclasses must be entities.
I assume you are using JOINED inheritance and trying to avoid a table for B. JPA does not specify a standard way of doing this. In EclipseLink you can avoid the table by making its table match the parent (#Table(name="A")).
My question is probably so simple, that I can't find an answer for it.
I want to do something like this:
#Entity
public class EntityA {
#Transient
#SomeQueryAnnotation(query="select b from EntityB where b.id=1")
private EntityB entityB;
}
EntityB is kind of static resource. It should not be saved back to the database. There is also no mapping between the entities.
[EDIT]
Do you think it was ok, when I do this:
#Entity
public class EntityA {
private EntityB getEntityB() {
ServiceRemote service = (ServiceRemote)context.lookup("ejb:ServiceRemote");
return service.getEntityB();
}
}
Than it should still be possible to use remoting, because the connection can be configured in each clients' jndi.properties file. what is your prefered method when you need to access the database from your entities?
Best recommendation - Unless these objects have an in-database relation, then there shouldn't be an entity relationship.
Second best - I would recommend you create a data transfer object to fetch your object.
#Entity
public class EntityA {
#Transient
private EntityB entityB;
}
#Stateless
public class EntityADTO {
EntityManager em;
public EntityA findA(Object pkey) {
EntityA a = em.find(okey, EntityA.class);
a.entityB = em.find(1, EntityB.class);
return a;
}
}
We have the following mapping:
#Entity
public class A {
private B b;
#OneToOne
public B getB() {
return b;
}
}
When we delete an object of class A it must not delete the referenced object B. At the moment we get an exception when we try to delete A because of the existing relationship to B. How is the correct mapping?
You should disable cascade delete
#OneToOne(cascade = {})
or you can try
#OneToOne(orphanRemoval=false)