In hibernate persisting parent without a nullable child - java

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.

Related

How to save parent entity without inserting child in one to one relationship by shared primary key in hibernate?

I am using one to one relation by shared primary key in hibernate,
whenever I call the save method to insert parent entity without inserying child entity I get below
exception
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.example.sms.domain.Child.parent]
Code of child class entity with mapping of parent class entity is
given below
#Entity
#Table(name = "t_child")
public class Child {
#Id
#Column(name="user_id", unique=true, nullable=false)
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters=#Parameter(name="property", value="user"))
private Long id;
#OneToOne(optional = false,fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private Parent parent;
And in parent class entity i have mapped child class entity like below
#OneToOne(mappedBy="parent", cascade=CascadeType.ALL)
private Child child;
Is there any way to save only parent entity without inserting child entity?
The entity relationship diagram is not showing us a one-to-one relationship. But let us assume it is.
If I understood you well the child entity is dependent on the parent entity and it does not have its own identity. So you don't need to define an ID attribute for it; so the mapping for the Child entity should look like the following:
#Entity
#Table(name = "t_child")
public class Child {
#Id
#OneToOne(optional = false,fetch = FetchType.LAZY)
#JoinColumn(name="parent_id")
private Parent parent;
// ...
}
and the Parententity should look like the following:
#Entity
#Table(name = "t_parent")
public class Parent {
#Id
#GeneratedValue(generator="gen")
private Long id;
#OneToOne(mappedBy="parent")
private Child child;
// ...
}
That should be the mapping. Now, if you want to save parent without child do as follows:
Parent parent = new Parent();
// set attribute values
entityManager.persist(parent);
But if you want to save child, you should know that you first must have to save parent because the child is dependent on the parent, which means the parent must exist.
Finally after 1 day I have found the solution myself
This code Was correct. Mapstruct was creating a problem while
converting Dto to domain object
#Mappings({
#Mapping(source = "fatherName", target = "child.childDetail.fatherName"),
#Mapping(source = "motherName", target = "child.childDetail.motherName"),
#Mapping(source = "firstName", target = "child.childDetail.firstName"),
#Mapping(source = "lastName", target = "child.childDetail.lastName"),
#Mapping(source = "dateOfBirth", target = "child.childDetail.dateOfBirth"),
})
public User ParentDtoToParent(ParentDto parentDto);
I was using the Same DTO object that I was using while storing every
child detail But to store only parent entity I was not sending any
child details value in the Json Format. So mapstruct Automatically
assigned NULL value to the Child attribute(firstname,lastname etc)
When Hibernate Was saving the object it got object With the Null value
of Attributes instead of NULL Object

#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!

JPA Inheritance issue

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=?

Delete child from parent and parent from child automatically with JPA annotations

Suppose that we have 3 Entities object class:
class Parent {
String name;
List<Child> children;
}
class Child {
String name;
Parent parent;
}
class Toy {
String name;
Child child;
}
How can I use JPA2.x (or hibernate) annotations to:
Delete all children automatically when parent delete (one to many)
Delete child automatically from children list when it is deleted (many to one)
Delete toy automatically when child remove (one to one)
I'm using Hibernate 4.3.5 and mysql 5.1.30.
Thanks
The remove entity state transition should cascade from parent to children, not the other way around.
You need something like this:
class Parent {
String name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();
public void addChild(Child child) {
child.setParent(this);
children.add(child);
}
public void removeChild(Child child) {
children.remove(child);
child.setParent(null);
}
}
class Child {
String name;
#ManyToOne
Parent parent;
#OneToOne(mappedBy = "child", cascade = CascadeType.ALL, orphanRemoval = true)
Toy toy;
}
class Toy {
String name;
#OneToOne
Child child;
}
You should use CascadeType.REMOVE. This is common annotation for both Hibernate and JPA. Hibernate has another similar type CacadeType like CascadeType.DELETE.
Delete all children automatically when parent delete (one to many)
class Parent {
String name;
#OneToMany(cascade = CascadeType.REMOVE)
List<Child> children;
}
Delete child automatically from children list when it is deleted (many to one)
class Child {
String name;
#ManyToOne(cascade = CascadeType.REMOVE)
Parent parent;
}
Delete toy automatically when child remove (one to one)
class Toy {
String name;
#OneToOne(cascade = CascadeType.REMOVE)
Child child;
}
orphanRemoval is delete all orphan entity example: store (s) has books(b1,b2,b3) and b1 has title(t) in this case if deleted store(s) some books(b2,b3) will be deleted. B2 and t still exist. if you use "cascade= CascadeType.Remove" just store(s) and all books will be deleted (only "t" exist).
s->b1,b2,b3 b2->t ------after(orphanRemoval = true)--------- b2->t
s->b1,b2,b3 b2->t ------ after(cascade=CascadeType.REMOVE)--------- t
If orphanRemoval=true is specified the disconnected entity instance is automatically removed. This is useful for cleaning up dependent objects that should not exist without a reference from an owner object.
If only cascade=CascadeType.REMOVE is specified no automatic action is taken since disconnecting a relationship is not a remove operation.

EJB3 and manual hierarchy persistence

I have a legacy database, which I am using EJB3 to model. The database is in quite a poor shape, and we have certain unusual restrictions on how we insert into the DB. Now I want to model the database in a hierarchy that fits in with the DB strucuture, but I want to be able to manually insert each entity individually without the persistence manager trying to persist the entities children.
I am trying something like the following (boilerplate left out):
#Entity
#Table(name = "PARENT_TABLE")
public class Parent {
#Id
#Column(name = "ID")
int id;
#OneToMany
List<Child> children;
}
#Entity
#Table(name = "CHILD_TABLE")
public class Child {
#Id
#Column(name = "ID")
int id;
}
Now this throws an exception:
java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST
Now I know the entity isn't marked PERSIST - I don't want the EntityManager to persist it! I want to be able to persist the parent first, and then the child - but not together. There are good reasons for wanting to do it this way, but it doesn't seem to want to play.
Heh welcome to the hair-pulling that is JPA configuration.
In your case you have two choices:
Manually persist the new object; or
Automatically persist it.
To automatically persist it you need to annotate the relationship. This is a common one-to-many idiom:
#Entity
#Table(name = "PARENT_TABLE")
public class Parent {
#Id private Long id;
#OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
private Collection<Child> children;
public void addChild(Child child) {
if (children == null) {
children = new ArrayList<Child>();
}
child.setParent(parent);
children.add(child);
}
}
#Entity
#Table(name = "CHILD_TABLE")
public class Child {
#Id private Long id;
#ManyToOne
private Parent parent;
public void setParent(Parnet parent) {
this.parent = parent;
}
}
Parent parent = // build or load parent
Child child = // build child
parent.addChild(child);
Because of the cascade persist this will work.
Note: You have to manage the relationship at a Java level yourself, hence manually setting the parent. This is important.
Without it you need to manually persist the object. You'll need an EntityManager to do that, in which case it is as simple as:
entityManager.persist(child);
At which point it will work correctly (assuming everything else does).
For purely child entities I would favour the annotation approach. It's just easier.
There is one gotcha I'll mention with JPA:
Parent parent = new Parent();
entityManager.persist(parent);
Child child = new Child();
parent.addChild(child);
Now I'm a little rusty on this but I believe that you may run into problems if you do this because the parent was persisted before the child was added. Be careful and check this case no matter what you do.

Categories