Hibernate criteria - join multiple tables and form a user object - java

I am trying to join multiple tables and to map the table columns to list of user objects.
Below is the SQL query and I am trying to convert to ORM using Hibernate Criteria:
SELECT table1.domainname, table2.policyname,table3.filterpath,table4.userdirectoryname
FROM table1, table2,table3, table4
WHERE table3.domainoid = table1.domainoid
AND table3.policyoid = table2.policyoid
AND table3.userdirectoryoid = table4.userdirectoryoid
AND table1.domainname = 'admin'
From the above query, we will get a list of user objects and trying to map the results to the user object. Below is the POJO class of the user object to form.
public class DomainDetails {
String domainName, policyName, filterPath, userDirName;
public DomainDetails(String domainName, String policyName, String filterPath, String userDirName) {
super();
this.domainName = domainName;
this.policyName = policyName;
this.filterPath = filterPath;
this.userDirName = userDirName;
}
// getters and setters...
}
How to join the multiple tables and the mapping of the respective columns to the user object?
appreciate the help..thanks

You can use mapping annotation ..
Mapping entity associations/relationships

Related

Want to create dynamic mongo query to accept the DTO fields

I am creating a REST service using springboot and MONGO as database.
I have a StudentDTO class with the following fields :
Class StudentDTO{
#Id
int s_no;
String name;
String dept;
int dept_no;
String course;
//getter and setters
}
I have some criteria on which i need to fetch the data. These criteria may vary. Below are some example :
1. Can fetch data on name, dept
2. Can fetch data on name, id
3. May want data on name, dept and course. etc
There is no fixed combination of criteria on which I can build my query.
One of the solution which is not appropriate i try to write is :
Query query = new Query();
Criteria criteria = new Criteria().andOperator(
Criteria.where("id").is(Integer.parseInt(dto.getId()),
Criteria.where("name").is(dto.getName()),
Criteria.where("dept").exists(true).is(dto.getDept()),
Criteria.where("dept_no").is(dto.getDept_no()),
Criteria.where("course").is(dto.getSource()));
query.addCriteria(criteria);
List<StudentDTO> recordsList = mongoTemplate.find(query, StudentDTO.class, "student_collection");
In the above solution there is no accommodation for the scenario is any of the field is missing.
To check weather attribute exist or not i tried using the below query :
Criteria.where("id").exist(true).is(Integer.parseInt(dto.getId());
but how i can add criteria over the DTO fields.
You can use below code. Use orOperator which accepts the array of criteria. Prepare the criteria values dynamically inside if statements and add the criteria array to or criteria.
Query query = new Query();
Criteria criteria = new Criteria();
List<Criteria> orCriterias = new ArrayList<>();
if( dto.getId() != null) {
orCriterias.add(Criteria.where("id").is(Integer.parseInt(dto.getId())));
}
... so on for other fields
criteria.orOperator(orCriterias.toArray(new Criteria[orCriterias.size()]));
query.addCriteria(criteria);
List<StudentDTO> recordsList = mongoTemplate.find(query, StudentDTO.class, "student_collection");

Hibernate Criteria: How to reset projection

I need to select products based on tags, here are the tables
products: productId, name, description, price, etc
tags: tagId, name
product_tags: productId, tagId
and I have 2 classes Product and Tag and relation is specified in Product class
#OneToMany(cascade = CascadeType.DETACH)
#JoinTable(
name = "product_tags",
joinColumns = #JoinColumn(name = "productId"),
inverseJoinColumns = #JoinColumn(name = "tagId")
)
public List getTags() {
return tags;
}
public void setTags(List tags) {
this.tags = tags;
}
Please note I only want to select products and not tags. following works fine
Criteria cri = getSession().createCriteria(Product.class);
cri.setFirstResult(index);
cri.setMaxResults(limit);
return cri.list();
As I am trying to get list for pagination, so I need total number of pages that can be retrieved by getting totalRecords/recordPerPage
Criteria cri = getSession().createCriteria(Product.class);
//add any required filter to criteria
//e.g cri.add(Restrictions.like("name", keyword, MatchMode.ANYWHERE));
//********** Following code is in utility function ******************/
//Get total number of records matching criteria
cri.setProjection(Projections.rowCount());
Long totalRecords = (Long)cri.uniqueResult();
//Get paginated records
cri.setProjection(null);// This is evil but works
cri.setFirstResult(index);
cri.setMaxResults(limit);
paginatedRecords = cri.list();
Question 1: Is it possible to set some thing like cri.setProjection(Product.class) instead of setting it null, I am aware that I can create a projection list and add all the column of product class but that seems overkill + the common part is in utility function and I found no way to retrieve the previous projection. cri.getProject()
Why I need another method because cri.setProjection(null) fails when I apply join, because it will project all the column of products, tags, product_tags. which cannot be casted to List
Get all products that have associated tag ids as (4,5,6)
cri.createAlias("tags", "t");
cri.add(Restrictions.in("t.tagId", new Integer[]{4,5,6}));
Here is the issued query
select
this_.productId as productId1_9_1_,
this_.categoryId as category6_9_1_,
this_.description as descript8_9_1_,
this_.name as name13_9_1_,
tags3_.productId as productId1_9_,
t1_.tagId as tagId2_10_,
t1_.tagId as tagId1_11_0_,
t1_.name as name4_11_0_,
from
products this_
inner join
product_tags tags3_
on this_.productId=tags3_.productId
inner join
tags t1_
on tags3_.tagId=t1_.tagId
where t1_.tagId in (4,5,6) limit 25
I have found a work-around for this as follows
cri.setProjection(null)
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
This will fix type cast exception.
Question 2: The back-end query is still the same, it join and project all the columns of all involved tables. ((Yakkh dirty)), Why the same query?, I am expecting projections on Product class only

Hibernate native sql join query

I have a problem with hibernate native sql join query. My query is below and works on Mysql db.
SELECT c.cart_id, u.name, u.surname, c.totalPrice
FROM sandbox.cart c JOIN
sandbox.user u
ON u.id = c.placedBy
I am using hibernate in code and encountered an exception
java.sql.SQLException: Column 'id' not found.
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1093)
Query in code here
Session session = hibernateUtil.getSessionFactory().getCurrentSession();
SQLQuery query = session.createSQLQuery(ORDER_PER_USER_QUERY);
query.addEntity(OrderPerUser.class);
return query.list();
Table column name
Cart
| cart_id | placedBy | totalPrice
User
| id | email | name | surname
My mapped class is
#Entity
public class OrderPerUser {
#Id
private long id;
private String name;
private String surName;
private long cartId;
private double totalPrice; }
You need to remove the line:
query.addEntity(OrderPerUser.class);
After that, you need to rewrite the code and map your object manually, because your OrderPerUser is not an entity:
Session session = hibernateUtil.getSessionFactory().getCurrentSession();
SQLQuery query = session.createSQLQuery(ORDER_PER_USER_QUERY);
List<OrderPerUser> returnList new ArrayList<>();
for(Object[] row : query.list()){
OrderPerUser orderPerUserObj = new OrderPerUser();
oderPerUserObj.setCartId(Long.parseLong(row[0].toString()));
//put other properties here
returnList.add(orderPerUserObj);
}
return returnList;
Edit1: Now I see that you added the mapped class, but OrderPerUser should not be an entity in your case, but a regular DTO. An entity requires an ID, but you can't select the ID in this case, because OrderPerUser is not part of a table, it is just some selected data that you want in your memory and not in the database. So you should make your OrderPerUser a regular data transfer object.
Please read about entities, data transfer objects, data access objects to see what each object should do.
My guess is that your OrderPerUser class which you try to use for collecting the result is expecting a column with name id, and you have no such column in your query...
Try using the query:
SELECT u.id, c.cart_id, u.name, u.surname, c.totalPrice
FROM sandbox.cart c
JOIN sandbox.user u ON u.id = c.placedBy

Hibernate: group by very slow

I have performance problems with the group by feature in Hibernate. Consider this 2 classes:
public class Project
{
...
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="user")
private User user;
...
}
public class User
{
...
private Integer id;
private String name;
...
}
Now I try to get a list of all Users assigned to the project. But this query is uselessly slow (more than 100'000 project entries, a lot of joins):
Session session = this.sessionFactory.openSession();
String SQL="SELECT user.id as id, user.name as name FROM Project p GROUP BY p.user.id";
Query q = session.createQuery(SQL);
q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List<Object> list = q.list();
session.close();
I try to change the query this way, but this is not working either because the variable user is an Object (but this would work as a native SQL query):
SELECT id, name FROM User WHERE id IN(SELECT user FROM Project GROUP BY user)
Any other ideas? Thank you in advance!
Try creating an index from the foreign key column.
#javax.persistence.Table(name = "project")
#Table(appliesTo = "project" ,indexes = #Index(columnNames = "user", name = "user_index"))
#Entity
public class Project
{
..
Update
columnNames has been depricated. Use columnList instead.
#Index(columnList = "user", name = "user_index")
Hope this helps.
I doubt this has to do with Hibernate being slow. Most probably the SQL query is slow if run directly on the database as well.
One good practice is to create indices whenever you have a foreign key in your table. In your case create an index for user_id on your project table and run the query once more.
If you want to get all users assigned to the project you don't need group by. Do something like this
select user from Project project inner join project.user user where project.id = :projectId

Get Hibernate Entity instance from id column in SQLQuery result

I have (non-Hibernated) database tables that contain ids for Hibernate entities. I can query them (using createSQLQuery), which gives me the ids, from which I can then load the entities.
I'd like to do that in one step, and I think I can do that with addEntity, but I am not sure how exactly. (Hibernate's documentation web site is down. Again.) I can use addEntity when all the columns for the entity table are present, but I have only the id now.
This complains about the missing columns:
return (List<MyEntity>) session.createSQLQuery(
"select entity_id from the_table where foreign_key_value = ?")
.addEntity("entity_id", MyEntity.class)
.setLong(0, foreignKey).list();
I think you want something like:
session.createSQLQuery("select {entity.*} from entity_table {entity} where ....")
.addEntity("entity", Entity.class).(bind-parameters).list();
Hibernate will expand "{entity.*}" to be the relevant columns from entity_table.
Although if you already have the IDs, you can simply use session.load() to convert those to actual instances (well, lazy-load proxies).
i would use a join
select *
from entity_table
where entity_id = (select entity_id
from non_hibernate_table
where id = ?)
For oracle dialect. If u have problem with mapping database column type to java data type u can set it manually like that: .addScalar("integerFieldName", Hibernate.INTEGER)
public class LookupCodeName
{
private String code;
private String name;
/*... getter-setters ... */
}
public class someBL {
public List<LookupCodeName> returnSomeEntity() {
SQLQuery sqlQuery = (SQLQuery)((HibernateSession)em).getHibernateSession()
.createSQLQuery( "SELECT st.name as name, st.code as code FROM someTable st")
.addScalar("code")
.addScalar("name")
.setResultTransformer(Transformers.aliasToBean(LookupCodeName.class));
}
return (List<LookupCodeName>)sqlQuery.list();
}

Categories