Good Day,
I have a Hibernate mapping that looks something like this:
public class Item implements Serializable {
private lond id;
private String Name;
private boolean status;
...
}
public class ItemHolder implements Serializable {
private long id;
private List<Item> items;
...
}
How can I do query to get all Item Holder's witch contains my Item?
(with criteria)
You just need a join:
Criteria c = session.createCriteria(ItemHolder.class, "itemHolder");
c.createAlias("itemHolder.items", "item");
c.add(Restrictions.eq("item.id", theItemId);
Criteria criteria=session.createCriteria(ItemHolder.class);
criteria.createAlias("items", "item");
criteria.add(Restrictions.eq("item.Name", "my Item").ignoreCase());
you can restrict with your requirement : id or name..
The query could be something like:
Item loadedItem = ...;
Query query = session.createQuery("from ItemHolder ih where :item in elements(ih.items)");
query.setParameter("item", loadedItem);
List list = query.list();
Related
What is the best way to query an Object and return its list attributes in a sorted list?
Example:
Given the scenario below, what is the best way to do a Groups query, by bringing: the list of Items ordered by name and (for each item) the list of StateItem ordered by the Description of StateObject?
public class Group {
// ...
#Fetch(FetchMode.JOIN)
#OneToMany(mappedBy = "group")
private List<ItemGroup> itemList;
// ...
}
public class ItemGroup {
// ...
#JoinColumn(name="ID_GROUP", referencedColumnName="ID_GROUP")
#ManyToOne
private Group group;
#Fetch(FetchMode.JOIN)
#OneToMany(mappedBy = "item")
private List<StateItem> stateList;
// ...
}
public class StateItem {
// ...
#JoinColumn(name="ID_ITEM", referencedColumnName="ID_ITEM")
#ManyToOne
private ItemGroup item;
#JoinColumn(name="CD_STATE", referencedColumnName="CD_STATE")
#ManyToOne
private StateObject state;
// ...
}
public class StateObject {
// ...
#Column(name="DE_STATE_OBJECT", length=255)
private String description;
// ...
}
PS: I think I can't use #OrderBy because I have to sort by child attributes, as in the StateObject example.
And the solution with #SortNatural or #SortComparator isn't performatic.
Can I do that work using only HQL or Criteria?
You can use org.hibernate.annotations.#SortNatural if StateItem implements comparable or you can use org.hibernate.annotations.#SortComparator and provide a comparator.
NOTE : This solution does in-memory sorting which is different than ordering (as provided by #OrderBy) which is applied during the SQL SELECT.
I recommend you to use the #SortComparator annotation which accepts a default value the class implementing Comparator<T>.
#Fetch(FetchMode.JOIN)
#SortComparator(StateItemComparator.class)
#OneToMany(mappedBy = "item")
private List<StateItem> stateList
Its method should define the comparing strategy.
public class StateItemComparatorComparator implements Comparator<StateItem> {
#Override
public int compare(StateItem s1, StateItem s2) {
return b1.getState().getDescription().compareTo(b2.getState().getDescription());
}
}
i need to translate this sql query to jpa criteria:
SELECT tbl1.id_t1, tbl2.name, tbl3.name, tbl4.symbol, tbl1.limit, tbl1.value, tbl1.uncertainty
FROM table_1 AS tbl1
JOIN table_2 AS tbl2 ON tbl2.id_t2=tbl1.id_t2
JOIN table_3 AS tbl3 ON tbl3.id_t3=tbl1.id_t3
JOIN table_4 AS tbl4 ON tbl4.id_t4=tbl1.id_t4
WHERE (tbl2.id_l=1 AND tbl3.id_l=1) AND tbl1.id_s=1;
my mapping between pojo and database table are as follows:
Table_1
#Entity
#Table("table_1")
public class Table1 {
#Id
#Column(name="id_t1")
private Long idRowT1
#ManyToOne
#JoinColumn(name="id_t2")
private Table2 tbl2;
#ManyToOne
#JoinColumn(name="id_t3")
private Table3 tbl3;
#ManyToOne
#JoinColumn(name="id_t4")
private Table4 tbl4;
#Column(name="limit")
private String limit;
#Column(name="value")
private String value;
#Column(name="uncertainty")
private String uncertainty;
// getter and setter
}
Table_2
#Entity
#Table("table_2")
public class Table2 {
#Id
#Column(name="id_t2")
private Long idT2;
// getter and setter
}
Table_2_lang
#Entity
#Table("table_2_lang")
#IdClass(Table2LangPK.class)
public class Table2Lang {
#Id
#Column(name="id_t2")
private Long idT2;
#Id
#Column(name="id_l")
private Lang l;
#Column(name="name")
private String name;
// getter and setter
}
Table_3
#Entity
#Table("table_3")
public class Table3 {
#Id
#Column(name="id_t3")
private Long idT3;
// getter and setter
}
Table_3_lang
#Entity
#Table("table_3_lang")
#IdClass(Table3LangPK.class)
public class Table3Lang {
#Id
#Column(name="id_t3")
private Long idT3;
#Id
#Column(name="id_l")
private Lang l;
#Column(name="name")
private String name;
// getter and setter
}
Table_4
#Entity
#Table("table_4")
public class Table4 {
#Id
#Column(name="id_t4")
private Long idT4;
#Column(name="name")
private String name;
// getter and setter
}
To send data from business layer to front-end i'm using value objects defined as follows:
Simple entity
public class SimpleEntityVO {
private Long entityId;
private String name;
// getter and setter
}
Complex Entity
public class SimpleEntityVO {
private Long entityId;
private SimpleEntityVO tbl2VO;
private SimpleEntityVO tbl3VO;
private SimpleEntityVO tbl4VO;
// ... other field of table_1
// getter and setter
}
In my EJB i need to implement a method that return a list of ComplexEntityVO starting from Table_1
...
private CriteriaBuilder cB = eM.getCriteriaBuilder();
public List<ComplexEntityVO> findAll(Long id_s, Long id_l) {
CriteriaQuery<ComplexEntityVO> cQ = cB.createQuery(ComplexEntityVO.class)
Root<Table1> tbl1Root = cQ.from(Table1.class);
// UPDATE BEGIN
Root<Table2Lang> tbl2Root = cQ.from(Table2Lang.class);
...
Selection<SimpleEntityVO> sESTbl2 = cB.construct(SimpleEntityVO.class, tbl2Root.get(Table2Lang_.id_t2), tbl2Root.get(Table2Lang_.name));
// The selection for table_3_lang and table_4 are the same
// UPDATE END
TypedQuery<ComplexEntityVO> tQ = eM.createQuery(cQ);
}
...
To achieve the results i've tried with join betwen Table1 and Table2Lang, tried with selection like the one exposed below
`Selection<SimpleEntityVO> sES = cB.construct(SimpleEntityVO.class, ...);`
using Root for lang table, tried with solution exposed here
https://community.oracle.com/message/10795956#10795956
but when i try to execute this statement
`cQ.select(cB.construct(ComplexEntityVO.class, id_t1, SimpleEntityVO)`
or this
`cQ.multiselect(...)`
i get the: IllegalArgumentException
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: , near line 1, column 64
[select new com.example.vo.ComplexEntityVO(generatedAlias0.id_t1,
new com.example.labims.vo.SimpleEntityVO(generatedAlias1.table2.id_t2, generatedAlias1.name),
new com.example.vo.SimpleEntityVO(generatedAlias2.table_3.id_t3, generatedAlias2.name),
new com.example.vo.SimpleEntityVO(generatedAlias3.id_t4, generatedAlias3.name),
generatedAlias0.limit, generatedAlias0.value, generatedAlias0.uncertainty)
from com.example.Table1 as generatedAlias0,
com.example.model.Table2Lang as generatedAlias1,
com.example.model.Table3Lang as generatedAlias2,
com.example.model.Table4 as generatedAlias3
where ( generatedAlias0.id_s=:param0 ) and ( ( generatedAlias1.lang.id_l=:param1 ) and ( generatedAlias2.lang.id_l=:param1 ) )]
From the cause of execption understanded that i can't instanciate new object inside select or multiselect statement, but i don't find a way to achieve the original SQL query using criteria API.
UPDATE
i've added an excerpt of what i've tried to achieve the result between //UPDATE BEGIN and //UPDATE END
I think make hibernate show sql == true
and take query by console,test showed query your databases and find error hbernate not generate query correct
There are two approaches to solve this problem.
Add a constructor method to ComplexEntityVO like this:
public ComplexEntityVO(Long id, Long simpleId2, String simpleName2 /* etc ... */) {
this.simpleEntityVo = new SimpleEntityVO(simpleId2, simpleName2);
// etc for other associations
}
add a ProjectionList to your query, return a List<Object[]> instead of a List<ComplexEntityVO> and then iterate over the results like so
for(Object[] o: results) {
ComplexEntityVO cvo = new ComplexEntityVO((Long)o[0]);
new SimpleEntityVO vo2 = new SimpleEntityVO((Long) o[1], (String) o[2]);
cvo.setTbl2VO(vo2);
// ... etc for other associations
}
Although the second is uglier I would prefer it, since it is more flexible, and allows more opportunities for debugging, logging etc.
See AliasToBeanResultTransformer(MyDTO.class) fails to instantiate MyDTO
I have 3 tables named role,permission and role_permission and their corresponding pojos
public class RoleTO
{
private int roleId;
private String roleName;
private int applicationId;
}
,
public class RolePermissionsTO
{
private int role_permissionId;
private int roleId;
private int permissionId;
}
,
public class PermissionTO
{
private int permissionId;
private String permission;
private String permissionDesc;
private int applicationId;
}
Now I have a method in my dao class
public List<PermissionTO> getPermissions(int applicationId, String roleName)throws HibernateException
{
Session session = getCurrentSession();
String hql = "SELECT P FROM PermissionTO P,Role R where P.applicationId=R.applicationId and P.applicationId=:applicationId and P.roleName=:roleName";
Query query = session.createQuery(hql);
query.setParameter("applicationId",applicationId);
query.setParameter("roleName",roleName);
return query.list();
}
But I need these three tables to be connected so that I get all the permissions in the permission class for the given application Id and the roleName linking the three tables.
Can anybody help
SELECT P FROM PermissionTO P,Role R
where P.applicationId=R.applicationId
AND R.applicationId=:applicationId
AND R.roleName=:roleName";
OR
Criteria cr = session.createCriteria(PermissionTO.class, "permissionTo");
cr.createAlias("permissionTo.permissionId", "rolePermissionsTo"); // inner join by default
cr.createAlias("rolePermissionsTo.roleId", "roleTo");
cr.add(Restrictions.eq("roleTo.applicationId", applicationId)); // applicationId set value to parameter
cr.add(Restrictions.eq("roleTo.roleName", roleName)); // roleName set value to parameter
List list = cr.list();
I also have a similar issue, but couldnt find a solution.
but in your query I find that you should be using some constraint to connect the RoleTO to RolePermissinTo using RoleId and ApplicationId and then use this result to get the corresponding PermissionTO list for each given role.
I am not expert with queries, i guess this should be the logic
I want to ask, it is possible that I create query projections and criterion for more than one level deep?
I have 2 model classes:
#Entity
#Table(name = "person")
public class Person implements Serializable {
#Id
#GeneratedValue
private int personID;
private double valueDouble;
private int valueInt;
private String name;
#OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
#JoinColumn(name="wifeId")
private Wife wife;
/*
* Setter Getter
*/
}
#Entity
#Table(name = "wife")
public class Wife implements Serializable {
#Id
#GeneratedValue
#Column(name="wifeId")
private int id;
#Column(name="name")
private String name;
#Column(name="age")
private int age;
/*
* Setter Getter
*/
}
My Criteria API :
ProjectionList projections = Projections.projectionList();
projections.add(Projections.property("this.personID"), "personID");
projections.add(Projections.property("this.wife"), "wife");
projections.add(Projections.property("this.wife.name"), "wife.name");
Criteria criteria = null;
criteria = getHandlerSession().createCriteria(Person.class);
criteria.createCriteria("wife", "wife", JoinType.LEFT.ordinal());
criterion = Restrictions.eq("wife.age", 19);
criteria.add(criterion);
criteria.setProjection(projections);
criteria.setResultTransformer(Transformers.aliasToBean(Person.class));
return criteria.list();
and I hope, I can query Person, with specified criteria for wife property, and specified return resultSet.
so i used Projections for getting specified return resultSet
I want personID, name(Person), name(Wife) will returned. how API i must Use, i more prefer use Hibernate Criteria API.
This time, I used code above for getting my expected result, but it will throw Exception with error message :
Exception in thread "main" org.hibernate.QueryException: could not resolve property: wife.name of: maladzan.model.Person,
and whether my Restrictions.eq("wife.age", 19); is correct for getting person which has wife with 19 as her age value ?
Thanks
AFAIK it is not possible to project more than one level deep with aliastobean transformer. Your options are
create a flattened Data Transfer Object (DTO)
fill the resulting Person in memory yourself
implement your own resulttransformer (similar to option 2)
option 1 looks like this:
Criteria criteria = getHandlerSession().createCriteria(Person.class)
.createAlias("wife", "wife", JoinType.LEFT.ordinal())
.add(Restrictions.eq("wife.age", 19));
.setProjection(Projections.projectionList()
.add(Projections.property("personID"), "personID")
.add(Projections.property("name"), "personName")
.add(Projections.property("wife.name"), "wifeName"));
.setResultTransformer(Transformers.aliasToBean(PersonWifeDto.class));
return criteria.list();
I wrote the ResultTransformer, that does this exactly. It's name is AliasToBeanNestedResultTransformer, check it out on github.
Thanks Sami Andoni. I was able to use your AliasToBeanNestedResultTransformer with a minor modification to suit my situation. What I found was that the nested transformer did not support the scenario where the field is in a super class so I enhanced it to look for fields up to 10 levels deep in the class inheritance hierarchy of the class you're projecting into:
public Object transformTuple(Object[] tuple, String[] aliases) {
...
if (alias.contains(".")) {
nestedAliases.add(alias);
String[] sp = alias.split("\\.");
String fieldName = sp[0];
String aliasName = sp[1];
Class<?> subclass = getDeclaredFieldForClassOrSuperClasses(resultClass, fieldName, 1);
...
}
Where getDeclaredFieldForClassOrSuperClasses() is defined as follows:
private Class<?> getDeclaredFieldForClassOrSuperClasses(Class<?> resultClass, String fieldName, int level) throws NoSuchFieldException{
Class<?> result = null;
try {
result = resultClass.getDeclaredField(fieldName).getType();
} catch (NoSuchFieldException e) {
if (level <= 10){
return getDeclaredFieldForClassOrSuperClasses(
resultClass.getSuperclass(), fieldName, level++);
} else {
throw e;
}
}
return result;
}
My Hibernate projection for this nested property looked like this:
Projections.projectionList().add( Property.forName("metadata.copyright").as("productMetadata.copyright"));
and the class I am projecting into looks like this:
public class ProductMetadata extends AbstractMetadata {
...
}
public abstract class AbstractMetadata {
...
protected String copyright;
...
}
Instead of creating Data Transfer Object (DTO)
In projectionlist make below changes and it will work for you.
ProjectionList projections = Projections.projectionList();
projections.add(Projections.property("person.personID"), "personID");
projections.add(Projections.property("person.wife"), "wife");
projections.add(Projections.property("wife.name"));
Criteria criteria = null;
criteria = getHandlerSession().createCriteria(Person.class,"person").createAlias("person.wife", "wife");
criterion = Restrictions.eq("wife.age", 19);
criteria.add(criterion);
criteria.setProjection(projections);
criteria.setResultTransformer(Transformers.aliasToBean(Person.class));
return criteria.list();
I'm using DBUtils in my simple project. I have Item and Person entity class (persons and items as tables in database). This simplified the class to better show what I mean. Now i need to get list of items with login names using BeanListHandler. To do this i added login property to Item, but this is ugly solution. Is something better to do that and use advantages of BeanListHandler?
public class Person {
private Long id;
private String login;
}
public class Item {
private Long id;
private String name;
// ... a lot more properties of item
private Long personId; // this is real column in "items" table
private String login; // UGLY (login is not in "items" table, only for BeanListHandler)
}
QueryRunner q = new QueryRunner(getDataSource());
String sql = "select i.*, p.login from items i, persons p where p.id = i.personId";
List<Item> l = (List<Item>) q.query(sql, new BeanListHandler<Item>(Item.class));