I'm using SimpleJdbcTemplate and MapSqlParameterSource in the folowing way:
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("typeId", typeId, Types.BIGINT);
List<Long> ids = _jdbcTemplate.query(_selectIdByParameters, new EntityIdRowMapper(), parameterSource);
When typeId ( which is a Long ) is null, then the query looks in the following way:
SELECT id FROM XXX WHERE typeId = null
whereas I would expect it to generate
SELECT id FROM XXX WHERE typeId IS NULL
I've reported this issue and the response was that
You will have to provide the appropriate SQL statement based on your query parameters.
and as a consequence my code is littered with null checks.
Is there a more elegant way of handling null parameters sent to the SimpleJdbcTemplate?
They have a point - JdbcTemplate isn't a SQL interpreter, it just replaces your placeholders.
I suggest you construct your clause with a utility method, and concat it to the query string:
String createNullCheckedClause(String column, Object value) {
String operator = (value == null ? "is" : "=");
return String.format("(%s %s ?)", column, operator);
}
...
String query = "select * from table where " + createNullCheckedClause("col", x);
Not very pretty. Alternatively, perhaps you can configure MySQL to allow "= NULL", but I don't think that's an option.
Related
I have an issue with JdbcTemplate when passing parameters objects with null reference.
Given that I have the following test data:
jdbcTemplate.execute("INSERT INTO TEST_TABLE VALUES (1,'TEST')");
jdbcTemplate.execute("INSERT INTO TEST_TABLE VALUES (2,'TEST_2')");
jdbcTemplate.execute("INSERT INTO TEST_TABLE VALUES (3, NULL)");
Following code doesn't retrieve anything:
String contentArg = null;
List<Entity> entityList_3 = jdbcTemplate.query("SELECT * FROM TEST_TABLE WHERE CONTENT = ?", new BeanPropertyRowMapper<>(Entity.class), contentArg);
Is there any way I could fix this, just using JdbcTemplate.
Use Oracle NVL function to support optional parameter:
WHERE CONTENT = NVL(?, CONTENT) "
I've been able to solve this by modifying the query.
In my case, I was using IBM solidDb and the fix contains in using IFNULL function.
AND IFNULL(CONTENT,'') = IFNULL(?,'')
I have my Oracle DB Stored Function as below:
CREATE OR REPLACE FUNCTION FN_EMP_CNT (EMP_ID NUMBER) RETURN NUMBER
IS
OLD_COUNT NUMBER(5) DEFAULT 0;
NEW_COUNT NUMBER(5) DEFAULT 0;
BEGIN
SELECT
COUNT(EMP_ID) INTO OLD_COUNT
FROM
OLD_DEPT
WHERE
EID = EMP_ID
AND DEPT_STAT='Closed';
SELECT
COUNT(EMP_ID) INTO NEW_COUNT
FROM
NEW_DEPT
WHERE
EID = EMP_ID
AND DEPT_STAT='Closed'
RETURN (NEW_COUNT + OLD_COUNT);
END;
When I use the below sql query directly it returns the correct number as 2:
SELECT FN_EMP_CNT(123) FROM DUAL;
But when I use Spring JDBC Template for retrieving the data it returns null.
int noOfEmps = jdbcTemplate.queryForObject("SELECT FN_EMP_CNT(?) FROM DUAL", new Object[] { empID}, Integer.class);
The most probable cause is that you use a wrong order of parameters, see Javadoc queryForObject
queryForObject(java.lang.String sql, java.lang.Class<T> requiredType, java.lang.Object... args)
Query given SQL to create a prepared statement from SQL and a list of
arguments to bind to the query, expecting a result object.
So use first the required return type followed by the parameter
This works for my fine
sql = 'SELECT FN_EMP_CNT(?) FROM DUAL'
res = jdbcTemplate.queryForObject(sql, Integer.class, 4)
HTH
I have the following DAO method:
public String getSomeTable(final String param1) {
String sqlString = "select * from table where name ilike ?";
Query query = this.getEntityManager().createNativeQuery(sqlString);
query.setParameter(1, "%param1%");
}
If param1 is null or empty then I want to select all entries from the table. What is the correct way to do this? I am currently using the following:
public String getSomeTable(final String param1) {
String sqlString = "select * from table where name = ?";
Query query = this.getEntityManager().createNativeQuery(sqlString);
if(param1 == null)
query.setParameter(1, "%%");
else
query.setParameter(1, "%param1%");
}
But this is not scalable. I have datatypes like integer, date, etc. I want to know if there is a way to skip checking for that parameter if it is null.
I was planning to use COALESCE(?, CASE WHEN ? = '' THEN '%%' ELSE '%?%') but I think ? can be used only once for a particular parameter. The next one > I write is linked to second param.
On SQL Server, I use something like this, perhaps you can translate it to postgres:
DECLARE #variable INT = NULL;
SELECT *
FROM sysobjects
WHERE
(1 = CASE WHEN #variable IS NULL THEN 1 ELSE 2 END)
OR
(id LIKE #variable);
I am getting null result through QueryRunner, but if I execute the query I am getting rows retruned in SQL developer.
ProductId: null
SKUCode:61334a
ServiceResult<List<T>> market = service.getMarketProduct(null,'61334a');
The above mentioned service code will hit DAO layer shown below:
return QueryHelper.queryList( GET_PRODUCTS, new Object[]{ 0 == ProductId? null : ProductId, SKUCode}, this.cs, CPCMarketProductVO.class );
GET_PRODUCTS= SELECT * FROM table WHERE ProductId= nvl(?, ProductId) AND prd_id = nvl(?, SKUCode).
Can someone please tell me why I am getting null results through java code and how results are returned in SQL developer.
I think you are a bit confused in what NVL function is for. Pratically it is useful when the query returns a NULL value and you are doing some kind of operation or aggregate function in it. Instead you are using it to create the request.
Probably you should change the GET_PRODUCTS query in something like
GET_PRODUCTS= SELECT * FROM table WHERE ProductId = ? AND prd_id = ?
When you substitute the ? values with the new Object[]{ 0 == ProductId? null : ProductId, SKUCode} and values ProductId = null, SKUCode = 61334a the query becomes:
GET_PRODUCTS= SELECT * FROM table WHERE ProductId = null AND prd_id = 61334a
returning an empty result set, which should be correct if ProductId is not nullable (I assume is a primary key).
If you instead have NULL ProductIds in your table the result set may not be empty.
Here is my code part:
Query q = em.createNativeQuery("insert into table_name (value_one, value_two, value_three) values (?,?,?)");
q.setParameter(1, value1);
q.setParameter(2, value2);
q.setParameter(3, value3);
q.executeUpdate();
value3 sometimes can be null (Date class object). And if it is null the following exception is thrown:
Caused by: org.postgresql.util.PSQLException: ERROR: column "value_three" is of type timestamp without time zone but expression is of type bytea
Hint: You will need to rewrite or cast the expression.
Position: 88
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:334)
at org.hibernate.engine.query.NativeSQLQueryPlan.performExecuteUpdate(NativeSQLQueryPlan.java:189)
... 11 more
How is it possible to get this code working and to persist null value into database?
I have faced the same issue when use EntityManager.createNamedQuery(guess the same issue with createNativeQuery).
In case you are going to pass nullable parameter to Query then use TypedParameterValue which allows to pass the type.
For instance:
setParameter("paramName", new TypedParameterValue(StandardBasicTypes.LONG, paramValue));
Here you explicitly set the type of passed value and when you pass null value as processor know the exact type.
You are using postgresql (already the stack is telling that), and likely Hibernate, and almost certainly hitting this problem: PostgreSQL JDBC Null String taken as a bytea
I used this particular solution:
https://stackoverflow.com/a/23501509/516188
So that means escaping to the Hibernate API so you can give the type of the expression.
In my case it was a nullable Short so I used:
.setParameter("short", shortValue, ShortType.INSTANCE);
shortValue being of type Short.
if you are not using Hibernate, but you are using EclipseLink you can use the query Hint:
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Query_Hints
Example
query:
String insert = "INSERT INTO mytable VALUES ( ?, ?, ?, ?)";
em = getEntityManager();
em.getTransaction().begin();
Query u = em.createNativeQuery(insert);
u.setHint(QueryHints.BIND_PARAMETERS, HintValues.FALSE);//<--the hint
u.setParameter(1, "value1");
u.setParameter(2, "value2");
u.setParameter(4, "value4");//just skipped the null element
u.executeUpdate();
em.getTransaction().commit();
and the result will be an insertion:
mytable
column1 column2 column3 column4
value1 value2 value4
of course if "column3" is nullable in the db...
I don't know if works also with Hibernate, but it could. I used PostgreSQL for my test.
In my case, using Oracle 12 and jboss 7.3, I solved using an empty String as param value. I don't understand why, but it works. This is my code:
String sql = "insert into customer (id,name,age) values (?1,?2,?3);
em = getEntityManager();
Query query = em.createNativeQuery(sql);
query.setParameter(1, getId());
query.setParameter(2, getName());
if (getAge() != null) {//getAge() return BigDecimal and map a NUMBER column type
query.setParameter(3, getAge());
} else {
query.setParameter(3, "");
}