Let JDBI map the results for a custom query - java

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();

Related

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

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/

How to remove a criteria in Spring Mongo Query

I have a Mongo query org.springframework.data.mongodb.core.query.Query with one criteria and I want to update this criteria by adding the same criteria with different value:
query.addCriteria(Criteria.where("clientName").is("client2"));
But this throws:
Method threw
'org.springframework.data.mongodb.InvalidMongoDbApiUsageException'
exception. Due to limitations of the com.mongodb.BasicDocument, you
can't add a second 'clientName' criteria. Query already contains
'{"clientName": "client1"}'
I've tried to remove the criteria using:
query.getQueryObject().remove("clientName")
But it seems like it returns a copy of the criteria and it didn't remove the criteria. I'm still getting the same exception.
How can I properly remove a criteria in Mongo query?
You can do as follow:
#Autowired
private MongoTemplate mongoTemplate;
public Query queryName(Type1 value1, Type2 value2) {
Query query = new Query(new Criteria().orOperator(Criteria.where("field1").is(value1), Criteria.where("field2").is(value2)));
}
Please note that you can change"orOperator" with "andOperator" based on what you need, otherwise you can use a single criteria.
I think that, instead of deleting a criteria, you should create a new query using for example the function above.

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 get exact cql from statement using java api from datastax

My code directly executes the bound statement prepared without any exact query. Then how to get the cql it is trying to perform in cassandra database?
For example:
public <T> void save(T entity) {
if (entity != null) {
Statement statement = getEntityMapper(entity).saveQuery(entity);
statement.setConsistencyLevel(consistencyLevelWrite);
mappingManager.getSession().execute(statement);
}
}
I am trying to get something like INSERT INTO "keyspace"."tableName"("column1","column2") VALUES (value1,value2)
My most generic answer is to enable the query logger. It will show executed queries in your application logs.
If you need something more specific and want to manipulate the query string in your own code, you can take inspiration from the implementation: QueryLogger.java. In this particular case, you can get the "generic" query string (with placeholders) by casting to BoundStatement and then invoking .preparedStatement().getQueryString() on it; then inspect the bound statement for the values of the placeholders. As you'll see in the code, QueryLogger handles a lot of corner cases (e.g. truncating large parameters).

How can mapped result from SQL native query to java POJO class (no entity)

I have simple java pojo and it's no entity.
class MyClass {
// fields, getter, setter and etc...
}
Also I have DAO with some function for execute native SQL query (createNativeQuery)
How can mapped result from SQL native query to MyClass without #Entity ?
If the bean field names are the same as the DB table's column names, you can use Spring JDBC's org.springframework.jdbc.core.BeanPropertyRowMapper<T>.
You call org.springframework.jdbc.core.simple.SimpleJdbcOperations.queryForObject(String, RowMapper<T>, Object...)) with the BeanPropertyRowMapper object and it calls all the setters for you, using reflection.
If its JPA, I'd used:
Query query = getEntityManager().createNativeQuery(sql.toString(), MyClass.class);
It works if MyClass is an EntityBean :-(
You can simply issue a query and call the getters/setters in your POJO class.
Pseudo-code:
get connection
ResultSet rs = execute query
if (rs.next()) {
setField1(rs.getString("field1"));
etc....
}
You can use EclipseLink query redirector. The following link explains it. The author has also provided code which is pretty generic and works quite well.
http://onpersistence.blogspot.in/2010/07/eclipselink-jpa-native-constructor.html

Categories

Resources