I'm stuck with the following problem - i have a MappedSuperClass entity with InheritanceType.JOINED, and some entities, extending that superclass. And when i try to query this entity with criteria api, i'm getting strange result set, which contains only one of sub entities.
The code as follows:
Base parent class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Parent
{
#Id
private long id;
/** getters and setters */
}
Mapped superclass, which contains shared attribute:
#MappedSuperclass
public class Attributes extends Parent
{
#Column
private String name;
/** getters and setters */
}
First subentity:
#Entity
#Table(name = "classA")
public class SubClassA extends Attributes
{
#Column
private String lastName;
/** getters and setters */
}
Second subentity:
#Entity
#Table(name = "classB")
public class SubClassB extends Attributes
{
#Column
private String secondName;
/** getters and setters */
}
And the criteria:
Criteria criteria = session.createCriteria(Parent.class);
Criterion restrictions = Restrictions.ilike("name", "n%");
Junction conditionGroup = Restrictions.conjunction();
conditionGroup.add(restrictions);
criteria.add(conditionGroup);
As a result, i've got the following sql:
select
this_.id as id1_0_0_,
this_1_.name as name1_1_0_,
this_1_.lastName as lastName2_1_0_,
this_2_.name as name1_2_0_,
this_2_.secondName as secondNa2_2_0_,
case
when this_1_.id is not null then 1
when this_2_.id is not null then 2
when this_.id is not null then 0
end as clazz_0_
from Parent this_
left outer join classA this_1_ on this_.id=this_1_.id
left outer join classB this_2_ on this_.id=this_2_.id
where
(lower(this_1_.name) like ?)
As i can see, the selection is nearly i've expected, except the "where" clause, which includes only this_1_.name, which is alias for classA, but not classB. Both tables (classA and classB) are filled with data, but, the result set contains data only from classA table.
Other observation shows, that, if i change InheritanceType to TABLE_PER_CLASS, the result would be correct, because it uses union:
select
this_.id as id1_0_0_,
this_.name as name1_1_0_,
this_.lastName as lastName2_1_0_,
this_.name as name1_2_0_,
this_.secondName as secondNa2_2_0_,
this_.clazz_ as clazz_0_
from
(
select
id,
cast(null as varchar(100)) as name,
cast(null as varchar(100)) as lastName,
cast(null as varchar(100)) as secondName,
0 as clazz_
from Parent
union all
select
id,
name,
lastName,
cast(null as varchar(100)) as secondName,
1 as clazz_
from classA
union all select
id,
name,
cast(null as varchar(100)) as lastName,
secondName,
2 as clazz_ from classB
) this_ where (lower(this_.name) like ?)
So, to make a long story short, the there is a question - is there a way to make restriction queries for InheritanceType.JOINED, which would cover ALL subclasses? Something like:
where
( (lower(this_1_.name) like ?) or
(lower(this_2_.name) like ?)
)
Thanks in advance for your replies!
Related
I have a JPA entity (Person) with onetomany relation (ContactInfo).
#Entity
public class Person {
#Id
#GeneratedValue
private Integer id;
private String name;
private String lastname;
private String sshKey;
#OneToMany(mappedBy = "personId")
private List<ContactInfo> contactInfoList;
}
#Entity
public class ContactInfo {
#Id
#GeneratedValue
private Integer id;
private Integer personId;
private String description;
}
I've defined a projection interface that includes this onetomany relation as described here.
public interface PersonProjection {
Integer getId();
String getName();
String getLastname();
List<ContactInfo> getContactInfoList();
}
public interface PersonRepository extends JpaRepository<Person,Integer> {
List<PersonProjection> findAllProjectedBy();
}
When I retrieve the data with findAllProjectedBy the result contains too many rows. It looks like the returned data is the result of a join query similar to:
select p.id, p.name, p.lastname, ci.id, ci.person_id, ci.description
from person p
join contact_info ci on ci.person_id = p.id
For example for this data set:
insert into person (id,name,lastname,ssh_key) values (1,'John','Wayne','SSH:KEY');
insert into contact_info (id, person_id, description) values (1,1,'+1 123 123 123'), (2,1,'john.wayne#west.com');
The findAllProjectedBy method returns 2 objects (incorrectly) and the standard findAll returns 1 object (correctly).
Full project is here
I've done some debugging and it seems that the problem is with the jpa query.
The findAll method uses this query:
select generatedAlias0 from Person as generatedAlias0
The findAllProjectedBy uses this query:
select contactInfoList, generatedAlias0.id, generatedAlias0.name, generatedAlias0.lastname from Person as generatedAlias0
left join generatedAlias0.contactInfoList as contactInfoList
Does anyone know how to fix this invalid behaviour?
A quick fix for this problem is described here:
https://jira.spring.io/browse/DATAJPA-1173
You need to describe one of the single projection attributes with a #Value annotation. For the example posted above you will end up with:
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
public interface PersonProjection {
#Value("#{target.id}")
Integer getId();
String getName();
String getLastname();
List<ContactInfo> getContactInfoList();
}
I have those Entities, It is not from real code, but it's not important I think.
#Entity
public class Country{
private String name;
//other fields with getters and setters
#OneToMany
private List<City> cites;
}
#Entity
public class City {
private String name;
//other fields with getters and setters
#OneToMany
private List<Employee> emps;//list size could be millions
}
#Entity
public class Employee {
private String name;
//other fields with getters and setters
#OneToMany
private List<Cars> cars;// list size could be 1000
}
#Entity
public class Car {
private String name;
#ManyToOne
private Employee emp;
}
I would like to get single Car entity but with other data as well(Country, City, Employee) like this
1 Country in which 1 City in which 1 Empoyee in which 1 Car(which Id I put on select)
So when I do jpa joins from Country like
select c from Country c
inner join c.cites ci
inner join ci.emps em
inner join ci.cars car where car.id = ?
I get ALL data in Country(with all cities).
What should I do to get 1 Country,1 City , 1 Employee, 1 Car.
If this is not possible to do with one jpa query then please suggest other way.
All relationships are bidirectional and lazy.
I tried this way.
1)select car by id. then get Id of employee from car.
2)select Employee by id - but at this point Employee data are nulls -
I think it is because ManyToOne from Car to Employee is lazy. What do you think ?
Just select the additional entities you want:
select c, ci, em, car from Country c
inner join c.cites ci
inner join ci.emps em
inner join ci.cars car where car.id = ?
Or, since your associations are bidirectional, select the car: it will have a ManyToOne to its employee, which will have a ManyToOne with its city, which will have a ManyToOne with its country:
select car from Car car where car.id = ?
or simply
em.find(Car.class, carId);
and now you can do
car.getEmployee().getCity().getCountry()
If all of the relationships are bidirectional then I would suggest starting from Car and then fetching up the hierarchy.
select c from Car car
inner join fetch car.emp emp
inner join fetch emp.city city
inner join fetch city.country country
where car.id = ?
Just remember to add that fetch in all of the joins that you missed.
Updated Question
I have 3 tables like
Section (id, title, url)//id is Primary key
Category (id, title)//id is Primary key
SubCategory (id, title)//id is Primary key
Here, I want to join these table as in simple query like
Select * From Category cat, Section se, SubCategory subCat WHERE
subCat.id=23456 AND subCat.category=cat.id AND subCat.section = se.id
How can I achieve the above query using Criteria in Hibernate? Can
anyone help me on this?
Adding my question here
My entity Files are as:
#Entity
#Table(name="section")
public class SectionEntity{
private int id;
private String title;
//Getter & setter method
}
#Entity
#Table(name="category")
public class CategoryEntity{
private int id;
private String title;
private Set<SubCategoryEntity> subCategory;
//Getter & setter method
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
#JoinColumn(name="category", insertable=false, updatable=false)
public Set<SubCategoryEntity> getSubCategory(){
return this.subCategory;
}
}
Updated SubCategoryEntity
#Entity
#Table(name="subcategory")
public class SubCategoryEntity{
private int id;
private String title;
private Set<SectionEntity> sectionEntity;
//Getter & setter method
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="id", insertable=false, updatable=false)
public Set<SectionEntity> getSectionEntity(){
this.section;
}
}
Steps to use Hibernate Criteria as:
Step 1: Creating criteria for Category Entity
Criteria categoriesCriteria = session.createCriteria(CategoriesEntity.class, "categoryEntity");
Step 2: Creating aliases of SubCategoryEntity and SectionEntity
categoriesCriteria.createAlias("categoryEntity.subCategoryEntity", "subCatEntity");
categoriesCriteria.createAlias("subCatEntity.sectionsEntity", "subCatSectionEntity");
Step 3: Set the property in projection list
Step 4: Add Restriction as:
categoriesCriteria.add(Restrictions.eq("subCatEntity.id", primCategoryId));
Step 5: Set projection property into CategoryEntity Criteria
categoriesCriteria.setProjection(projPropList);
Step 6: Getting result
categoriesCriteria.list();
My resulting query displaying as:
select this_.id as y0_, this_.title as y1_, this_.sef_url as y2_, subcatenti1_.id as y3_, subcatenti1_.title as y4_, subcatenti1_.sef_url as y5_
from jos_categories this_
inner join jos_subcategories subcatenti1_ on this_.id=subcatenti1_.category
inner join jos_sections subcatsect2_ on subcatenti1_.id=subcatsect2_.id
where subcatenti1_.id=?
But I have required expected query as:
select this_.id as y0_, this_.title as y1_, this_.sef_url as y2_, subcatenti1_.id as y3_, subcatenti1_.title as y4_, subcatenti1_.sef_url as y5_
from jos_categories this_
inner join jos_subcategories subcatenti1_ on this_.id=subcatenti1_.category
inner join jos_sections subcatsect2_ on subcatenti1_.section=subcatsect2_.id
where subcatenti1_.id=?
How Can I achieve this, can anyone help me on this?
In your SubCategoryFile make change like this,
1) remove "private int section;" and all getter and setter method of this.
2) and use this
#JoinColumn(name="section", insertable=false, updatable=false)
public Set<SectionEntity> getSection(){
this.section;
}
and then try to run. I hope it will work
I'm trying to add additional condition to Join clause using hibernate criteria. In fact, there are some methods, that allow this to do:
createCriteria(String associationPath, String alias, int joinType, Criterion withClause)
and
createAlias(String associationPath, String alias, int joinType, Criterion withClause)
They work properly with one-to-one and one-to-many relations. But when I'm trying to use them with entities having many-to-many relations, I'm getting following error:
Caused by: org.postgresql.util.PSQLException: No value specified for parameter 1.
Can anybody help me?
The rude example is below:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#ManyToMany
private Set<PersonName> names;
}
public class PersonName {
#Id
#GeneratedValue
private Long id;
#Column
private String name;
}
public class PersonDao extends HibernateDaoSupport {
public List<Person> findByName() {
Criteria criteria = getSession().createCriteria(Person.class, "p");
criteria.createCriteria("p.names", "names", JoinType.INNER_JOIN, Restrictions.eq("name", "John"));
return criteria.list();
}
}
the Query being generated is
select this_.id as y0_ from person this_
inner join debtor_info this_1_ on this_.id=this_1_.id
left outer join person_person_name personname3_ on this_.id=personname3_.person_id and ( name1_.name=? )
left outer join person_name name1_ on personname3_.person_name_id=name1_.id and ( name1_.name=? )
As you can see, join condition is being added two times, what is obviously incorrect
Thanks in advance.
BTW I'm using postgresql 9, Hibernate 3.6.3
This is a bug HHH-7355
Hibernate criteria JOIN + additional condition (with clause) don't work with many-to-many association and it will not be fixed because Hibernate Criteria API is deprecated and you should use JPA Crtiterias.
You can try to use HQL with clause
from Cat as cat
left join cat.kittens as kitten
with kitten.bodyWeight > 10.0
I thought I know how to use JOIN in JPQL but apparently not. Can anyone help me?
select b.fname, b.lname from Users b JOIN Groups c where c.groupName = :groupName
This give me Exception
org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing the query
Internal Exception: org.eclipse.persistence.internal.libraries.antlr.runtime.EarlyExitException
Users have a OneToMany relationship with Groups.
Users.java
#Entity
public class Users implements Serializable{
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
List<Groups> groups = null;
}
Groups.java
#Entity
public class Groups implements Serializable {
#ManyToOne
#JoinColumn(name="USERID")
private Users user;
}
My second question is let say this query return a unique result, then if I do
String temp = (String) em.createNamedQuery("***")
.setParameter("groupName", groupName)
.getSingleResult();
*** represent the query name above. So does fname and lname concatenated together inside temp or I get a List<String> back?
Join on one-to-many relation in JPQL looks as follows:
select b.fname, b.lname from Users b JOIN b.groups c where c.groupName = :groupName
When several properties are specified in select clause, result is returned as Object[]:
Object[] temp = (Object[]) em.createNamedQuery("...")
.setParameter("groupName", groupName)
.getSingleResult();
String fname = (String) temp[0];
String lname = (String) temp[1];
By the way, why your entities are named in plural form, it's confusing. If you want to have table names in plural, you may use #Table to specify the table name for the entity explicitly, so it doesn't interfere with reserved words:
#Entity #Table(name = "Users")
public class User implements Serializable { ... }