How to use 'LIKE' function to select array of Strings with JOOQ - java

I now want to use 'like' function with JOOQ to select data including array of string data by not case sensitive and partitial-match.
Table schema is:
CREATE TABLE favorites (
id int,
items varchar(100)[]
);
Sample data is:
INSERT INTO favorites (id, items)
VALUES (1, '{orange, lemon, banana}');
INSERT INTO favorites (id, items)
VALUES (2, '{apple, grape}');
To get first data, SQL is like:
SELECT id, items FROM favorites WHERE 'orange' = ANY (items);
My Goal is to select data by case-sensitive and partitial-match like: For example, using likeIgnoreCase("OraNge") or like("%ang%") ?
To develop below code with LIKE function:
Connection connection = ...;
DSLContext context = DSL.using(connection, ...);
List<Table> table = context.select().from(TABLE).fetchInto(Table.class);
How can I use like function?
Thank you in Advance.

The PostgreSQL value = ANY (array) operator cannot match values like the LIKE predicate. You will need to resort to an actual LIKE predicate instead. In SQL, you'd write:
SELECT id, items
FROM favorites
WHERE EXISTS (SELECT * FROM unnest(items) AS t(item) WHERE item ILIKE '%OraNge%')
Or, with jOOQ:
context.select(FAVORITES.ID, FAVORITES.ITEMS)
.from(FAVORITES)
.whereExists(
selectFrom(unnest(FAVORITES.ITEMS).as("t", "item")
.where(field(name("item", String.class)).likeIgnoreCase("%OraNge"))
)
.fetch();
The jOOQ version, as always, assumes you have this static import:
import static org.jooq.impl.DSL.*;

In addition, here are a few ways to use LIKE. You can always use the jOOQ LIKE predicates, see their documentation. In my second example, I use sql syntax in a string, just to prove you can. You can also use contains/startsWith/endsWith like you would with strings.
jooq.dsl()
.select()
.from(MY_TABLE)
.where(Employee.EMPLOYEES.LAST_NAME.like("ER")));
jooq.dsl()
.select()
.from(EMPLOYEES)
.where(Employee.EMPLOYEES.LAST_NAME.like("ER"))
.and("first_name like ?", "ST"));

Related

Insert if not exist and update certain values if it does

I'm using JDBI3 (and would like to use #SQLUpdate) and an Oracle DB.
I want to insert an item with 4 columns into the table if it does not exist, if it does exist I want to instead update 3 of the 4 values of the item. If it wasn't Oracle I would've used some ON DUPLICATE_KEY logic but that does not exist in Oracle. I read some things about using Merge but the Queries seemed really wonky for what I was trying to do. Any tips on what to look for?
Additional question: If it is Merge I should use (with some form of sub queries I assume), how does the query affect performance? I think this database is quite write heavy.
MERGE INTO device db USING (SELECT 'abc' AS col1, 'bcd' as col2, 'cde' as col3, 'def' as col4 FROM DUAL) input
on (db.col1 = input.col1 AND db.col2= input.col2)
WHEN MATCHED THEN UPDATE
SET db.col4 = input.col4
WHEN NOT MATCHED THEN INSERT
(db.col1, db.col2, db.col3, db.col4)
VALUES (input.col1, input.col2, input.col3, input.col4)
Merge it is. Performs well.
Dummy example based on your description:
merge into target_table a
using source_table b
on (a.id = b.id)
when matched then update set
a.name = b.name,
a.job = b.job,
a.sal = b.sal
when not matched then
insert (id, name, job, sal)
values (b.id, b.name, b.job, b.sal);

How to generate SQL from template with order by parameter using jOOQ?

I generate the SQL template like this with jOOQ 3.11.11.
DSLContext context = new DefaultDSLContext(conf);
Query query = context.select()
.from("table1")
.where(DSL.field("report_date").eq(DSL.param("bizdate")))
.orderBy(DSL.param("sort"));
String sqlTemp = context.renderNamedParams(query);
SQL template:
select *
from table1
where report_date = :bizdate
order by :sort
The SQL template is stored and the params are decided at realtime query condition.
ResultQuery resultQuery = context.resultQuery(sqlTemp, DSL.param("bizdate", "20190801"), DSL.param("sort", "id desc"));
The realtime SQL:
select *
from table1
where report_date = '20190801'
order by 'id desc'
There is something wrong with the order by clause.
So. How to replace the order by param sort with "id desc" or "name asc" and eliminate the quotes?
DSL.param() creates a bind variable, which is generated as ? in SQL, or :bizdate if you choose to use named parameters, or '20190801' if you choose to inline the bind variables. More about bind variables can be seen here.
You cannot use DSL.param() to generate column references or keywords. A column expression (e.g. a reference) is described in the jOOQ expression tree by the Field type. Keywords are described by the Keyword type, but you probably do not want to go this low level. Instead you want to handle some of the logic in your query expression. For example:
String sortField = "id";
SortOrder sortOrder = SortOrder.ASC;
Query query = context.select()
.from("table1")
.where(DSL.field("report_date").eq(DSL.param("bizdate")))
.orderBy(DSL.field(sortField).sort(sortOrder));
The mistake you're making is to think that you can use a single SQL template for all sorts of different dynamic SQL queries, but what if you're dynamically adding another predicate? Or another join? Or another column? You'd have to build a different jOOQ expression tree anyway. Just like here. You could store two SQL strings (one for each sort order), and repeat that for each sort column.
But, instead of pre-generating a single SQL string, I recommend you extract a function that takes the input parameters and generates the query every time afresh, e.g.:
ResultQuery<?> query(String bizDate, Field<?> sortField, SortOrder sortOrder) {
return context.selectFrom("table1")
.where(field("report_date").eq(bizDate))
.orderBy(sortField.sort(sortOrder));
}
Here is some further reading about using jOOQ for dynamic SQL:
https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql
https://blog.jooq.org/2017/01/16/a-functional-programming-approach-to-dynamic-sql-with-jooq

Spring namedParameterJdbcTemplate and a list parameter: how to check if it is null in SQL?

I'm using spring's NamedParameterJdbcTemplate because I have a SELECT ... IN () in my SQL query, as explained here.
In our specific case, the business logic should be:
- If the list of id's to check is null or empty, omit the entire IN condition
- If the list contains id's, use the IN condition like normal
So we programmed it like this:
SELECT * FROM table WHERE (:ids IS NULL or table.column IN (:ids))
This query works if the :ids is indeed a NULL or empty list, but it fails if it is not because the way spring fills in the parameters for a list of 3 values is like this:
SELECT * FROM table WHERE ((?,?,?) IS NULL or table.column IN (?,?,?))
and you cannot do "IS NULL" on the triple question mark statement. Is there any easy way to do solve this directly in the SQL query, thus not using Java code (we don't want to do string maniuptlation in the sql query in Java)?
You could try reversing the order like this:
SELECT * FROM table WHERE (table.column IN (:ids) or :ids IS NULL)
Since your 3 id case will satisfy the first condition, the 'or' may not be evaluated. This might depend on your DB though. This works with Hibernate + Oracle, but I don't see it working with Sybase IQ + NamedParameterJdbcTemplate so your mileage may vary.
If your DB supports Common Table Expressions (CTE's), you can try this:
with
x as (
select column
from table
where column in (:ids)
)
select *
from table
where (table.column in (:ids) or (select count(*) from x) = 0)

coverting sql count aggregate function to hibernate criteria

I have the following sql query which i am trying to covert to a hibernate find:
select column_1, column_2, count(*)
from data_table
group by column_1, column_2
order by column_1, column_2
;
So far this is the code i have:
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(table.class);
detachedCriteria.setProjection(Projections.projectionList()
.add(Projections.groupProperty("column_1"))
.add(Projections.groupProperty("column_2"))
.add(Projections.rowCount()))
.addOrder(Order.asc("column_1"))
.addOrder(Order.asc("column_2"));
For some reason i am getting the following error:
ORA-00979: not a GROUP BY expression
Is this the right way to translate the the sql query using hibernate? if not, what would be a better way
I am also trying to map the returned count column to a transient property in the model object. What would be a good way to implement that?
Thanks
I was able to implement what i was looking for using the code below:
detachedCriteria.setProjection(Projections.projectionList()
.add(Projections.groupProperty("column_1"))
.add(Projections.groupProperty("column_2"))
.add(Projections.sqlProjection(
"count(*) as counter",
new String[] { "counterproperty" },
new Type[] { Hibernate.LONG }
)))
.addOrder(Order.asc("column_1"))
.addOrder(Order.asc("column_2"));
detachedCriteria.setResultTransformer(Transformers.aliasToBean(modelObject.class));
counterProperty is a transient property i added to the model object

grouping data in java

is there someway we can group similar data in java?
i want to group all the data with same id and print it out.
i am querying for the data using jdbc and was searching for a library i could use for this.
any idea?
thanks
Use a Map<GroupID, List<Data>>.
Map<Long, List<Data>> groups = new HashMap<Long, List<Data>>();
while (resultSet.next()) {
Long groupId = resultSet.getLong("groupId");
String col1 = resultSet.getString("col1");
String col2 = resultSet.getString("col2");
// ...
List<Data> group = groups.get(groupId);
if (group == null) {
group = new ArrayList<Data>();
groups.put(groupId, group);
}
group.add(new Data(groupId, col1, col2 /* ... */));
}
You could also just make it a property of another (parent) bean.
See also:
Collections and Maps tutorial
Ideally you should use a where clause in your SQL query to limit the returned data to the id in question:
select *
from table
where id = 'xxxxxx'
Of course if you will be printing out the data for all id's this may be a bad choice, as then your app will perform multiple sql queries, which usually will result in a performance hit.
As for grouping data in Java, take a look at java.util.HashMap (or any of the container classes that implement the Map interface). HashMap is a container of key-value pairs. In your case, the 'key' can be a String (or whichever data type applies) representing your id, and the 'value' can be an object to contain the data associated to the id key (i.e.: ArrayList of Strings, or a new class you define to help you manage the data)
Are you looking for the SQL ORDER BY clause?
SELECT columns
WHERE criteria
ORDER BY id ASC;
That will give you all the data in your criteria and will order it by the id column which naturally means that all the rows with the same id will appear consecutively.

Categories