Is it possible to disable jpa hints per particular query? - java

JPA, and in my particular case eclipselink, generates /*+ FIRST_ROWS */ in case of using query.setFirstResult()/query.setMaxResults():
SELECT * FROM (
SELECT /*+ FIRST_ROWS */ a.*, ROWNUM rnum FROM (
SELECT * FROM TABLES INCLUDING JOINS, ORDERING, etc.) a
WHERE ROWNUM <= 10 )
WHERE rnum > 0;
That forces Oracle to use nested loops instead of hash-joins. In general is has seance, but in my particular case it dramatically decrease performance.
Is it possible to disable hint usage/generation for a particular query?

As #ibre5041 told, FIRST_ROWS hint is deprecated, in context of Oracle, FIRST_ROWS(N) should be used instead of it. In my case neither FIRST_ROW nor FIRST_ROW(N) is actually needed, so in order to tell eclipselink not to use outdated stuff, it's possible to specify oracle version within persistence.xml:
<property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.oracle.Oracle11Platform" />
After adding this, I got strange error:
Could not initialize class org.eclipse.persistence.platform.database.oracle.Oracle11Platform
However, after I put ojdbcN.jar to domain/lib/ext the error has gone.
As a result, eclipselink generates query without FIRST_ROW hint, and Oracle uses better plan.

Related

How to use SUM inside COALESCE in JOOQ

Given below is a gist of the query, which I'm able to run successfully in MySQL
SELECT a.*,
COALESCE(SUM(condition1 or condition2), 0) as countColumn
FROM table a
-- left joins with multiple tables
GROUP BY a.id;
Now, I'm trying to use it with JOOQ.
ctx.select(a.asterisk(),
coalesce(sum("How to get this ?")).as("columnCount"))
.from(a)
.leftJoin(b).on(someCondition)
.leftJoin(c).on(someCondition))
.leftJoin(d).on(someCondition)
.leftJoin(e).on(someCondition)
.groupBy(a.ID);
I'm having a hard time preparing the coalesce() part, and would really appreciate some help.
jOOQ's API is more strict about the distinction between Condition and Field<Boolean>, which means you cannot simply treat booleans as numbers as you can in MySQL. It's usually not a bad idea to be explicit about data types to prevent edge cases, so this strictness isn't necessarly a bad thing.
So, you can transform your booleans to integers as follows:
coalesce(
sum(
when(condition1.or(condition2), inline(1))
.else_(inline(0))
),
inline(0)
)
But even better than that, why not use a standard SQL FILTER clause, which can be emulated in MySQL using a COUNT(CASE ...) aggregate function:
count().filterWhere(condition1.or(condition2))

How to add the result of a query into a column

I have the following query which returns a column of values:
SELECT CONCAT(
from_unixtime(lastSaleTime/1000, '%Y-%d-%m %h:%i:%s.'),
CAST(EXTRACT(MICROSECOND FROM from_unixtime(lastSaleTime/1000))/1000
AS SIGNED)
) FROM IEX_Tick;
How can I copy this column to an existing column in the same table?
I believe a subquery would do:
UPDATE IEX_Tick SET SomeColumn = (
SELECT CONCAT(
from_unixtime(lastSaleTime/1000, '%Y-%d-%m %h:%i:%s.'),
CAST(EXTRACT(MICROSECOND FROM from_unixtime(lastSaleTime/1000))/1000 AS SIGNED)
) FROM IEX_Tick;
)
Edit:
(in response to this comment)
After doing some research, I found that, even though the suggested solution above is valid SQL, it's not a supported way of updating a table in MySQL.
Information I gathered from various related posts here on Stack Overflow (such as this and this) suggests that MySQL restricts these types of UPDATE queries:
... because your update could be cyclical… what if updating that record causes something to happen which made the WHERE condition FALSE? You know that isn’t the case, but the engine doesn’t. There also could be opposing locks on the table in the operation.
Therefore, a viable alternative that helps avoid this arbitrary restriction set by MySQL is to use another subquery, this time in your FROM clause, as shown below:
UPDATE IEX_Tick SET SomeColumn = (
SELECT CONCAT(
from_unixtime(lastSaleTime/1000, '%Y-%d-%m %h:%i:%s.'),
CAST(EXTRACT(MICROSECOND FROM from_unixtime(lastSaleTime/1000))/1000 AS SIGNED)
) FROM (
SELECT * FROM IEX_Tick AS SomeName
);
)
Note: I would personally avoid using the SELECT * FROM IEX_Tick, especially if IEX_Tick has many columns. I good way to optimise the subquery would be to use only the column(s) needed.

JOOQ slow code generation

Are there any parameters, which can turn on/off execution of next query during jooq code generation?
SELECT "SYS"."ALL_OBJECTS"."OWNER",
"SYS"."ALL_OBJECTS"."OBJECT_NAME",
"SYS"."ALL_OBJECTS"."OBJECT_ID",
"SYS"."ALL_PROCEDURES"."AGGREGATE"
FROM "SYS"."ALL_OBJECTS"
LEFT OUTER JOIN "SYS"."ALL_PROCEDURES"
ON ( "SYS"."ALL_OBJECTS"."OWNER" =
"SYS"."ALL_PROCEDURES"."OWNER"
AND "SYS"."ALL_OBJECTS"."OBJECT_NAME" =
"SYS"."ALL_PROCEDURES"."OBJECT_NAME")
WHERE ( UPPER ("SYS"."ALL_OBJECTS"."OWNER") IN ( 'MYSCHEMA')
AND "SYS"."ALL_OBJECTS"."OBJECT_TYPE" IN ( 'FUNCTION', 'PROCEDURE'))
ORDER BY "SYS"."ALL_OBJECTS"."OWNER" ASC,
"SYS"."ALL_OBJECTS"."OBJECT_NAME" ASC,
"SYS"."ALL_OBJECTS"."OBJECT_ID" ASC
On database with large number of schemas and objects it tooks about one hour to be executed
One major issue with the query run by jOOQ is the UPPER(OWNER) expression. This was introduced with jOOQ 2.4 (#1418) to prevent misconfigurations where users accidentally use lower case schema names. The feature was based on an erroneous assumption that case-sensitive users are impossible. They are certainly possible (even if rare), so #1418 was wrong. I've created two issues for this problem:
#5989: Fix the performance issue by avoiding functions on the OWNER column
#5990: Re-enact case-sensitive schema names
In the meantime, you have some possible workarounds:
Pre jOOQ 3.8
You can always override the JavaGenerator from jooq-codegen and re-implement some methods including generatePackages() and generateRoutines() to be empty. This way, the relevant code will not be executed at all.
Of course, this means you won't get any generated packages and routines.
Post jOOQ 3.8
There is a new configuration option where you can do the same as above configuratively:
<configuration>
<generator>
<database>
<includeRoutines>false</includeRoutines>
<includePackages>false</includePackages>
...
See also:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-advanced/codegen-config-include-object-types

Change Hibernate sequence generation for existing data

I recently came across this behavior of Hibernate when trying to move from identity column to sequence based id generation for our business entities. The problem is that we have been using hibernate_sequence generation on Oracle since the first deployment of our application.
So the question is: is it safe to set the following Hibernate's parameter to true on an existing production database without messing up the already generated ids?
hibernate.id.new_generator_mappings=true
EDIT:
The Hibernate documentation describes that the change is not backwards compatible with existing databases.
We are using HIBERNATE_SEQUENCE.NEXTVAL in database migrations and this is clearly not at all safe to do, since the NEXTVAL might conflict with a pre-existing id.
If I am not terribly wrong, backwards compatibility could be gained by setting the HIBERNATE_SEQUENCE's current value to the maximum id in the database. Am I correct? Is there any way to find this out?
If you want to migrate to hibernate.id.new_generator_mappings=true, you have to do two things:
Set the increment by of the database sequences to the same value as the allocationSize in Hibernate. The default allocationSize is 50.
After setting hibernate.id.new_generator_mappings=true Hibernate will produce values starting with SEQ.NEXTVAL-allocationSize. So you have to increment the sequence with allocation size. That means selecting a SEQ.NEXTVAL.
Here is the Oracle PL/SQL script I was running before the migration:
DECLARE
v NUMBER;
BEGIN
FOR r IN (select sequence_name from user_sequences) LOOP
EXECUTE IMMEDIATE 'ALTER SEQUENCE '|| r.sequence_name ||' INCREMENT BY 50';
EXECUTE IMMEDIATE 'SELECT '|| r.sequence_name ||' .NEXTVAL FROM DUAL' INTO v;
END LOOP;
END;
/

Java SQL [ limit | rownum ... ] uniformization API

I'm faced with the task of adding "DB pagination" while executing the query and not while serving the user with results.
The problem is that each DB engine has it's own approach to SQL for subtracting a set of results from the entire set. To be more precise, if one wants results N to N+d using:
mysql ---> select X from Y where Z limit N, d
oracle --> select * ( select X, ROW_NUMBER() OVER ( ORDER BY Y.colName ) from Y where Z ) WHERE R BETWEEN N and N+d
For now we provide support for Oracle & MySQL, but one can never know the clients requests, so I am trying to have a general implementation available, therefore I am looking for a library that provides some functionality like this:
qWithSubset = performLimitationOverQuery( qNoSubset, offset, amount, sortOnSet )
Any suggestion is welcome. Thank you.
Any JPA implementation, such as Hibernate for example, provides a level of abstraction between your code and database.
To be fair, it does much more than dealing with pagination. You could, however, look at and / or borrow its implementation of database "dialects" to deal with pagination without changing the way you're dealing with database in general if you were so inclined.

Categories