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(?,'')
Related
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'm using spring-data-couchbase 2.1.2, I redefined a repository with a custom query:
#Override
public List<PortfolioDocument> searchPortfolio() {
CouchbaseOperations template = templateProvider.resolve(PortfolioRepository.class, PortfolioDocument.class);
String statement = "select META(ipdb).id AS _ID, META(ipdb).cas AS _CAS, * " +
"from ipdb where _class = 'com.ipdb.datamodel.document.PortfolioDocument' AND title = 'dummytitle'";
SimpleN1qlQuery query = N1qlQuery.simple(statement);;
List<PortfolioDocument> portfolioDocuments = template.findByN1QL(query, PortfolioDocument.class);
....
}
template.findByN1QL(query, PortfolioDocument.class); returns some PortfolioDocument with all property to null except the id property.
If I call template.findByOne(id); the resulting object is ok.
Can you help me, please?
I have found a solution. The correct query is:
SELECT META(`ipdb`).id AS _ID, META(`ipdb`).cas AS _CAS, `ipdb`.* FROM `ipdb` WHERE ...
The best practice is to use the methods of N1qlUtils, you can look at the example below:
Statement statement = N1qlUtils.createSelectClauseForEntity(template.getCouchbaseBucket().name()).
from(Expression.i(template.getCouchbaseBucket().name()))
.where(".....");
createSelectClauseForEntity and from methods build the a part of query.
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, "");
}
I have query like
`Select * from Table1 where xyz in (List of String to be Supplied).
In my java code. I have a dao object in which I am calling this sql using jdbc template.
The method takes in a list of String and that needs to be supplied to this SQl. I have my row-mapper.
How to write the SQl and how to pass the list of variables?
My SQL will run on a Teradata Db.
Use a NamedParameterJdbcTemplate which, as the doc says:
It also allows for expanding a List of values to the appropriate number of placeholders.
So you just need
String sql = "select * from Table1 where xyz in :list";
// or String sql = "select * from Table1 where xyz in (:list)";
// I can't remember which one is right
Map parameters = new HashMap<String, Object>();
parameters.put("list", theListOfXyz);
List<Foo> result = template.query(sql, parameters, rowMapper);
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.