Loading child entities in parent/child relationship with JPA - jpa

My domain has a Category entity which has a biderectional relationship on itself. Each category can have a parent and children.
#Entity
public class Category implements DomainObject {
private Long id;
private Integer version;
private String name;
private Category parent;
private Set<Category> children;
#Override
#Id
#GeneratedValue
public final Long getId() {
return id;
}
#Version
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public void setId(Long id) {
this.id = id;
}
#Column(unique=true, nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#ManyToOne
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
#OneToMany
#JoinColumn(name = "parent_id")
public Set<Category> getChildren() {
return children;
}
public void setChildren(Set<Category> children) {
this.children = children;
}
}
I have created the following query to fetch the "root" categories with their direct (level 1) children.
select distinct c from Category c left join fetch c.children where c.parent is null order by c.name
This actually works. My question is: why do I need the "JoinColumn" annotation on getChildren() to make this work and why can't I just make a "foin fetch" query, without "distinct"? If I remove "distinct" I get a multiplication. For each child of a parent, the entire parent is copied in the result set.
Is there a better way to do this? It just feels... a bit crappy.

In JPA you need to set distinct when you join a OneToMany, otherwise it will return duplicates.
This is required.
The JPA spec requires this, but it is an odd default, but relates to what happens in database joins.

Related

Disable possibility of saving 2 Children to Parent in OneToOne association

It is possible to save same parent to 2 children:
I want to disable this possibility.
the domains:
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#OneToOne
Parent parent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#OneToOne(mappedBy="parent")
private Child child;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#PreRemove
private void preRemove() {
child.setParent(null);
}
}
How to change this code to disable possiblity of having same parent for 2 children? Should I craete FK or somethin? How do that in elegant way with JPA?
Define a #JoinColumn on your #OneToOne and add unique = true. This way uniqness of parents will be checked at the database side:
#OneToOne
#JoinColumn(name="PARENT_ID", unique = true)
Parent parent;

How to make a relation of same table in JPA?

Need make a relationship between same table. Example: the object "category" have subcategories and the subcategorie have other subcategorie.
In MySQL make a column and point to primary key of same table, but, howto make in JPA?
My code is:
#Entity
#Table(name = "objects")
public class JObject {
private long id;
private String name;
private JObject parentJObject;
private Set<JObject> jObjects;
public JObject(){
}
public JObject(long id){
this.id = id;
}
public JObject(String name){
this.name = name;
}
public JObject(String name, JObject parentJObject){
this.name = name;
this.parentJObject = parentJObject;
}
#Null
#JoinColumn(name="parent_object_id", referencedColumnName="id")
#ManyToOne(cascade=CascadeType.ALL)
public JObject getParentJObject() {
return parentJObject;
}
public void setParentJObject(JObject parentJObject) {
this.parentJObject = parentJObject;
}
#Null
#OneToMany(mappedBy = "parentJObject", cascade = CascadeType.ALL)
public Set<JObject> getJObjects() {
return jObjects;
}
public void setJObjects(Set<JObject> jObjects) {
this.jObjects = jObjects;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#NotNull
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And the making objects:
JObject jObjectcategories = new JObject("Demo 1");
Set categoriesJObjects = new HashSet<JObject>(){{
add(new JObject("Demo 1.1", jObjectcategories));
}};
jObjectcategories.setJObjects(categoriesJObjects);
jObjectDao.save(new HashSet<JObject>() {{
add(jObjectcategories);
}});
But does not works. The log says:
List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='It has to be null', propertyPath=JObjects, rootBeanClass=class a.b.c.models.JObject, messageTemplate='{javax.validation.constraints.Null.message}'} ]
You need to be consistent in where you place your JPA annotations: either all on fields, or all on getters. But not mixed as you're doing.
The OneToOne should be a ManyToOne according to your description, since several objects share the same parent.
And the cascade ALL doesn't make sense: you don't want to delete a parent when a child is deleted.

fetch one to many side with jpql

so I have done two entities with one to many relationship,
I have one category whohas many visitors,
and this is my code:
this is the Category entity :
#Entity
public class Category implements Serializable {
private Integer id;
private String name;
private List<Visitor> visitors = new ArrayList<Visitor>();
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(cascade=CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "category", orphanRemoval = true)
public List<Visitor> getVisitors() {
return visitors;
}
public void setVisitors(List<Visitor> visitors) {
this.visitors = visitors;
}
}
and here is the Visitor Entity :
#Entity
public class Visitor extends User {
private String passport;
private String citizenship;
private String gender;
private Company company;
private Category category;
public String getPassport() {
return passport;
}
public void setPassport(String passport) {
this.passport = passport;
}
public String getCitizenship() {
return citizenship;
}
public void setCitizenship(String citizenship) {
this.citizenship = citizenship;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
and here is the service method who list all the visitors and works fine :
public List<Visitor> findAllVisitors() {
return em.createQuery(
"SELECT v from Visitor v left join fetch v.category",
Visitor.class).getResultList();
}
with this method I can list all the visitors each with his category object associated,
now the problem is in the other side of the relationship ,
here is the method who list the categories each with their visitors list :
public List<Category> findAllCategories() {
return em.createQuery("select c from Category c",
Category.class).getResultList();
}
I want to get the list of all the categories but when I call this method in a REST call , I get this result :
I want just to get a simple list of categories (id and name).
what is wrong in my code please help me i am confused.
UPDATE:
this is how I get JSON from persistence context with RESTful method :
#Inject
private CategoryServiceLocal categoryServiceLocal;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Category> dofindAllCategories() {
return categoryServiceLocal.findAllCategories();
}
You have a lazy association from Category to visitors. To load all visitors you need to use left join fetch too.
select c from Category c left join fetch c.visitors
Please, use additional annotations to control how to JSON generated
Infinite Recursion with Jackson JSON and Hibernate JPA issue

org.hibernate.TransientObjectException: object references an unsaved transient instance

I am trying to establish unidirectional relationship between two entities called Person and Address,while saving Person(containing collection of Address) getting org.hibernate.TransientObjectException: object references an unsaved transient instance.
When I change cascadeType=all,child objects are getting propagated.But the problem here is with cascadeType=all Hibernate also tries to delete child entities on Deleting Owning entity.I don't want that to happen because in ManyToMany relationship child entity might be being referenced by some other entity.Ideally cascadeType=persist should do the job but unfortunately that give me mentioned exception.
Can somebody help me out how can I save the child objects (Address) with cascadeType=persist.I just wonder why cascadeType=persist is not doing the task of persisting.
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
#LazyCollection(LazyCollectionOption.FALSE)
#ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
private Collection<Address> address=new HashSet<Address>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Address> getAddress() {
return address;
}
public void setAddress(Collection<Address> address) {
this.address = address;
}
}
#Entity(name="Address")
public class Address {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Column(name="country")
private String country;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
In case of many- many relationship you always have to make use of JOIN Table(which is a third table).In that case you dont have a problem of Child entities getting deleted on deleting the Owning entity even if you use CASCADE-ALL.
Please see following post below:
Modeling many-to-many Relationship in JPA/Hibernate

entity manager merge() issue with id null

I'm encountering a problem when merging an existing parent entity with a new child entity.
Here's the scenario:
Create two new parent's and persist in db.
In parent's table we will have two new entries as follows
ID = 0, NAME = A
ID = 1, NAME = B
Using entitymanager.find() , fetch the parent with ID=0, and create a new child for the parent.
In the child's table, we will have the following entry
ID=0, NAME = CHILD of A, PARENT_ID = 0
Using entitymanager.find() , fetch the parent with ID=1, and create a new child for the parent.
After merging the parent object using entitymanager.merge(), the newly created child get's merge with the existing child with ID = 0.
The entries in child table, becomes ID=0 , CHILD OF PARENT B, PARENT_ID =0.
Why does this happen? Shouldn't it create a new entry for Child ?
The parent entity:
public class ParentEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="PARENT_SEQ")
#SequenceGenerator(sequenceName="PARENT_SEQ",schema="MURTAZA YAHYA", name = "PARENT_SEQ", initialValue=-1)
//#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID",unique=true, nullable=false)
private Integer id;
#Column(name="NAME" , length=150)
private String name;
#OneToMany(mappedBy="parentEntity",cascade={CascadeType.MERGE,CascadeType.PERSIST})
List<ChildEntity> childEntities;
public Integer getId() {
if(id == null)
return null;
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildEntity> getChildEntities() {
return childEntities;
}
public void setChildEntities(List<ChildEntity> childEntities) {
this.childEntities = childEntities;
}
}
The Child Entity
public class ChildEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="CHILD_SEQ")
#SequenceGenerator(sequenceName="CHILD_SEQ",schema="MURTAZA YAHYA", name = "CHILD_SEQ" , initialValue=0)
//#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID",unique=true, nullable=false)
private Integer id;
#Column(name="NAME" , length=150)
private String name;
#ManyToOne
#JoinColumn(name="PARENT_ID")
private ParentEntity parentEntity;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentEntity getParentEntity() {
return parentEntity;
}
public void setParentEntity(ParentEntity parentEntity) {
this.parentEntity = parentEntity;
}
}
public String AddChildToAnExistingParent(Parent p) {
// TODO Auto-generated method stub
ParentEntity parent = em.find(ParentEntity.class, p.getId());
ChildEntity childEntity = new ChildEntity();
childEntity.setName("INISYA");
childEntity.setParentEntity(parent);
List<ChildEntity> list = new ArrayList<ChildEntity>();
list.add(childEntity);
parent.setChildEntities(list);
em.merge(parent);
return "Success";
}
Any help will be greatly appreciated. Thank you,
As the Parent->Child association is annotated with CascadeType.PERSIST the following code should resolved the described scenario.
public String AddChildToAnExistingParent(Parent p)
{ // TODO Auto-generated method stub
ParentEntity parent = em.find(ParentEntity.class, p.getId());
ChildEntity childEntity = new ChildEntity();
childEntity.setName("INISYA");
childEntity.setParentEntity(parent);
parent.getChildEntities().add (childEntity);
em.persist(parent);// the cascade.persist cause new childs persistence/insert
return "Success";
}
For more details in this answer explains the behavior of EntityManager method merge and persist.