DISTINCT on a single column - jpa

I'm using spring data JPA along with Hibernate. I have to find all entries by id, however I'm selecting only some of the columns.
I managed to do this by using specified constructor
#Query("SELECT new Foo(f.field1, f.field2, f.field3)
FROM FooTable f WHERE f.field1 = :field")
I need to make field1 as a DISTINCT, however putting this column into DISTINCT(field1) doesn't work.
Is there a way to make it work?

Try this:
#Query("SELECT new Foo(f.field1, f.field2, f.field3)
FROM FooTable f, FooTable f2
WHERE f.field1 = :field
AND f.field1 != f2.field1")
Note that it'll miss the null values.

Related

I am having a hard time joining between unrelated domains in JPA

I am experiencing various things while studying JPA, but I am too unfamiliar with it, so I would like to get some advice.
The parts I got stuck in during my study were grouped into three main categories. Could you please take a look at the code below?
#Repository
public interface TestRepository extends JpaRepository<TestEntity,Long> {
#Query(" SELECT
, A.test1
, A.test2
, B.test1
, B.test2
FROM TEST_TABLE1 A
LEFT JOIN TEST_TABLE2 B
ON A.test_no = B.test_no
WHERE A.test3 = ?1 # Here's the first question
if(VO.test4 is not null) AND B.test4 = ?2") # Here's the second question
List<Object[] # Here's the third question> getTestList(VO);
}
First, is it possible to extract test3 from the VO received when using native sql?
Usually, String test1 is used like this, but I wonder if there is any other way other than this.
Second, if extracting is possible in VO, can you add a query in #QUERY depending on whether Test4 is valued or not?
Thirdly, if I use List<Object[]>, can the result of executing a query that is not in the already created entity (eg, test1 in TEST_TABLE2, which is not in the entity of TEST_TABLE1) can be included?,
First, is it possible to extract test3 from the VO received when using native sql? Usually, String test1 is used like this, but I wonder if there is any other way other than this.
Yes, it is possible.
You must use, eg where :#{[0].test3} is equals vo.test3
[0] is position the first param, past for method annotated with #Query
#Query(value = "SELECT a.test1, a.test2, b.test1, b.test2
FROM test_table1 a
LEFT JOIN test_table2 b ON a.test_no = b.test_no
WHERE a.test3 = :#{[0].test3}", nativeQuery = true)
List<Object[]> getList(VO);
Second, if extracting is possible in VO, can you add a query in #QUERY depending on whether Test4 is valued or not?
You can use a trick eg:
SELECT ... FROM table a
LEFT JOIN table b ON a.id = b.id
WHERE a.test3 = :#{[0].test3}
AND (:#{[0].test4} IS NOT NULL AND b.test4 = :#{[0].test4})
Thirdly, if I use List<Object[]>, can the result of executing a query that is not in the already created entity (eg, test1 in TEST_TABLE2, which is not in the entity of TEST_TABLE1) can be included?
Sorry, but I not understand the third question.
Maybe this tutorial will help you: https://www.baeldung.com/jpa-queries-custom-result-with-aggregation-functions

Using Hibernate to get the latest row with a LocalDateTime

I have a Java8 application using SpringBoot, which is pulling in Hibernate core 5.3.10 (connected to PostgreSQL 11). A simplified explanation: the application is maintaining a history of changes to a series of bespoke UserData records, containing two LocalDateTime columns for startDate and endDate, and a userId column (plus other columns).
The semantics of the application are that a history of changes to the UserData are maintained by the start_date and end_date contain no duplicates (respectively), and that any row where the endDate is null is the currently active record for a user. (Accordingly, a user may have no currently active record.)
It is a simple matter to retrieve all of the active rows, and this is working well.
However, I have a requirement to retrieve the latest row for all users irrespective of whether the row is active or not.
One way to achieve this outcome using SQL is with a query something like the following:
select ud1.* from user_data ud1
where ud1.start_date = (select max(ud2.start_date) from user_data ud2
where ud2.user_id = ud1.user_id);
I have been attempting to write a CriteriaQuery to replicate the SQL, but I have run into a data type problem with the CriteriaBuilder.max method. The max method is complaining that it will only accept a type Number column. The code looks like this:
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<UserDate> criteriaQuery = builder.createQuery(UserData.class);
final Root<UserData> ud1 = criteriaQuery.from(UserData.class);
final Subquery<LocalDateTime> maxUserDataStartDate = criteriaQuery.subquery(LocalDateTime.class);
final Root<UserData> ud2 = maxUserDataStartDate.from(UserData.class);
maxUserDataStartDate.select(builder.max(ud2.get("startDate"));
// ...
The problem is with the last line, where it complains that ud2.get("startDate") is not an extension of type Number - which is true, of course.
Does anybody know how to fix this situation? Especially, does anybody have an example they can share that does what I'm after?
You can do order by start_date desc and get top 1
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<UserData> criteriaQuery = builder.createQuery(UserData.class);
Root<UserData> ud = criteriaQuery.from(UserData.class);
criteriaQuery.orderBy(builder.desc(ud.get("startDate")));
entityManager.createQuery(criteriaQuery).getFirstResult();

QueryDSL coalesce in order by

I'm trying to join two tables and output them and sort them alphabetically by two fields like order by coalesce(tableA.name, tableB.name) (NOT order by tableA.name, tableB.name), so result should be something like:
tableA.name tableB.name
A null
B null
null C
D null
null E
In plain SQL it works fine but when I try to do it with QueryDSL it adds additional column to generated select statement and sorts only by first specified column:
//java code
query.orderBy(qTableA.name.coalesce(qTableB.name).asc());
//generated sql code
SELECT ...
COALESCE(tablea_.NAME, tableb_.NAME) AS col_9_0_
FROM ...
WHERE ...
ORDER BY tablea1_.NAME ASC
Can somebody tell why it does it like that and whether it is possible to make it work as I expect?
Try this:
final Coalesce<String> coalesce =
new Coalesce<>(String.class).add(optionalA).add(optionalB);
Use the coalesce in your select fields and so in your order by clause:
.orderBy(coalesce.asc()) // or desc()

Get count from query

I am using mysql 5.5 with openjpa 2.3.0.
I have entities with namedQueries (generated in netbeans - I would like to be able to use this), for example:
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
#NamedQuery(name = "User.findByGender", query = "SELECT u FROM User u WHERE u.gender = :gender")
I am creating restfull aplication with paged results. I would like to return for every paged result the Content-Range header as 1-20/250 where 20 is pagesize, 250 total count.
I tried to create a query
entityManager.createNativeQuery("SELECT count(1) FROM (" + namedQuery.toString() + ") as foo;");
where I could dynamicaly insert any named query and return the count without returning the result list -> it should be faster.
When I execute this, exception occurs
SQL state 42S22: Unknown column 'u' in 'field list'
Executing the query itself in entitymanager is ok.
Can I use the entity manager or criteria builder to create a query for counting results without returning the result list (and without writing for every namedQuery a count duplicate)? thank you for helping.
You are mixing JPQL with native queries. JPQL says SELECT u FROM entity u, SQL would be SELECT * FROM entity u or SELECT col1,col2,col3 FROM entity u
You could write a JPQL named query counting the stuff, e.g. SELECT COUNT(u) FROM entity u. A getSingleResult() would then return an Object[], whose first element contains the count.
Not nice but working. Why do You have to query for the number anyway? Pagination means next = lastindex+pagesize. if next < lastindex+pagesize, the end is reached.

SqlResultSetMapping with self join table

I have a query with a self join that looks like this,
select t1., t2. from table t1
left outer join table t2 on t2.LFT < t1.LFT
and t2.RGT > t1.RGT
AND t2.REG_CODE_PAR = 'ALL'
AND t1.STATUS_CODE = 'A'
AND t2.STATUS_CODE = 'A'
I'm using #NamedNativeQuery with a result set mapping to get the result.
#NamedNativeQuery(
name="findTree",
query="..... the query above",
resultSetMapping = "regionT")
With the following result set mapping
#SqlResultSetMapping(name = "regionT" , entities ={
#EntityResult(
entityClass = Tree.class
fields = {
#FieldResult(name = "regCode", column = "REG_CODE")
#FieldResult(name = "rgt", column = "RGT"),
#FieldResult(name = "lft", column = "LFT"),
#FieldResult(name = "name", column = "NAME"),
#FieldResult(name = "regCodePar", column = "REG_CODE_PAR"),
#FieldResult(name = "statusCode", column = "STATUS_CODE")
}
),
#EntityResult(
entityClass = TreeSelf.class
fields = {
#FieldResult(name = "regCode1", column = "REG_CODE")
#FieldResult(name = "rgt1", column = "RGT"),
#FieldResult(name = "lft1", column = "LFT"),
#FieldResult(name = "name1", column = "NAME"),
#FieldResult(name = "regCodePar1", column = "REG_CODE_PAR"),
#FieldResult(name = "statusCode1", column = "STATUS_CODE")
}
)
})
The entity class contains looks like this.
#NamedNativeQuery(...)
#SqlResultSetMapping(...)
#Entity
#Table(name = "table")
public class Tree implements Serializable {
#Id
#Column(name = "REG_CODE")
private String regCode; ... ..getters and setters...}
When I run the query using em.createQuery("findTree"), I get the exact same object in both
the 1st and 2nd elements of the returned object array.
Even if I create a class called TreeSelf that is identical to Tree and use it as the 2nd
EntityResult instead of having 2 EntityResults using the same entityClass, I get the same
result.
Can someone point out what's wrong with the configuration?
Let's see if I understand your question. You're expecting to capture two Tree entities from each native query result row. The first entity should be formed from t1's columns. The second entity should be formed from t2's columns. Contrary to expectation, you actually receive two instances formed from t1. No instances from t2 appear. You made a doppelganger Entity for Tree called TreeSelf while debugging, but TreeSelf is ultimately unnecessary and you want to get rid of it. Stop me if any of that was wrong.
I think the problem is due to ambiguous column names. Each column name in the native query appears twice, once from t1 and once from t2. The result mapper seems to be arbitrarily picking the first occurrence of each ambiguous column name for both Tree entities. I'm surprised that works at all. I would have expected an SQLException complaining about column reference ambiguity.
Also, are you sure you want a left outer join? What if no match is found for a t1 row? It will be paired with all NULL in t2's columns. Then you have a null-valued Tree entity. I think. I don't even know what the result mapper would do in that case. Perhaps you want an inner join?
Consider translating this native query into a JPQL query. (JPA Criteria API is just as well, but I find it more cumbersome for examples.) Here's a JPQL version of the native query:
SELECT t1, t2
FROM Tree t1, Tree t2
WHERE t2.lft < t1.lft AND t2.rgt > t1.rgt AND t2.regCodePar = 'ALL' AND
t1.statusCode = 'A' AND t2.statusCode = 'A'
N.B.: This changes the join semantics to inner instead of left outer.
Here's a sketch of code that could run this query:
EntityManager em = ... // EntityManager by injection, EntityManagerFactory, etc.
String jpql = ... // Like example above
TypedQuery<Object[]> q = em.createQuery(jpql, Object[].class);
for (Object[] oa : q.getResultList()) {
Tree t1 = (Tree)oa[0];
Tree t2 = (Tree)oa[1];
}
In case you are stuck with the native query for whatever reason, here's how you can work around the column name ambiguity. Instead of starting the native query like select t1.*, t2.*, alias each column with AS. The SELECT clause would resemble this:
SELECT t1.REG_CODE AS t1_REG_CODE, t1.RGT AS t1_RGT, (... rest of t1 cols ...),
t2.REG_CODE AS t2_REG_CODE, t2.RGT AS t2_RGT, (... rest of t2 cols ...)
The column attribute in each FieldResult must change accordingly. So the column attributes under the first EntityResult should all start with t1_ and the second's should all start with t2_.
I'd humbly recommend deleting the native query and sql result mapper and using JPA Query Language or Criteria API, if you can find a way.
Update: As confirmed in your comments, a useful answer to your question must preserve left (outer) join semantics. Unfortunately, JPQL and the Criteria API don't support complex left join conditions. There is no way to qualify a JPQL left join with an explicit ON condition.
To my knowledege, the only way to do a left outer join under the spec is by traversing an entity relationship. The JPA implementation then generates an ON condition that tests identity equality. The relevant spec bits are 4.4.5 "Joins" and 4.4.5.2 "Left Outer Joins".
To satisfy this constraint, each Tree you want to left-join to its ultimate parent must have an additional column storing the ultimate parent's id. You might be able to cheat around this constraint in a variety of ways (views?). But the path of least resistance seems to be modifying the native query to use aliased arguments, deleting TreeSelf, and updating the result mapper accordingly. Cleverer solutions welcome, though...