JPA Inheritance issue - java

Working with JPA 1 (hibernate-core version 3.3.0.SP1 and hibernate-entitymanager version 3.4.0.GA) :
I've some entities similar to those defined below, where ChildOne and ChildTwo extends from the Father entity.
#Entity
#Table(name = "TABLE_FATHER")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.INTEGER, name = Father.C_ID_CTG)
public class Father {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "sq")
#Column(name = "ID_PK", nullable = false)
#BusinessId
private Long id;
...
}
#Entity
#Table(name = "TABLE_CHILD_ONE")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue(Categories.ID_CTG_ONE)
public class ChildOne extends Father {
...
}
#Entity
#Table(name = "TABLE_CHILD_TWO")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue(Categories.ID_CTG_TWO)
public class ChildTwo extends Element {
...
}
Let's say I've one entity having a Father element, and another having a collection of father elements. In both cases, should go the children entities.
#Entity
#Table(name = "TABLE_ONE")
public class OneTable {
#JoinColumn(name = "ID_PK", referencedColumnName = "ID_PK", nullable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Father element;
...
}
#Entity
#Table(name = "TABLE_ANOTHER")
public class Another {
#Fetch(FetchMode.JOIN)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "id", fetch = FetchType.LAZY)
private Collection<Father> elementCollection;
...
}
I'm expecting to obtain always the children elements but when I get the element getElement() returns the father element
and, on the other hand, when I get the collection getElementCollection() the children elements are coming.
Apparently, the #JoinColumn is the cause of this behaviour, doing the join with the father table and forgetting the children elements.
The collection is working as expected.
How could I get the children element with a getElement() call? Any ideas or workarround?
Thanks in advance.

The problem is not caused by #JoinColumn.
The reason is Lazy Loading.
I manage to pinpoint your problem in simpler example.
Forgive me for changing convention from Father to Parent.
In the example below, uninitialized Element is type of jpa.inheritance.issue.Parent_$$_javassist_1. It is a Hibernate Proxy - dynamically created subclass of Parent.
You can "unproxy" it by invoking Hibernate proprietary API getHibernateLazyInitializer().getImplementation().
Collection of elementCollection is also Lazy Initialized. The type of the collection is org.hibernate.collection.PersistentBag which is being initilized with correct data at the time of first access.
Collection is initialized all at once.
Please see the test which successfully passed green with your exact version of Hibernate (3.3.0.SP1/3.4.0.GA).
#Test
public void test() {
Child c = new Child();
em.persist(c);
Another a = new Another();
a.setElement(c);
Collection<Parent> col = new ArrayList<Parent>();
col.add(c);
a.setElementCollection(col);
em.persist(a);
c.setAnother(a);
long idx = a.getId();
tx.commit();
// I'm cleaning the cache to be sure that call to a.getElement() will return proxy.
em.clear();
tx = em.getTransaction();
tx.begin();
a = em.find(Another.class, idx);
Assert.assertNotNull(a);
Parent p = a.getElement();
// At this point p is a type of jpa.inheritance.issue.Parent_$$_javassist_1
Assert.assertTrue(p instanceof Parent);
Assert.assertFalse(p instanceof Child);
// At this point a.elements is a not initialized (empty) collection of type org.hibernate.collection.PersistentBag
// When we access this collection for the first time, records are read from the database
Assert.assertEquals(1, a.getElementCollection().size());
if (p instanceof HibernateProxy) {
p =
(Parent) ((HibernateProxy) p).getHibernateLazyInitializer()
.getImplementation();
}
// At this point p is a type of jpa.inheritance.issue.Child
Assert.assertTrue(p instanceof Child);
}
#Entity
public class Another {
#JoinColumn(name = "element_id", referencedColumnName = "id", nullable = false)
#ManyToOne(fetch=FetchType.LAZY)
private Parent element;
public Parent getElement() {
return element;
}
public void setElement(Parent element) {
this.element = element;
}
#OneToMany(cascade = CascadeType.ALL, mappedBy = "another", fetch = FetchType.LAZY)
public Collection<Parent> elements;
public Collection<Parent> getElementCollection() {
return elements;
}
public void setElementCollection(Collection<Parent> elementCollection) {
this.elements = elementCollection;
}
// #Id ...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Parent {
#ManyToOne
private Another another;
public Another getAnother() {
return another;
}
public void setAnother(Another another) {
this.another = another;
}
// #Id ...
}
#Entity
public class Child extends Parent {
}
You don't need #DiscriminatorColumn nor #DiscriminatorValue because those annotations are needed with InheritanceType.SINGLE_TABLE as an only resort to determine the type.
With InheritanceType.JOINED Hibernate is able to determine polymorphic type by checking if there is a record in both (Parent and Child) tables with the same Id.
You can turn on hibernate logging to see how the query to determine the type looks like. It works like this:
select
another0_.id as id0_1_,
another0_.element_id as element2_0_1_,
parent1_.id as id1_0_,
parent1_1_.name as name2_0_,
case
when parent1_1_.id is not null then 1
when parent1_.id is not null then 0
else -1
end as clazz_0_
from
Another another0_
inner join
Parent parent1_
on another0_.element_id=parent1_.id
left outer join
Child parent1_1_
on parent1_.id=parent1_1_.id
where
another0_.id=?

Related

In hibernate persisting parent without a nullable child

I have a situation when using hibernate that my child gets persisted no matter what in a one-to-one mapping.
Say I have a parent entity,
#Entity
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Parent {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private Child child;
// getter/setter ...
}
Child entity,
#Entity
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Child {
#OneToOne(mappedBy = "child")
private Parent parent;
// getter/setter ...
}
When saving I always have to,
public class ParentServiceImpl ... {
public void save(ParentDTO parentDTO) {
// Map parent DTO to parent DAO
....
// Check if child is not null
if (parentDAO.getChild() != null) {
childRepository.save(parentDAO.getChild());
}
}
}
Without having to check with getChild whether child is null or not how can I achieve the persistence in hibernate for a parent with a nullable child ? In order words say I have a parent with no associated child then I want to only create a row for the parent and not a row for the child with nullable columns.

#OneToMany with #JoinFormula using polymorphism and generic

I have the following use case:
Parents contain Children and ParentTags.
Children contain ChildrenTags and their Parent node.
Both ParentTags and ChildrenTags are Tags with T being Parent or Child.
Now I want a derived field in Parent that map all tags relative to this parent or to one of its children.
The SQL query is straightforward but I can't make it work by annotating a given property.
Note that the #ManyToOne mapping works like a charm in order to find the Parent owner of a tag, cf my code below (this is a #OneToMany relation, Tag is maybe not the best name I could find for this example).
Parent.class
#Entity
#Audited
public class Parent {
#Id
private Long id;
#OneToMany(mappedBy = "parent")
private List<Child> children;
#OneToMany(mappedBy = "parent")
private List<ParentTag> tags;
// Does not work!
#OneToMany(mappedBy = "owner")
private List<Tag<?>> allTags;
}
Child.class
#Entity
#Audited
public class Child {
#Id
private Long id;
#ManyToOne
private Parent parent;
#OneToMany(mappedBy = "child")
private List<ChildTag> tags;
}
Tag.class
#Entity
#Audited
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Tag<T> {
#Id
private Long id;
protected String value;
#NotAudited // otherwise ClassCastException
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(
value = "CASE WHEN parent_id IS NOT NULL THEN parent_id WHEN child_id IS NOT NULL THEN (SELECT child.parent_id FROM Child child WHERE child.id = child_id) end",
referencedColumnName="id"))
})
private Parent owner;
public abstract T getNode();
}
ChildTag.class
#Audited
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class ChildTag extends Tag<Child> {
#ManyToOne
private Child child;
#Override
public Child getNode() {
return child;
}
}
ParentTag.class
#Audited
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class ParentTag extends Tag<Parent> {
#ManyToOne
private Parent parent;
#Override
public Parent getNode() {
return parent;
}
}
I'd like to find a way to load allTags by a given query or formula.
The inverse mapping #ManyToOne works (as long as owner is not audited, it was hard to find that bug).
I already tried the following solutions without success:
#JoinFormula
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(
value = "CASE WHEN parent_id IS NOT NULL THEN parent_id WHEN child_id IS NOT NULL THEN (SELECT child.parent_id FROM Child child WHERE child.id = child_id) end",
referencedColumnName="id"))
})
private List<Tag<?>> allTags;
I get a ClassCastException, Formula cannot be cast into a Column
#Loader
#Entity
#Audited
#NamedNativeQuery(name = "loadAllTags",
query = "SELECT * FROM Tag tag WHERE "
+ "(tag.parent_id IS NOT NULL AND tag.parent_id = :id) OR "
+ "(tag.child_id IS NOT NULL AND EXISTS (SELECT 1 FROM Child child WHERE child.id = tag.child_id AND child.parent_id = :id))")
public class Parent {
...
#Loader(namedQuery = "loadAllTags")
private List<Tag<?>> allTags;
}
No exception, actually when debugging I can see that the loader find all appropriate tags, but does not initialize the collection with them.
I also tried to put the query in a hbm configuration file, as I understood that #Loader and #NamedNativeQuery do not get along well, but without success (maybe I did something wrong here). However I'd rather implement a solution without configuration file.
I could add another column (owner_id) but if I can find a way to solve that problem without changing the model, it would be better.
I don't know if generics have something to do with it. In addition my entities are audited and indexed.
Any help will be greatly appreciated!

Play Ebean List null

I have an issue using Ebean to save a list of object.
I have a three class. the last one included two children class.
#Entity
#Table(name="A")
public class A extends Model {
#Id
public String idA;
#OneToMany(cascade=CascadeType.ALL, mappedBy = "currentA")
private List<B> listOfB;
}
The second class B :
#Entity
public class B extends Model {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
public Long idB;
#ManyToOne
#JoinColumn(name = "idA")
private A currentA;
#OneToMany(cascade=CascadeType.ALL, mappedBy = "currentB")
public ArrayList<C> lstOfC;
public B(List<C> lstC) {
this.lstOfC=lstC;
}
}
And the last one :
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("X")
public class C extends Model {
#ManyToOne
#JoinColumn(name = "idB")
private B currentB;
private setcurrentB(int currentB) {
this.currentB=currentB;
}
}
The problem I am facing is that I need to create a list of C object (lstOfC) but I do not know the value of currentB when I put the element in the list.
I need to set (with a setter) this value later then save it to the database.
But when I try that, the list of C object is null from the list of B in A.
ArrayList<C> lstC=new ArrayList<C>();
c1=new C();
c2=new C();
B=new B(lstC);
for (C c: lstC) {
c.setcurrentB(1);
Ebean.save(c);
}
You example doesn't quite make sense where setcurrentB(1) ... takes 1 but expects an instance of B - I presume that is a reference bean.
It seems like you want to temporarily turn off cascade persist and you can do that on the Transaction.
Transaction tranaction = Ebean.beginTransaction();
try {
// turn off persist cascade for this transaction
transaction.setPersistCascade(false);
for (C c: listC) {
}
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
}

Inheritance JPA and Hibernate Issue

I've a weird problem loading some objects. I'm using JPA 1, hibernate-core version 3.3.0.SP1 and hibernate-entitymanager version 3.4.0.GA
Let's say I've these JPA entities:
#Entity
#Table(name = "SLC_ELE")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.INTEGER, name = ElementoPrograma.C_ID_CTG_ELE)
public class Element {
...
}
#Entity
#Table(name = "SLC_ELE_ONE")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue(Categories.ID_CTG_ONE)
public class ElementTypeOne extends Element {
...
}
#Entity
#Table(name = "SLC_ELE_TWO")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue(Categories.ID_CTG_TWO)
public class ElementTypeTwo extends Element {
...
}
#Entity
#Table(name = ThreeElementExample.TABLENAME)
#AssociationOverrides({
#AssociationOverride(name = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_ONE,
joinColumns =
#JoinColumn(name = Element.C_ID_ELE)),
#AssociationOverride(name = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_TWO,
joinColumns =
#JoinColumn(name = OneEntity.C_ID_TWO)),
#AssociationOverride(name = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_THREE,
joinColumns =
#JoinColumn(name = AnotherEntity.C_ID_THREE))})
public class ThreeElementExample extends JpaMany3ManyEntity<Element, OneEntity, AnotherEntity> {
...
}
The thing is, I'd like to obtain always the subclasses (meaning the ElementTypeOne, ElementTypeTwo instead the elements) when I load a collection of these entities. The problem is that the many to many relation always get the Element (the father instead the children)
Let's say I've an entity A containing a colection of Elements:
#Fetch(FetchMode.JOIN)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idEle")
private Collection<Element> elementCollection;
And if I get the collection, everything works fine (I get the subclasses as expected).
The problem comes when I've another entity B with a collection of the JpaMany3ManyEntity (notice that the same entity element is involved)
#OneToMany(cascade = CascadeType.ALL, mappedBy = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_ONE, fetch = FetchType.LAZY)
private Collection<ThreeElementExample> threeElementExampleCollection;
If I loop the threeElementExampleCollection from class B before I try to obtain the elementCollection from class A, when I load the objects from the elementCollection
I obtain just the superclass (Element) objects instead the children.
I guess that, for any reason, the many to many relationship obtains always the Element objects (father) and saves them in the hibernate cache, but I need to avoid this behaviour.
Any ideas or workarround? Any kind of help would be really appreciated.
Thanks in advance.
EDIT: the many to many class:
#SuppressWarnings("serial")
#MappedSuperclass
#AssociationOverrides({
#AssociationOverride(name = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_ONE,
joinColumns =
#JoinColumn(name = "changeMeWhenExtends")),
#AssociationOverride(name = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_TWO,
joinColumns =
#JoinColumn(name = "changeMeWhenExtends")),
#AssociationOverride(name = JpaMany3ManyEntity.ASOCIATION_OVERRIDE_THREE,
joinColumns =
#JoinColumn(name = "changeMeWhenExtends"))})
public abstract class JpaMany3ManyEntity<A extends JpaBaseEntity, B extends JpaBaseEntity, C extends JpaBaseEntity> extends JpaBaseEntity {
public static final String ID_ATTNAME = "id";
public static final String ASOCIATION_OVERRIDE_ONE = JpaMany3ManyEntity.ID_ATTNAME + "." + JpaMany3ManyId.ID_ONE_ATTNAME;
public static final String ASOCIATION_OVERRIDE_TWO = JpaMany3ManyEntity.ID_ATTNAME + "." + JpaMany3ManyId.ID_TWO_ATTNAME;
public static final String ASOCIATION_OVERRIDE_THREE = JpaMany3ManyEntity.ID_ATTNAME + "." + JpaMany3ManyId.ID_THREE_ATTNAME;
...
}
Here's a workarround that works to me: Deproxy the entities.
Even having a parent proxy of the entity (jpa.inheritance.issue.Element_$$_javassist_1) if you deproxy it, you'll obtain the real entities (children).
Let's say you want to loop your (children) elements collection from the entity A and do something with them.
Something like:
public void loopDeproxyElements(List<Element> yourElementsCollection){
for(Element p : yourElementsCollection){
if(p instanceof HibernateProxy){
Element child = (Element) ((HibernateProxy) p).getHibernateLazyInitializer()
.getImplementation();
if (child instanceof ElementTypeOne){
//You can cast your object or do whatever you want, knowing for sure that's a child element)
ElementTypeOne myRealElement = (ElementTypeOne) child;
...
} else {
//It should be ElementTypeTwo (if u never create parent entities)
...
}
}
}
)
It will always get the children elements as I was expecting.
Try experiment with hibernate.default_batch_fetch_size property. By default it's set to 1. This will load only first entity from your collection. Increasing it to ~size of the collection might help.

Hibernate JPA value removing OneToMany relation

I have two tables where there is a OneToMany, MnatToOne relation.
When I have added instance of AlarmnotifyEmailEntity into alarmnotifyEmailEntityList object and update instance of AlarmnotifyEmailConfEntity, value is save properly into Database.
Bu I could not do the same thing when deleting one of the item of alarmnotifyEmailEntityList.
I am sure that value is removed from alarmnotifyEmailEntityList but it does not reflect this changes into Database
#Entity(name ="alarmnotify_email_conf")
#Table(name = "alarmnotify_email_conf")
public class AlarmnotifyEmailConfEntity implements Serializable {
#OneToMany(mappedBy = "alarmnotifyEmailConfRef",cascade=CascadeType.ALL)
private List<AlarmnotifyEmailEntity> alarmnotifyEmailEntityList;
}//end of Class
#Entity (name ="alarmnotify_email")
#Table(name = "alarmnotify_email")
public class AlarmnotifyEmailEntity implements Serializable {
#JoinColumn(name = "alarmnotify_email_conf_ref", referencedColumnName = "id")
#ManyToOne
private AlarmnotifyEmailConfEntity alarmnotifyEmailConfRef;
}end of Class
I am only invoking following statement to update.
JPAManager.getJPAManagerInstance().update(alarmnotifyemailconf);
public Object update(Object o) {
try {
tx.begin();
EntityManager em = getEntityManager();
System.out.println("updating object:" + o);
o = em.merge(o);
em.close();
tx.commit();
System.out.println("closed and commited merge operation");
return o;
}
catch (Exception e) {
e.printStackTrace();
}
return o;
}
From my experience cascade only applied for same operation. If we save parent, then children also will saved same case with update. But I think when you want to remove one of children, we have to remove explicitly using entity manager, and cannot just merging parent and expect will cascade remove to children.
I have found out the answer in jpa removing child from collection.
as a result adding orphanRemoval=true solved the problem.
#Entity(name ="alarmnotify_email_conf")
#Table(name = "alarmnotify_email_conf")
public class AlarmnotifyEmailConfEntity implements Serializable {
#OneToMany(mappedBy =
"alarmnotifyEmailConfRef",cascade=CascadeType.ALL ,orphanRemoval=true)
private List alarmnotifyEmailEntityList;
}//end of Class

Categories