Set NULL value in jooq - java

I have an big query and my problem is setting NULL values using jooq.
For example, I have this piece of sql query:
IF(t.PANEL_ID IS NULL, t.ID, NULL) AS testId
If transform this into jooq implementation something like this will come out:
when(TEST.PANEL_ID.isNull(), TEST.ID).otherwise(null)).as("testId")
but this is ambiguous method call.
I made some research, and find this snippet:
DSL.val((String) null)
but it didn't work, because it cannot resolve method with jooq.Param<String>.
How should I proceed?

Your NULL expression must be of the same type as your TEST.ID column. I would imagine this is not a String column, but some numeric one. Irrespective of the actual data type, you can always create a bind value using the data type of another expression, e.g.
// Bind variable:
DSL.val(null, TEST.ID)
// Inline value / NULL literal
DSL.inline(null, TEST.ID)
If you're doing this a lot, you could also extract your own utility like this:
public static <T> util(Field<?> nullable, Field<T> other) {
return when(nullable.isNull(), other).otherwise(inline(null, other));
}
Notice, jOOQ has a built-in method NVL2 for this purpose:
nvl2(TEST.PANEL_ID, inline(null, TEST.ID), TEST.ID).as("testId")

Related

Case When or iif syntax expression in jooq

I am trying to reproduce this MySQL query in jooq.
messageTable1, messageTable2 same column
select
msg.index as index,
left(msg.message, if(msg.type = 1, 10, msg.maxLength)) as message
from
messageTable1 as msg
select
msg.index as index,
left(msg.message, if(msg.type = 1, 10, msg.maxLength)) as message
from
messageTable2 as msg
making function java
public Message getMessageTest(String tableName) {
return roDslContext.select(
DSL.field("msg.index").as("index"),
left(DSL.field( "{0}", String.class, "msg.message"),
when(DSL.field("msg.type").eq(1), 10, DSL.field( "{0}", String.class, "msg.maxLength").as("message)
.from(tableName)
.fetchOneInto(Message.class);
}
but....
when(DSL.field("msg.type").eq(1), 10, DSL.field( "{0}", String.class, "msg.maxLength").as("message)
jooq is field msg.maxLength ... not accept
also so iif
how case, when, iif insert field column data??
Using the code generator
I'm assuming you're not using the code generator in this example, because you want to make your column references dynamic, depending on the table name.
There's a possibility of generating common interfaces onto your tables, which would abstract over the two table types. Also, there are Table.rename(String) methods generated on those tables, which allow for renaming a table MESSAGE_TABLE_1.rename("message_table_2"). So your query would look like this:
public Message getMessageTest(String tableName) {
MessageTable1 msg = MESSAGE_TABLE_1.rename(tableName);
return roDslContext
.select(
msg.INDEX,
left(msg.MESSAGE, if_(msg.TYPE.eq(1), val(10), msg.MAX_LENGTH))
)
.from(msg)
.fetchOneInto(Message.class);
}
As always, this is assuming the following static import:
import static org.jooq.impl.DSL.*;
The actual problem
You seem to be attempting to construct field references using plain SQL templating. You did it right for msg.index via DSL.field("msg.index"), but somehow tripped over the templating syntax for msg.message. This doesn't make sense:
field("{0}", String.class, "msg.message");
When using a template, the placeholder {0} needs to be replaced by a QueryPart instance. You can't just pass a String instead (which would be interpreted as a bind value). What you meant to do here is any of these:
// If you really need templating, though here, you don't:
field("{0}", String.class, name("msg", "message"));
// Instead, just do this, if you want quoted names:
field(name("msg", "message"), String.class);
// Or this, if you don't care about quoting the name
field("msg.message", String.class);

postgres jdbc insert array of custom type

I want to insert array of custom type into postgres with jdbc.
my sql schema:
CREATE TYPE element_pk_t AS (
workspace_id BIGINT,
element_id BIGINT,
history_id BIGINT
);
my java class:
public class ElementPK {
public Long workspaceId;
public Long elementId;
public Long historyId;
}
How should I do this in java with jdbc?
I've found tutorial regarding custom type https://docs.oracle.com/javase/tutorial/jdbc/basics/sqlcustommapping.html, but array of custom type is still unclear to me.
One trivial approach is using pure string style SQL statement, but I still prefer using PreparedStatement with setObject or setArray
If I understood you correctly, then, from the Java code, you need to generate query that looks, for example, like this:
INSERT INTO parent_table(elements)
VALUES (
ARRAY[
row(1, 2, 3)::element_pk,
row(4, 5, 6)::element_pk,
row(7, 8, 9)::element_pk
]);
Assuming that you have a table like the following:
CREATE TABLE parent_table(
id SERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
elements element_pk[]
)
And the Type like you described. If I were you, I wont implement it via an array. I would have use JSONB instead - in this way you wont lose an ability to use indexing. Of course, there is java.sql.Array out there, and you can still do it via the following:
Array array = connection.createArrayOf("public.element_pk", new ElementPK[]{
new ElementPK(1, 2, 3),
new ElementPK(9, 4, 6)
});
And then set the array, to PgPreparedStatement, but the thing is that internally PgPreparedStatement will enclose your elements within curly braces, which is ok, but each element will be represented by its toString method call result. I mean, assume your ElementPK toString looks like this:
#Override
public String toString() {
return "This is how it is implemented, really?";
}
then you will get the SQL like:
INSERT INTO parent_table (elements) VALUES ('{"This is how it is implemented, really?","This is how it is implemented, really?"}')
Again, it is maybe possible to adopt it, but from my perspective - at least having your logic within toString method is not that great, is it? Spare yourself and do the following:
CREATE TABLE via_jsonb(
elements JSONB
);
and then simply:
INSERT INTO via_jsonb VALUES(
'{
"workspace_id" : 1,
"element_id" : 2,
"history_id" : 3
}'
);
and in the Java code I would have simply create json from your object and set it as a string. Really, there are a lot of functions and cool features on JSONB out of the box.
Hope it helped, have a nice day!)
Follow up to the last part of #misha2045 answer.
If you still refuese to use JSONB for whatever reason you need to add parenthesis at the end and start of your toString method.
#Override
public String toString() {
return "("+this.field1+ ", " + this.field2+")";
}
Then set the array in the prepared statement
ps.setArray(1, con.createArrayOf("yourType", yourClassArray));

Default Values in Oracle Functions

Assume the following function declaration:
FUNCTION ARTTEXTJN
(p_art_id in number
,p_arttextart in varchar2 default 'basis'
,p_sprache in varchar2 default null
,p_aufart in number default null
,p_fallback_arttextart in varchar2 default 'J'
)
RETURN VARCHAR2
Expect the first parameter all parameter have a default value.
jOOQ generate a package method like this:
public static Field<String> arttextjn(Field<? extends Number> pArtId, Field<String> pArttextart,
Field<String> pSprache, Field<? extends Number> pAufart, Field<String> pFallbackArttextart) {
Arttextjn f = new Arttextjn();
f.setPArtId(pArtId);
f.setPArttextart(pArttextart);
f.setPSprache(pSprache);
f.setPAufart(pAufart);
f.setPFallbackArttextart(pFallbackArttextart);
return f.asField();
}
If I want to use it in a query I have to pass null to the function:
dsl.select(KAMPARTIKEL.ARTNR, KAMPARTIKEL.ARTNRKAMP,
PaBez.arttextjn(KAMPARTIKEL.ART_ID, null, null, null, null))
But then Oracle does not use the default values.
Is there a way to tell jOOQ to generate overloaded methods with all possible combinations? Otherwise I'm not able to use that function in a select clause.
Is there a way to tell jOOQ to generate overloaded methods with all possible combinations?
No, there would be way too many combinations. Of course, you could extend the code generator yourself, but I would advise against it.
Otherwise I'm not able to use that function in a select clause.
Yes, you can use it! But not using that auxiliary method PaBez.arttextjn. It's possible to call it as a standalone function call:
Arttextjn f = new Arttextjn();
f.setPArtId(1);
f.execute();
String result = f.getReturnValue();
It should be possible to use in a SQL statement as well:
Arttextjn f = new Arttextjn();
f.setPArtId(KAMPARTIKEL.ART_ID);
var result =
dsl.select(KAMPARTIKEL.ARTNR, KAMPARTIKEL.ARTNRKAMP, f.asField())
.from(KAMPARTIKEL)
.fetch();
In your case, this should work out of the box.
Note that as of jOOQ 3.11, and in Oracle, jOOQ passes function arguments by index in this case, not by name (as it does for PostgreSQL). The generated SQL is:
select KAMPARTIKEL.ARTNR, KAMPARTIKEL.ARTNRKAMP, pa_bez.arttextjn(KAMPARTIKEL.ART_ID)
from KAMPARTIKEL
Which works because you're using only the first parameter, applying defaults to the rest. It wouldn't work if you would pass the last parameter, in case of which the generated SQL would have to use named parameters:
select
KAMPARTIKEL.ARTNR,
KAMPARTIKEL.ARTNRKAMP,
pa_bez.arttextjn(p_art_id => KAMPARTIKEL.ART_ID)
from KAMPARTIKEL
I've created an issue to fix this for jOOQ 3.12:
https://github.com/jOOQ/jOOQ/issues/8560

hibernate, check if object exists and null values

I am using hibernate to save records (i.e. objects) to a database. Before saving my objects, I want to verify if the database already contains this object. (The primary key is just an incremental key and cannot be used for this.)
I am creating a HQL statement at runtime to check the existance of a record with these attributes (i.e. column1-3).
The resulting query should look like:
from myTable where column1 is null and column2 = :column2 and column3 = :column3'
Because sometimes the columns can contain null values, I check the value of the attributes, if it is a NULL value, then I use a is instead of a = in this query (e.g. the column1 is :column1 in the above statement).
Because I start to realize that I am doing a lot of work to achieve something reletively crucial, I am starting to wonder if I'm on the right track. Is there an easier way to check the existance of objects ?
EDIT: I slightly rephrased my question after I realized that also column1 is :column1 does not work when :column1 parameter is set to null. Apparently the only syntax that seems to work as expected is column1 is null. So, it seems like you just cannot use wildcards when searching for null values. But that does not change the main aspect of my question: should I really be checking all this stuff at runtime ?
This is the best way that I found so far.
I prefer to put my filters in a map. The key refers to the property (i.e. map.put("column1", Integer.valueOf(1))).
There is a Restritions.eqOrIsNull method that simplifies the conversion to a Criterion object. The following method converts an entire Map to a List<Criterion>.
public List<Criterion> mapToCriterion(Map<String, Object> params)
{
if (params == null) return null;
// convert the parameter map to a list of criterion
List<Criterion> criterionList = new ArrayList<>(params.size());
for (Map.Entry<String, Object> entry : params.entrySet())
criterionList.add(Restrictions.eqOrIsNull(entry.getKey(), entry.getValue()));
return criterionList;
}
Later on, I use the List<Criterion> to build a Criteria object.
Criteria criteria = session.createCriteria(clazz);
if (criterionList != null)
{
for(Criterion criterion : criterionList)
criteria.add(criterion);
}
// get the full list
#SuppressWarnings("unchecked")
List<T> objectList = criteria.list();
My general impression is still that there are missing several convenience methods here (e.g. Criteria#addAll(List<Criterion>) would have been nice).

Use NamedParameterJdbcTemplate to update array field

I have a double precision array field dblArrayFld in a table myTable and I'd like to update it using Spring's NamedParameterJdbcTemplate (I'm using Postgres).
I'm running code like this:
SqlParameterSource params = (new MapSqlParameterSource())
.addValue("myarray", myDblArrayListVar)
.addValue("myid", 123);
namedJdbcTemplate.update("UPDATE myTable SET dblArrayFld = :myarray WHERE idFld = :myid", params);
This returns an error that reads syntax error at or near "$2"
I'm assuming my syntax on :myarray is at fault here. I've also tried encasing :myarray in the following ways:
dblArrayFld={:myarray}
dblArrayFld={ :myarray }
dblArrayFld=[:myarray]
dblArrayFld=ARRAY[:myarray]
dblArrayFld=(:myarray)
What's the correct syntax here?
Wehn you try to bind Collection or array as named parameter, NamedParameterJdbcTemplate explodes the appropriate named parameter in your statement into a number of positional parameters matching the length of your array / collection. This is useful for WHERE column IN (:param) statements, but is not going to work in this case.
In order to set an actual Postgres array you have to supply your parameter as java.sql.Array. You can create its instance using Connection#createArrayOf() method.

Categories

Resources