Having issues with distinct in JPA - java

Using JPA, I want to select all Log objects for a specific actiontype. From the log object I want to get the user (log.getUser()), but the users appear several times in the result list. I tried it with distinct, but it did not work, I guess because I was not able to define, what exactly has to be distinct. Here is my JPA query:
SELECT DISTINCT log
FROM Log AS log JOIN log.action AS action
JOIN log.user AS user
WHERE action.actionType = :actionType
If I say SELECT DISTINCT user, then I don't have the whole log object in the end.
Any help or hint would be appreciated.
Edit:
Part of my Log Class:
public class Log {
private int logId;
private Calendar logDate;
private User user;
private Action action;
private String description;
....
}

Two Queries Solution
Since you want Log objects and distinct User objects you could do two queries, first one to retrieve the Log objects and second one to retrieve distict User objects.
// first one to select Log objects
String logQuery = "SELECT l FROM Log l WHERE l.actionType = :actionType";
...
List<Log> logs = logJpaQuery.getResultList();
// second one to select distinct users from this objects
String usersQuery = "SELECT distinct l.user FROM Log l where l.logId in (:logIds)";
...
userJpaQuery.setParameter("logIds", logs);
List<User> users = userJpaQuery.getResultList();
With this approach you have the distinct users for the select Logs objects.

Related

Efficient JPQL query for retrieving complex entities

I'm quite the rookie with JPA/JPQL, so please excuse me if this question is not crystal clear.
I am trying to find an efficient JQPL query in order to get all records of a complex object.
(ie. represented by multiple tables, with several one-to-many relationships - see simplified example below):
class ComplexObject {
private Set< SubOject1> so1 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject1>...
}
class SubObject1 {
private Set< SubOject2> so2 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject2>...
}
I am using the following JPQL query :
select distinct CO
from ComplexObject CO
left join fetch CO.so1 SO1
left join fetch SO1.so2
The query is run on a stateless session, in order to get a de facto snapshot of the current data in the DB, which is detached from the entity manager (hence the usage of left join fetch).
Unfortunately, I've encountered 2 problems :
Since the complex object contains multiple instances of so1, and each so1 instance contains multiple instances of so2, the underlying translation to SQL queries generates a specific select query per row of the product of all the table joins - a very wasteful solution. Is there a way to reduce the number of internal select queries? (This seems like the dreaded N+1 queries problem).
The JPQL query returns a ComplexObject instance per internal SQL query on the product of all the table joins - which means multiple references to the ComplexObject instances. Why does this happen on a 'select distinct' query?
The JPA framework I am using is hibernate, and the DB is HyperSQL.
The (1) issue turned out to be related to using the p6spy logging framework, which printed out all the results from a large DB table. The logging format led to an incorrect assumption that many queries where being executed.
While trying to fine tune performance, using native queries did not appear to have better performance then using JPQL queries.
Using a Native Query also resulted in Object typed results, which required post processing.
You can use View Objects to receive only the columns what you want:
StringBuilder sb = new StringBuilder();
sb.append(" SELECT new ").append(ObjectVO.class.getName()).append("(co.someInfo1, co.someInfo2, so1.someInfo )");
sb.append(" FROM ComplexObject co ");
sb.append(" JOIN co.subOject1s so1 ");
sb.append(" LEFT JOIN so1.so2 so2 ");
sb.append(" WHERE so1.id = :idSo1 AND so2 = :someThing");
Query q = em.createQuery(sb.toString());
q.setParameter("idSo1", idSo1);
q.setParameter("someThing", someThing);
List<ObjectVO> listResult = q.getResultList();
The ObjectVO class:
public class ObjectVO {
private String info1;
private Long info2;
private String info3;
public PedidoModel(String info1, Long info2, String info3){
this.info1 = info1;
this.info2 = info2;
this.info3 = info3;
}
}

how to get only selected table from stored procedures?

I have a stored Procedure like the following
CREATE PROCEDURE [dbo].[spConfiguration_test]
#ID int
AS
select empid,name from employee;
select * from address;
I wanted to call this stored procedure from jpa.So I did like this
DAOcode
public List test()
{
String execProce="exec spConfiguration_test 1";
System.out.println(execProce);
Query query = entityManagerUtil.entityManager.createNativeQuery(execProce);
return query.getResultList();
}
service class Code
List test=serviceDaoImpl.test();
when I debug this then List(test) size is showing 1 and when I run it then it gives me only the records of 1st table(select empid,name from employee;)
But I want the details of 2nd table when the stored procedure is executed.
Can any one guide me please?
If you merge the two queries into one. Then everything must work.
Example:
SELECT e.empid
,e.name
,a.*
FROM employee e
,address a
WHERE e.empid = a.empid;

HQL JOIN involving multiple associations

I have three entities with relation UserDetails hasmany FiledTask and Task hasMany FiledTask. All I want is list of FiledTask of particular User
For UserDetails:
#OneToMany(mappedBy="user",cascade=CascadeType.ALL)
Collection<FiledTask> filedTasks = new ArrayList<FiledTask>();
And for Tasks I have
#OneToMany(mappedBy="task")
Collection<FiledTask> filedTasks = new ArrayList<FiledTask>();
And FiledTask looks like
#ManyToOne
#JoinColumn(nullable = false, name = "taskId")
private Tasks task;
#ManyToOne
#JoinColumn(nullable = false, name = "userId")
private UserDetails user;
I have tried
query = session.createQuery("from Tasks as tasks inner join tasks.filedTasks as files with files.user = :user");
query.setParameter("user", user); //user is UserDetails instance
But I am getting error clause can only reference columns in the driving table, means FiledTask can't userId for comparision?
with clause can only reference columns in the driving table [from com.akhi.app.cdm.Tasks as tasks inner join tasks.filedTasks as files with files.user = :user]
If you want the FiledTasks of a given user, then the easiest thing to do is
UserDetails user = session.find(UserDetails.class, userId);
Collection<FiledTask> filesTasks = user.getFiledTasks();
Using HQL, it would be
select f from FiledTask f where f.user = :user
If what you want is in fact the tasks of a given user, then the query would simply be
select t from Tasks t
inner join t.filedTasks filedTask
where filedTask.user = :user
Note that the entity Tasks shouldbe named Task. An instance represents a single task, and not multiple ones.
OK, looks like there is a bug in Hibernate
https://hibernate.atlassian.net/browse/HHH-2772
I think in your case what you need to do is
query = session.createQuery("from Tasks as tasks inner join tasks.filedTasks as files with files.user.id = :userId");
query.setParameter("userId", user.id);
Hope this helps somebody. I spend several hours on this.
I had the same problem using a MySql (Inno) DB plus my dialect class derived from Hibernate's MySQL5InnoDBDialect - class.
I had to override the supportsRowValueConstructorSyntax-method and let it return false as MySQLDialect (which is the base class of MySQL5InnoDBDialect) returns true.
#Override
public boolean supportsRowValueConstructorSyntax() {
return false;
}
Doing this, hibernate does not use the object reference (in your case .user) in order to create a tuple for the comparison. I hope, this will help.

Writing a Hibernate Criteria API Query with restrictions for multiple sub-elements

I have a data model that looks like this (simplified example):
public class Address { private List<AddressLine> addressLines; }
public class AddressLine { private String value; private String type; }
I am trying to use the Criteria API to search for Addresses in the database that contain specific combinations of AddressLines. For example, to retrieve all addresses that contain the address lines {(type="CITY", value="London"), (type="COUNTRY", value="GB")}. I haven't been able to find any examples of such a query.
As far as I have been able to get is to query for an Address based on a single AddressLine.
session.createCriteria(Address.class)
.createCriteria("addressLines")
.add(Restrictions.and(Restrictions.eq("type", type), Restrictions.eq("value", value))).list()
If I add a restriction for a second address lines the SQL that hibernate generates is basically asking SELECT x WHERE x.y = 'a' AND x.y = 'b' so will never return any results.
I have found similar questions being asked before but none of them have an accepted or voted for answer.
You need to write the Criteria equivalent of
select a from Address a where
exists (select line1.id from AddressLine line1 where line1.address.id = a.id
and line1.type = 'CITY'
and line1.value = 'London')
and exists (select line2.id from AddressLine line where line2.address.id = a.id
and line2.type = 'COUNTRY'
and line2.value = 'GB')
This means writing a DetachedCriteria for each subquery, with an id projection, and using these detached criterias as argument of two Subqueries.exists() calls. The alias of the address entity in the main criteria can be used in the detached criterias to implement the line1.address.id = a.id restriction.

How do I write a HQL Query for this?

I want a single HQL query that returns all groups containing a given user that where created before a given date. Somehow I can't get it right.
public class Group
{
#ManyToMany
Set<User> users;
Date created;
}
public class User
{
...
}
II-Bhima's answer is essentially right - here is a little fix:
select g from Group as g
inner join g.users as user
where g.created < :createdDate
and user = :user
you need it so that Groups are returned and not Object[] with Group-User 2-tuples.

Categories