Query problems with Hibernate (JPA) and PostgreSQL - java

I'm trying to use PostgreSQL as the database for Hibernate/JPA. However, I get an error when executing a simple query. My query is as follows:
SELECT DISTINCT p FROM UserProfile p ORDER BY :order
When I execute the query, I'll pass in a value like "lastLoginDate" for :order. However, I get the following exception when trying to execute the query:
ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
Position: 781
This query works just fine using MySQL as the database, but we'd like to move towards PostgreSQL. My overall configuration and database connection is correct, as I see the tables being created properly. Is there something I'm missing? Is there a configuration property I need to set? Any help you can give would be MUCH appreciated. Thanks!

Postgresql prohibits that query because it is ambiguous:
there's actually a definitional reason for it. Consider
SELECT DISTINCT x FROM tab ORDER BY y;
For any particular x-value in the table there might be many different y
values. Which one will you use to sort that x-value in the output?
It's not very clear what you want (an example?), but if you intend to sort all records by ':order' column, and then remove duplicated values, you can do that first with DISTINCT ON: , and then reorder:
SELECT p FROM
( SELECT DISTINCT ON (p) * from UserProfile ORDER BY p , :order)
AS UserProfileUniq
ORDER BY :order ;

Related

SQLite to Oracle 0 records showing up

Hey all I have the query below that I wrote when I was using SQLite:
SELECT
ad.ID,ad.Script_Name,ad.Current_Status,
ad.Issues_found_during_run,ad.Testers,
ad.Run_Date,ad.Tools,u.fTag,u.role,u.avatar
FROM
allData ad
INNER JOIN
users u
ON
u.fTag = ad.lastUserWhoUpdated
GROUP BY
ad.ID
ORDER BY
ad.ID ASC
That produces the 6 records I would imagine would come up with that query.
However, I have since moved to using Oracle database. This is my information on the Oracle server I am using:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
So when I convert the SQLite data to Oracle and run the same query with just a modification for the GROUP BY since it seems Oracle wants all the names that are in the SELECT statement and not just the one I need to group by:
SELECT
ad.ID,ad.Script_Name,ad.Current_Status,
ad.Issues_found_during_run,ad.Testers,
ad.Run_Date,ad.Tools,u.fTag,u.role,u.avatar
FROM
allData ad
INNER JOIN
users u
ON
u.fTag = ad.lastUserWhoUpdated
GROUP BY
ad.ID,ad.Script_Name,ad.Current_Status,ad.Issues_found_during_run,ad.Testers,ad.Run_Date,ad.Tools,u.fTag,u.role,u.avatar
ORDER BY
ad.ID ASC;
That above produces 0 records when ran in SQL Developer. So, what would Oracle need me to do in order to fix this so it pulls the 6 records as did the SQLite version?
You can use something like this:
SELECT
ad.ID,MAX(ad.Script_Name), MAX(ad.Current_Status),
MAX(ad.Issues_found_during_run), MAX(ad.Testers),
MAX(ad.Run_Date), MAX(ad.Tools), MAX(u.fTag),MAX(u.role),MAX(u.avatar)
FROM
allData ad
INNER JOIN
users u
ON
u.fTag = ad.lastUserWhoUpdated
GROUP BY
ad.ID
ORDER BY
ad.ID ASC;
More on Aggregrate functions here:
https://docs.oracle.com/database/121/SQLRF/functions003.htm
It doesn't make sense (to me, at least). There's no WHERE clause which would restrict number of rows returned. GROUP BY certainly doesn't have to do anything with that. The only suspicious thing is
on u.fTag = ad.lastUserWhoUpdated
which caused no rows to be retrieved.
Therefore:
are you sure that there are matching values in those two tables?
check those two columns' datatypes, especially if one (or both) are CHAR (I mean CHAR, not VARCHAR2) which right-pads values with blanks up to the whole column size, so you might try with
on trim(u.fTag) = trim(ad.lastUserWhoUpdated)
which has its drawbacks (index won't be used, unless it is a function-based) so - if it turns out that it is the case, modify datatype to VARCHAR2.

Hibernate group by with counts

In SQL I can do something like this:
select m.*,sq.c from message m inner join (
select max(m.id) as id, count(m.id) as c
from message m group by m.externalid
) sq on sq.id=m.id;
i.e. group the messages table, select by max(id) for each group and then return all the columns plus a count.
Can I do this in Hibernate (ideally JPQL) returning the Messages object plus the count value?
Or what's the best alternative - one query to fetch messages and then a separate one for the counts seems like it could go wrong. But I'm reluctant to have to trawl over a List if I can avoid it.
Edit:
This is as close as I can get to the equivalent Apple example from the link in the comments:
select new com.example.MessageStat(m, count(m)) from message as m group by m.externalid
But it gives this error:
ERROR [2018-08-22 11:35:54,600] org.hibernate.engine.jdbc.spi.SqlExceptionHelper: ERROR: column "message0_.id" must appear in the GROUP BY clause or be used in an aggregate function
Which I had with the SQL before adding the inner join, but I haven't been able to figure out the equivalent JPQL for that.

CLOB and CriteriaQuery

I have an entity that has a CLOB attribute:
public class EntityS {
...
#Lob
private String description;
}
To retrieve certain EntityS from the DB we use a CriteriaQuery where we need the results to be unique, so we do:
query.where(builder.and(predicates.toArray(new Predicate[predicates.size()]))).distinct(true).orderBy(builder.asc(root.<Long> get(EntityS_.id)));
If we do that we get the following error:
ORA-00932: inconsistent datatypes: expected - got CLOB
I know that's because you cannot use distinct when selecting a CLOB. But we need the CLOB. Is there a workaround for this using CriteriaQuery with Predicates and so on?
We are using an ugly workaround getting rid of the .unique(true) and then filtering the results, but that's crap. We are using it only to be able to keep on developing the app, but we need a better solution and I don't seem to find one...
In case you are using Hibernate as persistence provider, you can specify the following query hint:
query.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false);
This way, "distinct" is not passed through to the SQL command, but Hibernate will take care of returning only distinct values.
See here for more information: https://thoughts-on-java.org/hibernate-tips-apply-distinct-to-jpql-but-not-sql-query/
Thinking outside the box - I have no idea if this will work, but perhaps it is worth a shot. (I tested it and it seems to work, but I created a table with just one column, CLOB data type, and two rows, both with the value to_clob('abcd') - of course it should work on that setup.)
To de-duplicate, compute a hash of each clob, and instruct Oracle to compute a row number partitioned by the hash value and ordered by nothing (null). Then select just the rows where the row number is 1. Something like below (t is the table I created, with one CLOB column called c).
I expect that execution time should be reasonably good. The biggest concern, of course, is collisions. How important is it that you not miss ANY of the CLOBs, and how many rows do you have in the base table in the first place? Is something like "one chance in a billion" of having a collision acceptable?
select c
from (
select c, row_number() over (partition by dbms_crypto.hash(c, 3) order by null) as rn
from t
)
where rn = 1;
Note - the user (your application, in your case) must have EXECUTE privilege on SYS.DBMS_CRYPTO. A DBA can grant it if needed.

Running a nativeQuery in a loop doesn't return correct data

I am trying to run native query in a loop, the query displays the correct sql syntax but the output is always the same.
for (int i=0; i<translations.size(); i++) {
Query query = entityManager.createNativeQuery("Select * from " + translations.get(i).getName(), MyModel.class);
rows = (List<MyModel>)query.getResultList();
// rest of the function...
}
now in the console I can see the Hibernate statements like:
Hibernate: Select * from translation1
Hibernate: Select * from translation2
Hibernate: Select * from translation3
but the variable "rows" always contains the result of the first select statement i.e. rows of translation1 table.
Any ideas why in the console it shows that it is selecting from other tables too but in reality it always gets data from translation1 table?
If all tables have the same set of ids, it's an expected behaviour.
Hibernate session cache guarantees that there can be only one instance of an entity of a particular type with a particular id inside a session. Since entities are resolved via the session cache even in the case of a native query, you get the same instances.
So, you have several options:
rethink your database shema
construct objects from query result manually
forcibly clear the session cache by calling clear() or detach()
Are you sure that the rows variable doesn't actually contain the LAST of your returned results? how do you initialize rows and what do you do with the variable inside the loop? If you want to get all the results from all the queries you have to add each one (inside the loop) to a list/set variable declared BEFORE the loop starts.
Or you could use some proper SQL and do:
SELECT * FROM Table1 [JOIN/UNION] Table2 etc...

How to order by count() in JPA

I am using This JPA-Query:
SELECT DISTINCT e.label FROM Entity e
GROUP BY e.label
ORDER BY COUNT(e.label) DESC
I get no errors and the results are sorted almost correct but there are some values wrong (either two values are flipped or some single values are completly misplaced)
EDIT:
Adding COUNT(e.label) to my SELECT clause resolves this problem for this query.
But in a similar query which also contains a WHERE clause the problem persists:
SELECT DISTINCT e.label, COUNT(e.label) FROM Entity e
WHERE TYPE(e.cat) = :category
GROUP BY e.label
ORDER BY COUNT(e.label) DESC
You might need to include the COUNT(e.label) in your SELECT clause:
SELECT DISTINCT e.label, COUNT(e.label)
FROM Entity e
GROUP BY e.label
ORDER BY COUNT(e.label) DESC
UPDATE: Regarding the second query please read section 8.6. Polymorphic queries of the EntityManager documentation. It seems that if you make your queries in a way that requires multiple SELECTs, then the ORDER BY won't work anymore. Using the TYPE keyword seems to be such a case. A quote from the above link:
The following query would return all persistent objects:
from java.lang.Object o // HQL only
The interface Named might be implemented by various persistent classes:
from Named n, Named m where n.name = m.name // HQL only
Note that these last two queries will require more than one SQL SELECT. This means that the order by clause does not correctly order the whole result set. (It also means you can't call these queries using Query.scroll().)
For whatever reason the following style named query didn't work for me:
SELECT DISTINCT e.label, COUNT(e.label)
FROM Entity e
GROUP BY e.label
ORDER BY COUNT(e.label) DESC
It could be because I am using an old version of Hibernate. I got the order by working by using a number to choose the column to sort by like this:
SELECT DISTINCT e.label, COUNT(e.label)
FROM Entity e
GROUP BY e.label
ORDER BY 2 DESC
Can't see how the order could be incorrect. What is the incorrect result?
What is the SQL that is generated, if you try the same SQL directly on the database, does it give the same incorrect order?
What database are you using?
You could always sort in Java instead using sort().

Categories