How to insert null value to foreign key with JDBCTemplate - java

I've the following schema :
CREATE TABLE API
(
ID BIGINT NOT NULL,
NAME VARCHAR NOT NULL,
API_PARENT_ID BIGINT,
CONSTRAINT API_PKEY PRIMARY KEY (ID)
CONSTRAINT API_PARENT_ID_FKEY FOREIGN KEY (API_PARENT_ID) references API,
);
My API entity have a FK on itself in case of api dependencies (like child-parent relation)
I've handle myself (meaning I've avoided to use hibernate) the CRUD part with a jdbc template.
I've encountering a problem when my API is the parent and then the api_parent_id value should be null.
The following line (i've skipped the meaningless columns):
String _SQL_UPDATE_API = "update api set api_parent_id = ? where id = ?"
this.jdbcTemplate.update(_SQL_UPDATE_API, api.getParent !=null ? api.getParent().getId() : null , api.getId());
return the following error :
[90012-196]; nested exception is org.h2.jdbc.JdbcSQLException: Parameter "#1" is not set; SQL statement: update api set api_parent_id = ? where id = ?
How should I handle the null value ?
I've tried with MapSqlParameterSource or with a PreparedStatement in specifying the
ps.setNull(1,Types.NULL);

ps.setNull(1,Types.NULL);
Unfortunately, you'll need to set the specific JDBC type of the column that corresponds to the type of the column, according to your JDBC driver.
Some JDBC drivers are pickier than other ones. I guess H2 needs the specific type.
In this case, use Types.BIGINT, as in:
ps.setNull(1,Types.BIGINT);

Pass the argument along with arguments type using this update function.
Integer id = 1
Integer parent_id = null
String _SQL_UPDATE_API = "update api set api_parent_id = ? where id = ?";
java.lang.Object[] args = [parent_id, id]
int[] argTypes = [java.sql.Types.BIGINT, java.sql.Types.BIGINT]
updCnt = jdbcTemplate.update(_SQL_UPDATE_API, args, argTypes);
Note - this is Groovy syntax, so adjust for Java if required.
Worked for my in Oracle 12.

Related

Is there way to set default as null for SQL parameter?

I have a code that creates sql parameters using MapSqlParameterSource. Here is my code:
MapSqlParameterSource parameters = new MapSqlParameterSource()
.addValue(EVENT_ID, eventId)
.addValue(TYPE, type.toString())
.addValue(ACCOUNT_ID, null)
.addValue(USER_ID, null);
if (Type.SPOOFER_USER == type) {
parameters.addValue(USER_ID, account.getUser().getId());
}
else {
parameters.addValue(ACCOUNT_ID, account.getId());
}
Basically, if account type is spoofer, I have to have user id instead of account id. However, I don't like that I have to set account_id and user_id to null when I instantiate parameters. Is there way to set account_id and user_id as null so I don't have to write this two lines?:
MapSqlParameterSource parameters = new MapSqlParameterSource()
.addValue(EVENT_ID, eventId)
.addValue(TYPE, type.toString())
.addValue(ACCOUNT_ID, null) //////////////////////////THIS ONE
.addValue(USER_ID, null); //////////////////////////AND THIS ONE
Here is my sql query:
INSERT INTO database (id, event_id, type, account_id, user_id)
VALUES (database.nextval, :event_id, :type, :account_id, :user_id)
Update:
Maybe my question was not specific enough. What happens is that when I run
jdbcTemplate.update(insertEventExtra, parameters);
With the given parameters without making them "NULL", I get this exception in my unit test:
org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter 'user_id': No value registered for key 'user_id'
I use hsql to test it. my .sql looks like this:
...
ID NUMBER(38,0) PRIMARY KEY,
EVENT_ID BIGINT NOT NULL,
TYPE VARCHAR2(20 BYTE) NOT NULL,
ACCOUNT_ID NUMBER(38,0),
GROUP_ID NUMBER(38,0),
USER_ID NUMBER(38,0),
...
So my specific question is that my test is giving me exception when I try to run test with parameters without setting them to null.
You must include the addValue(ACCOUNT_ID, null) and addValue(USER_ID, null) because your INSERT statement includes the two named parameters :account_id, :user_id.
The framework attempts to extract the values for the named parameters from the MapSqlParameterSource object and when it does not find one of them, it throws the exception. It does this to avoid user errors, because if you didn't intend to provide a value for a parameter, you wouldn't include the parameter in the INSERT statement.
Make your columns nullable and default to null in the database schema. Then, if you don't specify a value for a column when inserting, it should default to null
By default all not null columns have default value of NULL unless and until you provide value while inserting or updating the column.

JDBC. Replace in all rows value of the column

Java 11. PostgreSQL.
Having following table in db:
TABLE public.account (
id bigserial NOT NULL,
account_id varchar(100) NOT NULL,
display_name varchar(100) NOT NULL,
is_deleted bool NULL DEFAULT false,
);
There are about 1000 rows in this table. In the code I have a static method, which return random string - Helper.getRandomName()
How, using JDBC, in this table (public.account) for all rows replace "display_name" value with value of Helper.getRandomName()?
This is a SQL question. You need to run an update query:
UPDATE public.account set display_name = ?
And provide the new name as the parameter. The absence of a WHERE clause means that all rows will be affected.
If you want to do this for each row individually, then it's harder. You'll want to do a select statement to find all the IDs, and then you can prepare a batch of updates using JDBC, adding a where clause for each ID.
JDBC is just a thin Java wrapper around plain SQL execution.

Missing table alias falls back to using only field name

My tables
N
ID|T_ID
1|1
2|2
T
ID|NAME
1|T1
2|T2
Using the tables as follows
com.db.N N_TABLE = N.as("N_TABLE");
com.db.T T_TABLE = T.as("T_TABLE");
com.db.T T2_TABLE = T.as("T2_TABLE"); //Random alias, not used in query
SelectQuery selectQuery = create.selectQuery();
selectQuery.addFrom(N_TABLE);
selectQuery.addJoin(T_TABLE, JoinType.LEFT_OUTER_JOIN, T_TABLE.ID.eq(N_TABLE.T_ID));
Result<Record> result = selectQuery.fetch();
for (Record record : result) {
System.out.println(record.get(T2_TABLE.NAME));
}
It gives a ambiguity warning, but still gets the value even though alias is wrong. I would expect it to return "null", I guess it falls back to using only field name.
Any idea how should I use it to get "null" in case of a wrong alias?
EDIT
I'll try to provide a more concrete example
My table is as follows
CREATE TABLE user
(
id bigserial NOT NULL,
username character varying(200) NOT NULL,
last_name character varying(100),
created_user_id bigint NOT NULL,
modified_user_id bigint NOT NULL,
CONSTRAINT pk_user PRIMARY KEY (id),
CONSTRAINT user_username_key UNIQUE (username)
)
Data in tables
3;"admin";"admin";3;3
4;"test";"test";4;3
Code
//Input params
Long userId = 4L;
boolean includeModifiedUser = false;
User userTable = USER.as("userTable");
User modifiedUserTable = USER.as("modifiedUserTable");
SelectQuery selectQuery = create.selectQuery();
selectQuery.addFrom(userTable);
//In some cases I want to include the last modifier in the query
if (includeModifiedUser) {
selectQuery.addJoin(modifiedUserTable, JoinType.LEFT_OUTER_JOIN, modifiedUserTable.ID.eq(userTable.MODIFIED_USER_ID));
}
selectQuery.addConditions(userTable.ID.eq(userId));
Record record = selectQuery.fetchOne();
System.out.println(record.get(userTable.LAST_NAME)); //prints "test1"
System.out.println(record.get(modifiedUserTable.LAST_NAME)); //prints "test1", would expect null as modifiedUserTable is currently not joined
Tested on jooq 3.9.3 and 3.9.5
Works as designed
In SQL, there is no such thing as a qualified column name in a result set. Instead, a result set (like any other table) has a set of columns and each column has a name, which is described by jOOQ's Field.getName(). Now, "unfortunately", in top-level SELECT statements, you are allowed to have duplicate column names, in all SQL dialects, and also in jOOQ. This is useful when you join two tables and both tables have e.g. an ID column. That way, you don't have to rename each column just because an ambiguity arises.
If you do have duplicate column names in a table / result, jOOQ will apply the algorithm described in TableLike.field(Field)
This will return:
A field that is the same as the argument field (by identity comparison).
A field that is equal to the argument field (exact matching fully qualified name).
A field that is equal to the argument field (partially matching qualified name).
A field whose name is equal to the name of the argument field.
null otherwise.
If several fields have the same name, the first one is returned and a warning is logged.
As you can see, the rationale here is that if there is no full or partial identity or qualified name equality between a field in the result set and the field you're looking up in the result set, then the field name as in Field.getName() is used to look up the field.
Side note on column match ambiguity
At first, you've mentioned that there was an "ambiguous match" warning in the logs, which then disappeared. That warning is there to indicate that two columns go by the same Field.getName(), but neither of them is an "exact" match as described before. In that case, you will get the first column as a match (for historic reasons), and that warning, because that might not be what you wanted to do.

Prepared Statement failing with DB2 SQL error: SQLCODE: -401, SQLSTATE: 42818

I am working on an application which updates data in a database(IBM DB2 v 9.7) via JDBC . Here's The table schema:
Column name Data Type Length
1)INDEX BIGINT -
2)USER_NAME VARCHAR 30
3)SRC VARCHAR 30
4)STATUS VARCHAR 150
5)RT_COUNT BIGINT -
And my Code is as:
String cmd1="Update ANALYTICS SET RT_COUNT = 1 WHERE USER_NAME = ? AND STATUS = ?";
PreparedStatement process=connection.prepareStatement(cmd1);
process.setString(1, Source);
process.setString(2, Content);
if(process.executeUpdate()==0)
{....
But it fails,can anyone help me ?
Thanks and regards
According to IBM's site, the SQLSTATE of 42818 is 42818 "The operands of an operator or function are not compatible or not comparable.". This means you set the wrong data type to your prepared statement.
You might need to bring the RT_COUNT out and do a setInt or setLong or something to make it work.
Also, I am assuming Source and Content are Strings? If they are not, that may contribute to this as well.
Edit
To do the RT_COUNT thing I was talking about you would have to modify your query to do the following:
String cmd1="Update ANALYTICS SET RT_COUNT = ? WHERE USER_NAME = ? AND STATUS = ?";
PreparedStatement process = connection.prepareStatement(cmd1);
process.setLong(1, new Long(1));
process.setString(2, source);
process.setString(3, content);
if(process.executeUpdate()==0)
{....
And that might actually be your problem, cause I'm not sure how the driver is interpreting the "1". It needs to be Long to map to bigint in DB2. See for what data types in Java map to what data types in DB2.
In addition to #Chris Aldrich's answer of casting in Java, if you know what types the parameters should be, you can also cast them in SQL, like so:
UPDATE ANALYTICS
SET RT_COUNT = CAST(? AS BIGINT)
WHERE USER_NAME = CAST(? AS VARCHAR(30))
AND STATUS = CAST(? AS VARCHAR(150))

SQL update not working with integer ID

This command on SQL Server
UPDATE tbl SET name='Hi' WHERE id=''
works if 'id' is set as an integer value, but it does not work on H2.
What may be the solution?
If ID is integer, you shouldn't use quotes for the value:
UPDATE TEST SET NAME='Hi' WHERE ID = 5; // not ID = '5'
Many databases will accept the quoted version, but are not required to by the SQL language specification.
UPDATE TEST SET NAME='Hi' WHERE ID='1';
that is working in sql server even if id field is integer
but if you want to update the row where id is null then you have to use below statement :
UPDATE TEST SET NAME='Hi' WHERE ID is Null;
instead of UPDATE TEST SET NAME='Hi' WHERE ID ='';
And if id is varchar then you can use your statement to update the values where ID is not null and data is not available there.
But if you want to update the values for record where NULL value of ID field then you have to use
UPDATE TEST SET NAME='Hi' WHERE ID is Null;
H2 throws an exception because it can't convert the empty string '' to a number. H2 internally uses java.lang.Long.parseLong("") which fails with java.lang.NumberFormatException: For input string: "".
For the SQL script:
drop table tbl;
create table tbl(id int primary key, name varchar(255));
insert into tbl values(1, 'Hello');
UPDATE tbl SET name='Hi' WHERE id='';
H2 will throw the exception:
Data conversion error converting [22018-161]
Most other databases (PostgreSQL, Apache Derby, HSQLDB) throw a similar exception.
You need to use a number, or IS NULL as in:
UPDATE tbl SET name='Hi' WHERE id IS NULL;
or
UPDATE tbl SET name='Hi' WHERE id = 0;

Categories

Resources