Is it possible to alter the SELECT/WHERE of jOOQ DSL query - java

I would like manipulate a jOOQ DSL query changing its SELECT columns and WHERE conditions.
For example:
DSLContext ctx = ...;
SelectHavingStep query = ctx.select(MyEntity.MY_ENTITY.ZIP, DSL.count(MyEntity.MY_ENTITY.ZIP))
.from(MyEntity.MY_ENTITY)
.where(MyEntity.MY_ENTITY.ID.gt("P"))
.groupBy(MyEntity.MY_ENTITY.ZIP);
Use case 1:
I would like to pass the above query to a utility class that will produce the same query just with with a different SELECT, for example:
ctx.select(DSL.count())
.from(MyEntity.MY_ENTITY)
.where(MyEntity.MY_ENTITY.ID.gt("P"))
.groupBy(MyEntity.MY_ENTITY.ZIP);
This particular example is to be able to create paginated results showing the total number of rows of the query.
Use case 2:
I would like to pass the above query to a utility class that will produce the same query just with with a modified WHERE clause, for example:
SelectHavingStep query =
ctx.select(MyEntity.MY_ENTITY.ZIP, DSL.count(MyEntity.MY_ENTITY.ZIP))
.from(MyEntity.MY_ENTITY)
.where(
MyEntity.MY_ENTITY.ID.gt("P")
.and(MyEntity.MY_ENTITY.ZIP.in("100", "200", "300"))
)
.groupBy(MyEntity.MY_ENTITY.ZIP);
This particular example is to further restrict a query based on some criteria (i.e. data visibility based on the user doing the query).
Is this possible?
Currently I'm using helper classes to do this at query construction time in the application code. I would like to move the responsibility to a library so it can be enforced transparently to the app.
Thanks.

You shouldn't try to alter jOOQ objects, instead you should try to create them dynamically in a functional way. There are different ways to achieve your use-cases, e.g.
Use case 1:
An approach to generic paginated querying can be seen here: https://blog.jooq.org/calculating-pagination-metadata-without-extra-roundtrips-in-sql/
Ideally, you would avoid the extra round trip for the COUNT(*) query and use a COUNT(*) OVER () window function. If that's not available in your SQL dialect, then you could do this, instead:
public ResultQuery<Record> mySelect(
boolean count,
Supplier<List<Field<?>>> select,
Function<? super SelectFromStep<Record>, ? extends ResultQuery<Record>> f
) {
return f.apply(count ? ctx.select(count()) : ctx.select(select.get()));
}
And then use it like this:
mySelect(false,
() -> List.of(MY_ENTITY.ZIP),
q -> q.from(MY_ENTITY)
.where(MY_ENTITY.ID.gt("P"))
.groupBy(MY_ENTITY.ZIP)
).fetch();
This is just one way to do it. There are many others, see the below link.
Use case 2:
Just take the above example one step further and extract the logic used to create the WHERE clause in yet another function, e.g.
public Condition myWhere(Function<? super Condition, ? extends Condition> f) {
return f.apply(MY_ENTITY.ID.gt("P"));
}
And now use it as follows:
mySelect(false,
() -> List.of(MY_ENTITY.ZIP),
q -> q.from(MY_ENTITY)
.where(myWhere(c -> c.and(MY_ENTITY.ZIP.in("100", "200", "300")))
.groupBy(MY_ENTITY.ZIP)
).fetch();
Again, there are many different ways to solve this, depending on what is the "common part", and what is the "user-defined part". You can also abstract over your MY_ENTITY table and pass around functions that produce the actual table.
More information
See also these resources:
https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
https://blog.jooq.org/a-functional-programming-approach-to-dynamic-sql-with-jooq/

Related

Spring Boot: keywords supported for JPA

I wanted to perform the Spring JPA repository where wanted to apply the and operation among 2 columns where one column cloud have multiple values in it.
SQL query for the same:
select * from table_name where col1='col1_val' and col2 IN
('col2_val_a','col2_val_b','col2_val_c');
I know that for and operation I can extend the JpaRepository and create the method with like this for:
List<MyPoJoObject> findByCol1AndCol2(String col1_val,String col2_val);
and for IN operation we can use : findByCol2In(Collection<String> col2_val)
But i did not know how i can club both the mentioned JPA default method into one, as per my sql statement mentioned before.
You can use the following method named:
List<MyPoJoObject> findByCol1AndCol2In(String col1_val, Collection<String> col2_val);
On this link repository-query-keywords you can find repository query keywords that you can use and combine them as well.
You can certainly combined both into one method.
List<MyPoJoObject> findByCol1AndCol2In(String col1_val,String[] col2_val);
Try this. I am not sure if it will accept Collection<String>. I will try that and update the answer.
HTH.
If you want to perform this logic for more than two columns then your method name becomes verbose.
Instead of stuck with Spring naming why can't you write your own JPA query.
Example:
#Query("select pojo from MyPoJoObject as pojo where pojo.col1 = :col1_val and pojo.col2 in :col2_val")
List<MyPoJoObject> findByColumns(String col1_val, List<String> col2_val);

How to obtain list of count() results using JpaRepository #Query?

I'm building REST API connected to ORACLE 11G DB. API sends data to Android client using JSON. To get data I'm using JpaRepository, and #Query annotations.
I want to provide data for charts: number of contracts in years.
I have native SQL query:
select aa.ROK, count(aa.NUMER_UMOWY)
from (select distinct NUMER_UMOWY, ROK from AGR_EFEKTY) aa
group by aa.ROK order by aa.ROK
Result of query using SQL Developer look like this:
I tried to get result using native query:
But result is always like this:
or error depending what I try.
Is it possible to obtain list of count() results using #Query?
If not, what should I use?
Thanks in advance :-)
I think What you are trying to use here is spring data projection.
As mentioned in the reference doc:
Spring Data query methods usually return one or multiple instances of
the aggregate root managed by the repository. However, it might
sometimes be desirable to create projections based on certain
attributes of those types. Spring Data allows modeling dedicated
return types, to more selectively retrieve partial views of the
managed aggregates.
and particularly closed projection where all accessor methods match the target attributes. In your case the count is not an attribute of your aggregate.
To perform what you want you can use constructor as follow :
class ContractsDto{
private String rok;
private int count;
public ContractsDto(String rok, int count) {
this.rok=rok;
this.count =count;
}
// getters
}
The query will be:
#Query(value = "select new ContractsDto(aa.rok , /*count */) from fromClause")
List<ContractsDto> getContractsPerYear();

How to disable hibernate formula like filter? Is this possible?

In Hibernate ORM we can disable filter using
session.disableFilter("filterName");
Is there any solution to disable formula in hibernate (with xml mapping configuration)?
I want to disable formula in some circumstances to improve performance .
If you are using JPA 2.1 you can use function call feature. for this purpose you should move formula query into database function and the call that function in select clause .for example assume that calculate is a database function that takes two numeric parameter.
then you can call this function by this way:
Object r = em.createQuery("SELECT function('calculate', a.id, 1) FROM Author a WHERE a.id = 1").getSingleResult();
and you have to register your custom function by this way:
public class MyPostgreSQL9Dialect extends PostgreSQL94Dialect {
public MyPostgreSQL9Dialect() {
super();
registerFunction("calculate", new StandardSQLFunction("calculate"));
}
}
for more information check this.

Let JDBI map the results for a custom query

I want to make a complicated query, and let JDBI handle the result mapping. Normally, I would do something like this:
interface MyDao {
#MapResultAsBean #SqlQuery("hardcoded query with :arg here")
ResultDto query(#Bind("arg") String arg);
}
ResultDto result = dbi.open(MyDao.class).query(arg);
Since the query is generated at runtime, I cannot do this, but I still want to use the result set mapping features. I've tried using the Handle interface:
String query = generateCustomQuery();
ResultDto result = dbi.open().createQuery(query).mapTo(ResultDto.class).first();
but I don't see a way to pass the arg. I could string-concat it into the generated query, but I'd rather pass it as if using PreparedStatement.
I believe you want to make use of bind.
dbi.open().createQuery(query).mapTo(ResultDto.class).bind(":arg", "value").first();

Using jooq to generate a InsertSelect

I wanna a function like this in Factory:
public final <R extends Record> InsertValuesStep<R> insertInto(Table<R> into, Collection<? extends Field<?>> fields, Select<?> select) {
return new org.jooq.impl.InsertSelectQueryImpl<R>(this, into, fields, select);
}
but i cann't access org.jooq.impl.InsertSelectQueryImpl, and i wanna to use InsertOnDuplicateStep to set onDuplicateKeyUpdate()
How to implemet it, Lukas?
i wanna to get sql like this:
insert ignore into tb1(field1,field2) select value1,value2 from tb2
You're right. As of jOOQ 3.0, there is currently an API design flaw that prevents you from combining INSERT .. SELECT with the ON DUPLICATE KEY UPDATE / IGNORE clauses in jOOQ. I have registered #2529 for this issue.
A similar flaw has already been registered as #2123, where ON DUPLICATE KEY UPDATE / IGNORE cannot be combined with a RETURNING clause.
There is currently no workaround for this issue, I'm afraid.

Categories

Resources