QueryDSL implementation of SQL's ISNULL function - java

Is there a way to write the SQL function ISNULL in a JPAQuery using QueryDSL when it's part of the selected data? For example, I have the native SQL query:
SELECT ISNULL(date1, date2) AS date_field FROM TABLE..
Currently, the only option I see is to return both date1 and date2 in my JPAQuery, and then do my own check in Java code to know which one to access. But it would be nice to have to return multiple fields when I don't need to.
I think it would look like this:
return new JPAQuery<ResponseObject>(entityManager)
.select(Projections.bean(ResponseObject.class,
table.date1,
table.date2))
.from(table)
.fetch();
Sorry if I've confused some areas of JPA with QueryDSL or vice versa.

You can simply use coalesce that is supported by QueryDSL and will work same as ISNULL
For example:
new Coalesce<>(Date.class)
.add(qEntity.firstOptional)
.add(qEntity.secondOptional)

Related

Can't solve the JPQL :parameter problem by using FUNC()

Ok, just to cut it short, I've done the actual JPQL without using any parameter first and it looks like this.
SELECT count(dt)
FROM transaction dt
WHERE dt.transactionType = 'TEST'
AND dt.date
BETWEEN FUNC('TO_DATE','01-2019','mm-yyyy')
AND FUNC('TO_DATE','02-2019','mm-yyyy')
This thing work! But the thing is now I need to make the transactionType and date as a parameter and this is how it looks like
SELECT count(dt)
FROM transaction dt
WHERE dt.transactionType = :transType
AND dt.date
BETWEEN FUNC('TO_DATE',:lastMonth,'mm-yyyy')
AND FUNC('TO_DATE',:nextMonth,'mm-yyyy')
So for :transType it's fine, but inside this FUNC() seems like I shouldnt put the parameter just like that and need some workaround. I've been googling and can't find any result.
The error was like this
You have attempted to set a parameter value using a name of
lastMonth,'mm-yyyy') that does not exist in the query string
As you can see, the parameter inside FUNC() take along the parameter behind it that meant for FUNC(). What did I miss? Enlighten me please.
Make sure you're using setString for the parameter type.
I always had difficulty with named parameters within JPA, depending upon how the query was created - try using ordinal parameters, eg: ?1 and set them by index.
I'd avoid FUNC as it can carry some major overhead if you're not extremely careful.
There's a workaround for this problem.
Initially, the simplified SQL as below:
SELECT count(*)
FROM table tb
WHERE tb.date between to_date('01-2020','mm-yyyy') and to_date('02-2020','mm-yyyy');
And by directly convert the simplified SQL to JPQL, it turns out as such:
SELECT count(tb)
FROM table tb
WHERE tb.date BETWEEN FUNC('TO_DATE','01-2020','mm-yyyy') AND FUNC('TO_DATE','02-2020','mm-yyyy')
But, the JPQL need to be dynamic as the date will not be static, so by using JPQL parameter to ensure this JPQL can be used at any date, instinctively I thought to use as such:
SELECT count(tb)
FROM table tb
WHERE tb.date BETWEEN FUNC('TO_DATE',:fromDate,'mm-yyyy') AND FUNC('TO_DATE',:toDate,'mm-yyyy')
But as my initial question when this thread first started, such JPQL will not work. So how did I found a workaround? Relatively quite simple actually.
Instead of using this to get the ranging date (as from sql wise I use to_date)
WHERE tb.date BETWEEN FUNC('TO_DATE','','') AND FUNC('TO_DATE','','')
I used this
WHERE FUNC('TO_CHAR','','') between (--fromDate) and (--toDate)
Which finally resulted in final working JPQL of
SELECT count(tb)
FROM table tb
WHERE FUNC('TO_CHAR',tb.date,'mm-yyyy') BETWEEN (:fromDate) AND (:toDate)

Parse sql query using antlr parsetree to mongo bson document in Java

I have a SQL like query example:
Select id,name from employee where age > 30 and department = 'IT' limit 200
The SQL query grammer is defined in an ANTLR4 grammar file. Is there any implementation that converts the parse tree of this query to a bson document?
The bson document will then be used to query a mongo db.
In one of my previous jobs I did something similar: got a query (not an sql, but pretty similar) and translated it to mongo query with antlr.
I don't have a code to share, However I can share my thoughts:
Mongo is not SQL compliant, so you can't just take a sql grammar. What about JOINs and all the relational algebra? What about aggregations that are pretty tricky in mongo with their aggregation framework? In the opposite direction, how do you generate SQL that gets translated to "exists" clause in mongo. There are many things like this, some are small, some are huge, but bottom line you must be talking about some kind of subset of sql ,some DSL that is allowed to be used as a query language and looks "like" an sql because people are used to SQL.
With that in mind, you should create your own grammar and Antlr will generate a lexer/parser for you. You'll also get for granted a syntax check of the query in Runtime. Antlr won't be able to parse the query if its not in a correct format obviously, some grammar rule will fail. This is an another reason to not take SQL "as is".
So far so good, you've created your own listener / visitor. In my case I've opted for creating an object representation of the query with internal state and everything.
So the query
Select id,name
from employee
where age > 30
and department = 'IT'
limit 200
Was translated to objects of type:
class Query {
private SelectClause select;
private FromClause from;
private WhereClause where;
private Limit limit;
}
class SelectClause {
private List<String> fields;
}
...
class WhereClause {
Condition root;
}
interface Condition {
...
}
class AndCondition implements Condition { // the same for Not, Or
}
For this particular query its something like:
Query q = new Query(new SelectClause(["id", "name"]), new FromClause("employee"), new WhereClause(new AndCondition(new SimpleLeafCondition("age", Operators.GT, 30), new SimpleLeafCondition("department", Operators.EQ, "IT" )), new Limit(30));
Then Its possible to make some optimizations in the query (like embedding of where clauses if you need, or, for example, manipulating the "For" part if you're working multi tenant environment and have different collections for different tenants).
After all you can go with design pattern "interpreter" and recursively parse the query objects and "translate" them to valid mongo query.
I remember that this step took me something like 1 day to accomplish (it was 7 years ago with mongo 2 I guess, but still), given the correct structure of objects representing the query, so this should not be that complicated. I'm bringing this up, because It looks like its your primary concern in the question.

Runtime SQL Query Builder

My question is similar to
Is there any good dynamic SQL builder library in Java?
However one important point taken from above thread:
Querydsl and jOOQ seem to be the most popular and mature choices however there's one thing to be aware of: Both rely on the concept of code generation, where meta classes are generated for database tables and fields. This facilitates a nice, clean DSL but it faces a problem when trying to create queries for databases that are only known at runtime.
Is there any way to create the queries at runtime besides just using plain JDBC + String concatenation?
What I'm looking for is a web application that can be used to build forms to query existing databases. Now if something like that already exists links to such a product would be welcome too.
While source code generation for database meta data certainly adds much value to using jOOQ, it is not a prerequisite. Many jOOQ users use jOOQ for the same use-case that you envision. This is also reflected in the jOOQ tutorials, which list using jOOQ without code generation as a perfectly valid use-case. For example:
String sql = create.select(
fieldByName("BOOK","TITLE"),
fieldByName("AUTHOR","FIRST_NAME"),
fieldByName("AUTHOR","LAST_NAME"))
.from(tableByName("BOOK"))
.join(tableByName("AUTHOR"))
.on(fieldByName("BOOK", "AUTHOR_ID").eq(
fieldByName("AUTHOR", "ID")))
.where(fieldByName("BOOK", "PUBLISHED_IN").eq(1948))
.getSQL();
In a similar fashion, bind values can be extracted from any Query using Query.getBindValues().
This approach will still beat plain JDBC + String concatenation for dynamic SQL statements, as you do not need to worry about:
Syntax correctness
Cross-database compatibility
SQL Injection
Bind variable indexing
(Disclaimer: I work for the vendor of jOOQ)
SQLBuilder http://openhms.sourceforge.net/sqlbuilder/ is very useful for me.
Some simple examples:
String query1 = new InsertQuery("table1")
.addCustomColumn("s01", "12")
.addCustomColumn("stolbez", 19)
.addCustomColumn("FIRSTNAME", "Alexander")
.addCustomColumn("LASTNAME", "Ivanov")
.toString();
String query2 = new UpdateQuery("table2")
.addCustomSetClause("id", 1)
.addCustomSetClause("FIRSTNAME", "Alexander")
.addCustomSetClause("LASTNAME", "Ivanov")
.toString();
Results:
INSERT INTO table1 (s01,stolbez,FIRSTNAME,LASTNAME) VALUES ('12',19,'Alexander','Ivanov')
UPDATE table2 SET id = 1,FIRSTNAME = 'Alexander',LASTNAME = 'Ivanov'
I have a custom solution for dynamically generating such SQL queries with just 2-3 classes for similar requirement. It is a simple approch.
This can be referred at Creating Dynamic SQL queries in Java
For simpler use cases like a dynamic filter condition based on the inputs selected from UI, one can use the below simpler approach by directly modifying the query in below style:
select t1.id, t1.col1, t1.col2,
from table1 t1
where (:col1Value is null or t1.col1 = :col1Value)
and (:col2Value is null or t1.col2 = :col2Value);
Here values for col1 or col2 can be null but the query will work fine.

Parsing and modification of SQL statements in Java

Does anyone know about a SQL statements parser, in Java, that would allow having an Object representation of a SQL query, allow modifying this representation and generate back the updated SQL statement?
Regards,
Christophe
I would think that ANTLR would be able to do this.
Maybe you could look at JSqlParser.
This is a demo use Java SQL Parser do something like this:
Input SQL:
SELECT A as A_Alias, B AS B_Alias FROM TABLE_X
If you need to remove the second column “B AS B_Alias” from the select list, just do something like this:
columns.removeResultColumn(1); // 0 is the first column
then you will get this new SQL(the , was removed automatically):
SELECT A as A_Alias FROM TABLE_X
this demo also illustrates how to replace a column, Add criteria (where clause), Add Order by clause and etc.

Compare time part of datetime field in Hibernate

I've got an application that uses a hibernate(annotations)/mysql combination for ORM. In that application, I got an entity with a Date field. I'm looking for a way to select on that date within a time range (so hh:mm:ss without the date part).
In MySQL there's a function TIME(expression) that can extract the time part and use that in the where clause, but that does not seem to be available in Hibernate without switching to native queries. Is there an option in hibernate to do this, or should I loop through the results in java and do the comparison there? Would this be much slower as the MySQL solution, since that would not use indexes anyway?
The following functions are available in HQL, maybe you could use them:
second(...), minute(...), hour(...), day(...), month(...), year(...)
Add the expression as a SQL restriction rather than having a full native query. I don't know MySQL specifically, but imagine something like this:
Criteria criteria = session.createCriteria(MyTable.class);
criteria.add(
Expression.sql(
"TIME( {alias}.my_date, 'hh:mm:ss') >= :1",
dateRangeMin,
new StringType()
)
);

Categories