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();
Related
I have the following entities:
#Entity
#Table(name="table1")
public class Entity1 {
#Id
private Integer id;
#OneToMany(mappedBy = "entity1")
private List<Entity2> entities2;
}
#Entity
#Table(name="table2")
public class Entity2 {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="id")
private Entity3 entity3;
}
Using Criteria API I have tried:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Entity1> query = cb.createQuery(Entity1.class);
Root<Entity1> entity1= query.from(Entity1.class);
entity1.fetch("entities2", JoinType.LEFT);
entity1.fetch("entities2", JoinType.LEFT).fetch("entity3", JoinType.LEFT);
But when the query is executed:
query.select(entity1).where(cb.and(predicates.toArray(new Predicate[predicates.size()]))));
List<Entity1> entities1 = entityManager.createQuery(query).getResultList();
Multiple queries intead one are executed (related to Entity 3). I think the problem is beacuse the relationship is inside another one. Because when you fecth the first join, there are not several queries.
I would appreciate your help. Thank so much
By default the relationships wich are like Collections the hibernate deal how lazy,
if you defined relationship how Lazy and not received LazyLoadException check your config.
I am using JPA 2.0, with Hibernate 1.0.1.Final.
I want all Parent table fields with no of children in single query.
In Other word, I want to translate from SQL into CriteriaAPI of JPA/Hibernate.
select kgroup.*, count(userGroup.uid)
from kernelGroup kgroup
left join kernelUserGroup userGroup on (kgroup.groupId = userGroup.groupId)
group by kgroup.groupId
I have following JPA Entities.
#Entity
#Table(name="kernel_group")
public class KernelGroup implements Serializable {
#Id
private int groupId;
private boolean autoGroup;
private String groupName;
#OneToMany
private Set<KernelUserGroup> kernelUserGroups;
private long jpaVersion;
}
#Entity
#Table(name="kernel_usergroup")
public class KernelUserGroup implements Serializable {
#EmbeddedId
private KernelUserGroupPK id;
private long jpaVersion;
#ManyToOne
private KernelGroup kernelGroup;
#ManyToOne
private KernelUser kernelUser;
}
#Embeddable
public class KernelUserGroupPK implements Serializable {
private String uid;
private int groupId;
}
My Current Criteria Query is like this :
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<KernelGroupDto> cQuery = cb.createQuery(KernelGroupDto.class);
Root<KernelGroup> root = cQuery.from(KernelGroup.class);
Join<KernelGroup, KernelUserGroup> userGroupsJoin = root.join(KernelGroup_.kernelUserGroups, JoinType.LEFT);
cQuery.select(cb.construct(KernelGroupDto.class, root, cb.count(userGroupsJoin.get(KernelUserGroup_.id).get(KernelUserGroupPK_.uid))));
cQuery.groupBy(root.get(KernelGroup_.groupId));
em.createQuery(cQuery).getResultList();
Now the Problem is, It fires multiple Queries to the database.
1) One query to retrieve groupId and no of count of users
2) N Queries to retrieve group info for each group.
I want only one Query to retrieve GroupInfo and no of count of the users as shown in Above SQL Query.
Please give me good suggestion.
Implement Using the bellow Code.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<KernelGroupDto> cQuery = cb.createQuery(KernelGroupDto.class);
Root<KernelGroup> root = cQuery.from(KernelGroup.class);
Join<KernelGroup, KernelUserGroup> userGroupsJoin =
root.join(KernelGroup_.kernelUserGroups, JoinType.LEFT);
cQuery.select(cb.construct(KernelGroupDto.class, root.
<Long>get("id"),cb.count(userGroupsJoin)));
cQuery .groupBy( root.<Long>get("id") );
cQuery.groupBy(root.get(KernelGroup_.groupId));
em.createQuery(cQuery).getResultList();
This code will work. for child count.
you must have the constructor of the class KernelGroupDto(Long id, Long childCount)
I have an entity, that i'd like to join OneToOne with a table with a composite key (Omitting getters/setters):
#Entity
#Table(name = "parent")
public class Parent {
#Id
private String parentId;
#Column(name = "data")
private String data;
#OneToOne
private Child child;
}
And:
#Entity
#IdClass(ChildKey.class)
#Table(name = "child")
public class Child{
#Id
private String parentId;
#Id
private String username;
#Column(name = "data")
private String childData;
}
public class ChildKey implements Serializable {
private String parentId;
private String username;
}
Parent does not have a notion of the 'username' field in the Child entity. I need to pass this in as criteria. In the DB, the primary key of child is on parentId and username.
If I don't specify a JoinColumn, hibernate attempts to map using fields child_username and child_parentId. If I specify only one Joincolumn, I get a broken mapping. If I specify both JoinColumns, I have no column on parent to specify.
How can I map this class and pass in the username as criteria? (it is coming from authentication data) Or how can I do this in a different way if I'm off track.
You might be able to use a Derived Identity.
The Parent class would remain the same; but you would specify a #OneToOne mapping back to the child's parent and the Child and ChildKey classes would look like this:
#Entity
#IdClass(ChildKey.class)
#Table(name = "child")
public class Child{
#Id
#OneToOne(mappedBy="child")
private Parent parent;
#Id
private String username;
#Column(name = "data")
private String childData;
}
public class ChildKey implements Serializable {
private String parent; // name matches name of the #Id field and type matches type of Parent #Id field
private String username; // name and type match those of the #Id field
}
Derived identity is discussed in JPA 2.1 spec, section 2.4.1.
What I ended up doing was defining a #Filter on the Child class, like so:
#Entity
#IdClass(ChildKey.class)
#Table(name = "child")
#FilterDef(name = "usernameFilter", parameters = {
#ParamDef( name = "username", type="string")
})
public class Child { ... }
On the Parent class, I annotated the collection with a reference to the filter:
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "parentId")
#Filter(name="usernameFilter", condition = "username = :username")
private List<Child> children;
Finally, in my DAO, I parameterized the filter by name like so:
Filter filter = currentSession().enableFilter("usernameFilter");
filter.setParameter("username", user.getUsername());
Doing this resulted in the exact SQL I had in mind, which is an additional clause in the JOIN criteria with a variable:
SELECT
...
FROM
parent this_
LEFT OUTER JOIN
child child_ ON this_.parentId = child_.parentId
AND child_.username = ?
I might not have been clear about what end result I was looking for in my original question. Posting this answer in case it helps someone else.
I have 3 entities joined via onetoone relations. My goal is get entities using hibernate criteria where match.status != null. And how to tell hibernate to not join algo entity to result, should be (pick.algo = null).
#Entity
public class Pick {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int pid;
#Column(columnDefinition="DATETIME")
private Date insertTime;
#Column(columnDefinition="DATETIME")
private Date setupResTime;
#OneToOne
private DbMatch match;
#OneToOne
private Algo algo;
#Transient
private Integer algoID;
....
Criteria query:
public List<Pick> getPicksHistory(){
Criteria criteria = session.createCriteria(Pick.class);
criteria.add(Restrictions.isNotNull("match.status"));
return criteria.list();
}
from the hibernate documentation (http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#querycriteria-associations) :
Criteria criteria = session.createCriteria(Pick.class);
criteria.createAlias("match", "m");
criteria.add(Restrictions.isNotNull("m.status"));
criteria.setFetchMode("algo", FetchMode.LAZY);
criteria.list();
You can add alias and apply your condition to the alias name
Criteria criteria = session.createCriteria(Pick.class);
criteria.createAlias("match", "match", JoinType.INNER_JOIN); //<---
criteria.add(Restrictions.isNotNull("match.status"));
TO get nulls you can use e.g. JoinType.LEFT_OUTER_JOIN
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.