I have an object with several #onetomany relationships, and I need to query for properties in the parent, as well as properties of the children. I can't seem to get it done.
For example, I need a query that lets me see the Parent objects where where the parent's name is "John" and the child's favorite color is blue. Hope that makes sense. The reason for the complication seems to be that children are in a list, not in a #onetoone relationship.
PARENT:
#Entity
#Table(name="Parent")
public class Parent {
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.AUTO, generator="parent_gen")
#SequenceGenerator(name="parent_gen", sequenceName="PARENT_SEQUENCE")
private int parentID;
#Column(name="name")
private String name;
#OneToMany(cascade=CascadeType.ALL)
#OrderBy("name ASC")
#JoinTable(name = "parent_to_child")
private List<Child> childList;
// and so forth
Child
#Entity
#Table(name="Child")
public class Child{
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.AUTO, generator="child_gen")
#SequenceGenerator(name="child_gen", sequenceName="CHILD_SEQUENCE")
private int childID;
#Column(name="favoriteColor")
private String favoriteColor;
// and so forth
select p from Parent p join p.childList c
where p.name = 'John' and c.favoriteColor = 'blue'
This will return a List<Parent>.
You can look all this in the hql reference
Try something as follows:
from Parent as parent
left join parent.childList as children
with children.favoriteColor = 'blue'
where parent.name = 'John'
you need to do--- parent "left join fetch" child with your condition.
"left join fetch" gives result in List< Parent>
Without Fetch it will be List where object[0] = parent and object[1] = child.
public class Clients implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = { CascadeType.ALL},orphanRemoval=true)
#JoinColumn(name="client_id")
List<SmsNumbers> smsNumbers;
}
#Table(name="smsnumbers")
public class SmsNumbers implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
String number; //getter and setter
}
On the basis of child class i fetch the parent in unidirectional relation using the following criteria-
Session session = HibernateUtil.openSession();
try{
Criteria criteria=session.createCriteria(Clients.class);
criteria.createAlias("smsNumbers", "child");
criteria.add(Restrictions.eq("child.number", phone).ignoreCase());
Clients cli=(Clients) criteria.list().get(0);
System.out.println(cli.getId());
}catch (Exception e) {
// TODO: handle exception
}
JPQL provides a special syntax that, in these cases, makes things easier and helps you to think in an Oriented Object way:
SELECT p FROM Parent P, IN (P.childList) C
WHERE P.name='John' and C.favoriteColor='blue';
The operator IN iterates over lists, thus avoiding the need of using JOINs.
Criteria criteria=session.createCriteria(Parent.class);
criteria.add(Restrictions.eq("name", "John"));
criteria.createAlias("childList", "child");
criteria.add(Restrictions.eq("child.favoriteColor", "Blue").ignoreCase());
You can try with criteria API also.
Related
Consider I have 2 entity
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#OneToMany(cascade = CascadeType.ALL)
public List<Child> children = new ArrayList<>();
}
and
#Entity
class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#ManyToOne
public Parent parent;
}
how can I create Child instance with parentId
without call findById(Long parentId) i.e.
Child createChild(parentId) {
Child child = new Child();
child.parent = //parent.findById(parentId); I don't wanna go to database
//for nothing if in this spot anyway will be parentId in database
return child;
}
I thought it can be done with quare but hql don't have
INSERT .... VALUE .., so I'm here, appreciate any help.
If it's don't have any sense due to architecture,
please explain, it's be a great help.
No need to create new object in
public List<Child> children = new ArrayList<>();
just write
#OneToMany(targetEntity = Child.class, cascade = CascadeType.ALL)
public List<Child> children;
It is not a big problem that you will call parent.findById(parentId);
Hibernate caches some of the requests especially when used findById. You can see this answer link
The only thing to note is that you should not override findById in the repository, or if you do you should added it to the jpa cache.
EntityManager#getReference(Class<T> entityClass, Object primaryKey) is your friend here.
entityManager.getReference(Parent.class, parentId); returns an entity proxy. It can be used to improve the performance of the write operations since there will be no database call unless you access the fields of the returned entity.
I have two tables and they maintain the parent-child relationship between them by a foreign key.
The query looks something like below. I want to use the criteriaquery along with jpa. So can anyone help me with the criteriaquery & how the two entity classes would look like
ps:if there is any custom enity class required apart from these two entities classes help me with that as well.
Select parent.notification_id,parent.city,parent.name,parent.accountNo,
case when child.accountNo is not null then 'Yes' else 'No' end as checked
FROM parent
JOIN child ON parent.notification_id=child.notification_id_child
AND child.accountNo='test' WHERE parent.city='delhi' or parent.city='all' or parent.accountNo="test";
The column 'notification_id_child' of table 'child' is the foreign key and refers to the primarykey of table 'parent'.
There are multiple strategies that you can use to implement this:
MappedSuperclass (Parent class will be mapped with this annotation and not entity)
Single Table (Single table for each hierarchy, you can use #DiscriminatorColumn JPA annotation for identifying each hierarchy)
Joined Table (Each class for the parent and child)
In this scenario, you would have to join both the tables on the common column to fetch the results.
These are some good answers on joining tables
Joining two table entities in Spring Data JPA
Link for some good answers on usage of discrimintaorColumn
How to access discriminator column in JPA
Finally, I managed to solve the problem. My entity classes and criteria query looks something like the below.
Parent Entity
#Entity
#Table(name="parent")
public class Parent{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="notification_id")
private Long notificationId;
#Column(name="city")
private String city;
#Column(name="name")
private String name;
#Column(name="accountNo")
private String accountNo;
#JoinColumn(name="notification_id_child")
#OneToMany
private List<Child> child;
//Getters Setters
}
Child Entity
#Entity
#Table(name="child")
public class Child{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Column(name="accountNo")
private String accountNo;
#Column(name="notification_id_child")
private String notificationIdChild;
//Getters Setters
}
Custom Entity
public class CustomEntity{
private Long notificationId;
private String city;
private String accountNo;
private String checked;
}
Criteria Query
#PersistenceContext
EntitiManager em;
CriteraBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<CustomEntity> cq = cb.createQuery(CustomEntity.class);
Root<Parent> parentEntity = cq.from(Parent.class);
Join<Parent,Child> join = parentEntity.join("child", JoinType.LEFT);
join.on(cb.equal(join.get("accountNo"),"test"));
Path<String> notificationIdPath = parentEntity.get("notificationId");
Path<String> cityPath = parentEntity.get("city");
Path<String> accountNoPath = parentEntity.get("accountNo");
cq.multiselect(notificationIdPath, cityPath, accountNoPath,
cb.selectCase().when(join.get("accountNo").isNotNull(),"Yes").otherwise("No"));
Path<String> accountNoPath = parentEntity("accountNo");
Predicate accountNoPredicate = cb.equal(accountNoPath, "test");
Predicate cityPredicateAll = cb.equal(cityPath,"all");
Predicate cityPredicateSpecified = cb.equal(cityPath,"delhi");
cq.where(cb.or(cityPredicateAll, cityPredicateSpecified, accountNoPredicate));
TypedQuery<CustomEntity> query = em.createQuery(cq);
List<CustomEntity> CustomEntityList = query.getResult();
I have to join two tables and filter the result with a Where with Specifications and JPA. But I'm not very familiar with it.
The tables are for example:
public class A {
private Long id;
private Long secondId;
...
}
public class daughterA {
#JoinColumn (name = "id")
private Long idA;
...
}
and the WHERE is to be applied on secondId.
Thank you all for the help, I apologize for any inaccuracies, i'm beginning with these technologies.
First of all, your Join seems to be a one To Many join so you should make it as it is :
add the One annotation in the parent class being mapped by the name of the instance of it in the other class, this notation covers a Set or a List of objects of the daughters class
add the ManyToOne annotation in the daughter class, a daughter can only have one mother, and idA is not Long but it refers to the parent class it's in fact a foreign key
public class A{
#Id
private Long id;
private Long secondId;
#OneToMany(mappedBy="idA")
private Set<daughterA > daughters;
...
}
public class daughterA {
#ManyToOne
#JoinColumn (name = "id")
private A idA;
...
}
and for the where clause :
TypedQuery<Country> query =
em.createQuery("SELECT d FROM daughterA d WHERE d.idA.secondId = :secondId ", Country.class);
A results = query.setParameter("secondId",sencondId).getSingleResult();
I want to select parent with child that i want.
But when I select my parent I have to show all the childs
How can i do that?
Example:
public class parent{
private Integer id;
#OnetoMany
#JoinColumn(name="parentId")
private List<child> children;
}
public class child{
private Integer id;
private Integer parentId;
}
findByIdAndchildType(Integer id, String type)
I want to see : parent(id) - > child (type)
But i can see parent(id) - > child(othertype), child(othertype1), child(type)
It sounds to me that you're trying to get a bi-directional relation. This is possible by adding the mapping to both sides of the relation.
For example, add a #ManyToOne mapping to the Child entity. Be aware that you should probably remove your parentId field since now you can access it by using child.getParent().getId().
#Entity
public class Child {
#Id
private Integer id;
#ManyToOne
#JoinColumn(name = "parentId")
private Parent parent;
// Remove parentId field
// Getters + Setters ...
}
NOTE: If you want to keep the parentId field, you'll have to choose which two of the mappings (getParentId() or getParent().getId()) you want to use for inserting and updating entities. The other field should have both insertable = false and updatable = false.
The next step is to change the #OneToMany mapping to use mappedBy:
#Entity
public class Parent {
#Id
private Integer id;
#OneToMany(mappedBy = "parent") // Change this
private List<Child> children;
// Getters + Setters ...
}
If you want to retrieve a specific child with its parent, you can now create a repository for Child entities:
public interface ChildRepository extends JpaRepository<Child, Integer> {
}
After that, you can get a specific child by using:
Optional<Child> child = repository.findById(123); // 123 is the ID of the child in this case
Optional<Parent> parent = child.map(Child::getParent);
With Spring boot 1.x that would be:
Child child = repository.findOne(123);
Parent parent = null;
if (child != null) {
parent = child.getParent();
}
I have 2 entities O and D with a one to many relationship from O (one) to D (many). The relationship itself has an attribute - count.
What is the best way to model this using hibernate?
What I have currently is another entity OD representing the relationship. It has its own artificial key
The abbreviated version of the entities is as below
#Entity
class O {
#Id
private Long id;
#OneToMany(mappedBy = "o")
private Set<OD> ods;
}
#Entity
class OD {
#Id
private Long id;
#ManyToOne
private O o;
// This is uni-directional reln
#OneToOne
private D d;
private int count;
}
Is this the best way? I do not like the fact that the relationship has its own id, but is there a better way to model this relationship?
You can model the OD as #Embeddable, and change the owning side from #OneToMany to #ElementCollection e.g.
#Entity
public class O {
#Id
private Long id;
#ElementCollection
#CollectionTable( name = "OD")
private Set<OD> ods;
}
#Embeddable
public class OD {
// This is uni-directional reln
#OneToOne
private D d;
private int count;
}
The DDL statements would be almost the same with the distinction that the life-cycle of the OD would always be dependent on the life of its parent object, and would not hold an identity of its own