Can't seem to find a way to get one string from table using JdbcTemplate query.
This is the table my sql returns:
ID | STREET_NAME
------------------------
1 | Elm street
Now how am I supposed to get the value of STREET_NAME. SQL always returns one row, so no need to worry about returning more than one row.
For some background info:
INNER JOIN and COUNT in the same query
Using Tony Stark answer to get my table.
But how can I extract "Elm street" from it using JdbcTemplate?
It would help a lot to know what your SQL query looks like, but assuming it's something like SELECT STREET_NAME FROM table WHERE ID=1;
CODE:
public String getStreetNameById(int id) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "SELECT STREET_NAME FROM table WHERE ID=?";
String streetName = (String) jdbcTemplate.queryForObject(
sql, new Object[] { id }, String.class);
return streetName;
}
The class JdbcTemplate implements JdbcOperations.
If you look at the queryForObject javadocs in JdbcOperations it states:
Deprecated. as of 5.3, in favor of queryForObject(String, Class, Object...)
Basically, they have changed the method signature to get rid of Object[] arguments in the method signatures in favor of varargs. Please, see the relevant Github issue.
You can rewrite answer of #jlewkovich with the new method signature like this:
public String getStreetNameById(int id) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "SELECT STREET_NAME FROM table WHERE ID=?";
String streetName = (String) jdbcTemplate.queryForObject(
sql, String.class, id);
return streetName;
}
If you want to get only one column "string" from your table (or any query with joins), you have to say the name of the column.
Using SELECT * FROM TABLE is a very-very bad practice by the way. I bet you did this.
#JLewkovich modified code:
public String getStreetNameById(int id) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "SELECT STREET_NAME FROM table WHERE ID=?";
String streetName = (String) jdbcTemplate.queryForObject(
sql, new Object[] { id }, String.class);
return streetName;
}
But what if there is 0 or more than one result?
Think about it!
But to getting a sequence value (in Oracle), this should work.
public Long getSequence() {
Long seq;
String sql = "select SEQ_XY.NEXTVAL from dual";
seq = jdbcTemplateObject.queryForObject(sql, new Object[] {}, Long.class);
return seq;
}
As per latest specification queryForObject with below syntax is now deprecated
<T> T queryForObject(String sql, Object[] args, Class<T> requiredType)
New method uses varargs.
<T> T queryForObject(String sql, Class<T> requiredType, Object... args)
Updated Solution: We have to replace the class type with Object args and vice-versa.
Sql Query: SELECT STREET_NAME FROM table WHERE ID=1;
public String getStreetNameById(int id) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "SELECT STREET_NAME FROM table WHERE ID=?";
String streetName = (String) jdbcTemplate.queryForObject(
sql, String.class, new Object[] { id });
return streetName;
}
I usually do this way:
String result = DataAccessUtils.singleResult(
jdbcTemplate.queryForList(
"SELECT street_name FROM table WHERE id = :id",
Collections.singletonMap("id", id),
String.class
)
)
queryForList is used instead of queryForObject for handling empty results. queryForObject throws EmptyResultDataAccessException for empty results. Often it is not desired behavior.
DataAccessUtils.singleResult + queryForList:
returns null for empty result
returns single result if exactly 1 row found
throws exception if more then 1 rows found. Should not happen for primary key / unique index search
DataAccessUtils.singleResult
Related
I want to delete multiple database entries at once. Each entry should only be deleted if 3 fields match (here: name, email, age).
If I'd just wanted to delete by a single property, I'd go for:
String sql = "DELETE FROM persons WHERE (email) IN (?)";
JdbcTemplate template;
template.execute(sql, Arrays.asList(emails...));
But what if my condition is formed by multiple fields?
String sql = "DELETE FROM persons WHERE (name, email, age) IN (?, ?, ?)";
JdbcTemplate template;
template.execute(sql, ...); ???
The condition should always match all 3 fields (AND)!
Use the batchUpdate(sql, batchArgs, argTypes) method.
String sql = "DELETE FROM persons WHERE name = ? AND email = ? AND age = ?";
int[] argTypes = { Types.VARCHAR, Types.VARCHAR, Types.INTEGER };
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[] { "John Doe", "john#example.com", 42 });
batchArgs.add(new Object[] { "Jane Smith", "jane#example.com", 47 });
. . .
JdbcTemplate template = ...;
int[] rowCounts = template.batchUpdate(sql, batchArgs, argTypes);
A batchUpdate is what you are looking for here. You would need to change/tweak your query a little bit though.
If you can pass a list of objects (you must match the class members with the values on the SQL query), it can be done automatically:
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
// class People { String name; String email; Integer age; }
final String sql = "DELETE FROM persons WHERE name = :name AND email = :email AND age = :age";
final SqlParameterSource[] batchArgs = SqlParameterSourceUtils.createBatch(people.toArray()); // List<People>
final int[] results = namedParameterJdbcTemplate.batchUpdate(sql, batchArgs);
logger.debug("{} record(s) inserted successfully", results.length);
The other approach would be what #Andreas proposed.
I would also recommend to use, always, parameterized queries: DELETE FROM persons WHERE name = :name AND email = :email AND age = :age.
I am new to PostgreSQL. I need to call postgresql function from spring jdbctemplate for storing Employee table details. Below is my code in which I am using insert query to store the Employee details. I need to replace insert query with the Postgresql function-"UpdateEmployee".
#Autowired
JdbcTemplate postgressqljdbctemplate;
#Override
public void update(Employee employee) {
String SQL = "insert into employee(Id, name, age, salary) values (?,?,?,?)";
postgressqljdbctemplate.update(SQL, new Object[] { employee.getId(), employee.getName(),
employee.getAge(), employee.getSalary()});
}
Ok, the first thing you should do is design that function for insert/update data. Postgres supports many languages for that, but the most popular one is plpgsql.
The function itself might look like:
CREATE OR REPLACE FUNCTION update_employee(p_id INT, p_name VARCHAR(255), p_age INT, p_salary DECIMAL)
RETURNS INT
LANGUAGE plpgsql
AS $$
BEGIN
IF p_id IS NULL
THEN
INSERT INTO employee (name, age, salary) VALUES (p_name, p_age, p_salary) RETURNING id INTO p_id;
ELSE
UPDATE employee
SET name = p_name, age = p_age, salary = p_salary
WHERE id = p_id;
END IF;
RETURN p_id;
END;
$$;
Now if you call this function with null as ID it will insert the data, otherwise data will be found by specified id and updated.
In both cases you'll get the ID of modified record back.
SELECT update_employee(NULL, 'John', 42, 100000); -- insert (returns 1)
SELECT update_employee(1, 'John Smith', 42, 200000); -- update previous record
Probably it would be better to separate insert function from update, but it is just a sample.
So, with that you can call the function from spring using for example SimpleJdbcCall:
final SimpleJdbcCall updateEmployeeCall = new SimpleJdbcCall(jdbcTemplate).withFunctionName("update_employee");
final Map<String, Object> params = new HashMap<>();
params.put("p_id", null);
params.put("p_name", "John");
params.put("p_age", 28);
params.put("p_salary", 150000);
final Map<String, Object> result = updateEmployeeCall.execute(params);
System.out.println(result.get("returnvalue"));
Given that my native SQL query returns a single non-null result, can somebody help me evaluate using query.getSingleResult() vs using query.getResultList.get(0)
Just for example:
String queryStr = "Select count(id) FROM Job J where companyMaster = 3";
Query query = getEntityManager().createNativeQuery(queryStr);
return ((BigInteger) query.getResultList().get(0)).intValue();
produces the same result as:
String queryStr = "Select count(id) FROM Job Jo where companyMaster = 3";
Query query = getEntityManager().createNativeQuery(queryStr);
return ((BigInteger) query.getSingleResult()).intValue();
If you are using database method like getById then it is okay to use the getSingleResult method. In this case, you are sure that there is only one entity in the database matching that id.
But as you are performing a count, you should use the getSingleResult as it almost definitely will return you a result rather than using getResultList.get(0).
I am trying to extract ROWID or the primary key using Spring's NamedParameterJdbcTemplate and GeneratedKeyHolder.
I am trying to do something like this.
MapSqlParameterSource parameters = new MapSqlParameterSource()
.addValue("param1", value1)
.addValue("param2", value2);
KeyHolder keyHolder = new GeneratedKeyHolder();
namedParameterJdbcTemplate.update("INSERT INTO TABLE(ID, col1, col2)"
+ "VALUES(TABLE.TABLE_SEQ.NEXTVAL, :param1, :param2)",
parameters, keyHolder);
After executing above query when I try to do keyHolder.getKey().longValue() it is throwing below exception.
HTTP Status 500 - Request processing failed; nested exception is org.springframework.dao.DataRetrievalFailureException: The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
When I went through this http://docs.oracle.com/cd/B28359_01/java.111/b31224/datacc.htm I understand (i hope i did) that ojdbc is not mapping oracle RowId to java RowId.
Can any one suggest is there any way to extract the key? (Yes it can be done using PreparedStatement but it is making my code bit ugly to read and manipulate on some conditions). Your suggestions are much appreciated.
You have to use this
namedParameterJdbcTemplate.update("INSERT INTO TABLE(ID, col1, col2)"
+ "VALUES(TABLE.TABLE_SEQ.NEXTVAL, :param1, :param2)",
parameters, keyHolder, new String[]{"ID"});
Here is a fully working example: Assuming Database is Oracle and column name which store generated Id is "GENERATED_ID" ( Can be any name)
public Integer insertRecordReturnGeneratedId(final MyObject obj)
{
final String INSERT_QUERY = "INSERT INTO MY_TABLE VALUES(GENERATED_ID_SEQ.NEXTVAL, :param1, :param2)";
try
{
MapSqlParameterSource parameters = new MapSqlParameterSource().addValue( "param1", obj.getField1() ).addValue( "param2", obj.getField1() ) ;
final KeyHolder holder = new GeneratedKeyHolder();
this.namedParameterJdbcTemplate.update( INSERT_QUERY, parameters, holder, new String[] {"GENERATED_ID" } );
Number generatedId = holder.getKey();
// Note: USING holder.getKey("GENERATED_ID") IS ok TOO.
return generatedId.intValue();
}
catch( DataAccessException dataAccessException )
{
}
}
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.