How to get number of affected rows from JdbcTemplate? - java

I'm using spring JdbcTemplate to execute a sql query:
JdbcTemplate template = new JdbcTemplate(ds);
template.execute(sqlInsert); //returns void
How could I get the number of effected rows, as the execute() method returns void?

Call the update method of JdbcTemplate. It will gives you the number of effected rows as return value.
update
public int update(PreparedStatementCreator psc)
throws DataAccessException
Description copied from interface: JdbcOperations
Issue a single SQL update operation (such as
an insert, update or delete statement) using a
PreparedStatementCreator to provide SQL and any required parameters.
A PreparedStatementCreator can either be implemented directly or
configured through a PreparedStatementCreatorFactory.
Specified by:
update in interface JdbcOperations
Parameters:
psc - object that provides SQL and any necessary parameters
Returns:
the number of rows affected
Throws:
DataAccessException - if there is any problem issuing the update
See Also:
PreparedStatementCreatorFactory

You can probably use JdbcTemplate.update() for that case. this will return the number of rows updated or deleted.

Related

org.hibernate.HibernateException: PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered

In my PostgreSQL database I have a function my_cursors() that returns 2 cursors. I need to call it through a Java app, so I used Hibernate ORM, but I am getting the error in the title.
Question: Is there another way to call my_cursors(), so that it returns only a single cursor?
Below is my junit test:
#SuppressWarnings("unchecked")
#Test
#Transactional("myTransactionManager")
public void call_my_cursors() {
StoredProcedureQuery query = em.createStoredProcedureQuery("my_cursors")
.registerStoredProcedureParameter(1, Object.class, ParameterMode.REF_CURSOR)
.registerStoredProcedureParameter(2, Object.class, ParameterMode.REF_CURSOR);
List<Object> obj = (List) query.getResultList();
assertNotNull(obj);
}
You can use a custom Dialect and override org.hibernate.dialect.PostgreSQL81Dialect#getCallableStatementSupport to return a custom implementation similar to org.hibernate.procedure.internal.PostgresCallableStatementSupport that doesn't do this check. The question is, since when does PostgreSQL support multiple REF_CURSOR? I don't think this was done for fun, so it would be nice to know which version of PostgreSQL starts to support this so that this can be updated in Hibernate.

How to get exact cql from statement using java api from datastax

My code directly executes the bound statement prepared without any exact query. Then how to get the cql it is trying to perform in cassandra database?
For example:
public <T> void save(T entity) {
if (entity != null) {
Statement statement = getEntityMapper(entity).saveQuery(entity);
statement.setConsistencyLevel(consistencyLevelWrite);
mappingManager.getSession().execute(statement);
}
}
I am trying to get something like INSERT INTO "keyspace"."tableName"("column1","column2") VALUES (value1,value2)
My most generic answer is to enable the query logger. It will show executed queries in your application logs.
If you need something more specific and want to manipulate the query string in your own code, you can take inspiration from the implementation: QueryLogger.java. In this particular case, you can get the "generic" query string (with placeholders) by casting to BoundStatement and then invoking .preparedStatement().getQueryString() on it; then inspect the bound statement for the values of the placeholders. As you'll see in the code, QueryLogger handles a lot of corner cases (e.g. truncating large parameters).

IncorrectResultSizeDataAccessException - Trying to simply get the most recent row

I have a Spring CrudRepository called 'ImportReceiptRepository'.
I'm simply trying to compose a method which grabs the first row in an order by clause.
Here's what I'm using currently:
ImportReceipt importReceipt = this.importReceiptRepository.getOneByImportTypeOrderByTimestampDesc(importType);
The issue is that when there is more than a single row returned, Spring throws a:
org.springframework.dao.IncorrectResultSizeDataAccessException: result returns more than one elements; nested exception is javax.persistence.NonUniqueResultException: result returns more than one elements
How should I rename this CrudRepository function to simply grab the first row when there are 0-n rows returned?
Simply using Pageable was the key:
List<ImportReceipt> findByImportType(String importType, Pageable pageable);
and calling it like:
List<ImportReceipt> importReceipts = this.importReceiptRepository.findByImportType(importType, new PageRequest(0, 1, Direction.DESC, "Timestamp"));
ImportReceipt importReceipt = importReceipts.get(0);
Credit: How to combine pagination with a criteria query in Spring Data JPA?
You can make it even simpler by findFirst:
ImportReceipt findFirstByImportType(String importType);
For more info: Limiting Query Results
As of the upcoming version 1.7 of Spring Data JPA, the query method as you originally declared it works out of the box. See this ticket for details. The support has already been published in the first milestone of the release.

"Not supported for DML operations" with simple UPDATE query

I'm getting the error Not supported for DML operations when I use the following HQL...
#Query("UPDATE WorkstationEntity w SET w.lastActivity = :timestamp WHERE w.uuid = :uuid")
void updateLastActivity(#Param("uuid") String uuid, #Param("timestamp") Timestamp timestamp);
What could be causing the issue? It doesn't seem to be a common error given the few results I've found in Google.
Check the post hibernate hql ERROR: Not supported for DML operations in the hibernate users forum.
Most likely you called
querySt.list();
for your UPDATE query. Instead you should call
querySt.executeUpdate();
I was also having the same problem with annotations.After searching and doing some tricks I was able to solve it.
There are some below steps which you need to verify while using DML operation with JPA.
Use anotation
#Modifying(org.springframework.data.jpa.repository.Modifying) and #Transactional(org.springframework.transaction.annotation.Transactional) on required method.
Use void as return type of method.
e.g:-
#Modifying
#Query("UPDATE ProcedureDTO o SET o.isSelectedByUser =?1")
#Transactional
public void getListOfProcedureBasedOnSelection(Boolean isSelected);```
Make sure your service class method which calls updateLastActivity has #Transactional(org.springframework.transaction.annotation.Transactional) annotation. and modify the repository method to below,
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
...
#Modifying
#Query("UPDATE WorkstationEntity w SET w.lastActivity = :timestamp WHERE w.uuid = :uuid")
void updateLastActivity(#Param("uuid") String uuid, #Param("timestamp") Timestamp timestamp);
For more insights please use this answer.
I had exact same problem, in my case I had to only add #Modifying annotation. According to documentation:
Indicates a query method should be considered as modifying query as that changes the way it needs to be executed. This annotation is only considered if used on query methods defined through a Query annotation. It's not applied on custom implementation methods or queries derived from the method name as they already have control over the underlying data access APIs or specify if they are modifying by their name.
Queries that require a #Modifying annotation include INSERT, UPDATE, DELETE, and DDL statements.
The same happened to me because, being q an object of class Query, q.list() is not to be used for updates or deletes, but q.executeUpdate()

How to return ids on Inserts with mybatis in mysql with annotations

See this related question for Postgres. For some reason, the solution doesn't work for me - the return value of the insert statement is always "1".
See this other question for an XML based solution. I would like to do the same without XML - insert a record and find the new auto-generated id of the record I just insreted.
I didn't find a matching annotation to <selectkey> (see this open issue)
How do I proceed?
Examining mybatis code reveals that INSERT is implemented via UPDATE, and always returns the number of inserted rows! So ... unless I'm completely missing something here, there's no way to do this using the current (3.0.3) implementation.
Actually, it's possible to do it, with the #Options annotation (provided you're using auto_increment or something similar in your database) :
#Insert("insert into table3 (id, name) values(null, #{name})")
#Options(useGeneratedKeys=true, keyProperty="idName")
int insertTable3(SomeBean myBean);
Note that the keyProperty="idName" part is not necessary if the key property in SomeBean is named "id". There's also a keyColumn attribute available, for the rare cases when MyBatis can't find the primary key column by himself. Please also note that by using #Options, you're submitting your method to some default parameters ; it's important to consult the doc (linked below -- page 60 in the current version) !
(Old answer) The (quite recent) #SelectKey annotation can be used for more complex key retrieval (sequences, identity() function...). Here's what the MyBatis 3 User Guide (pdf) offers as examples :
This example shows using the #SelectKey annotation to retrieve a value from a sequence before an
insert:
#Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
#SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
This example shows using the #SelectKey annotation to retrieve an identity value after an insert:
#Insert("insert into table2 (name) values(#{name})")
#SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
The <insert>, <update>and <delete> statements return the number of affected rows, as is common with database APIs.
If a new ID is generated for the inserted row, it is reflected in the object you passed as a parameter. So for example, if you call mapper.insert(someObject) inside your annotated insert method, after inserting, you can call someObject.getId (or similar) to retrieve it.
Using the options of <insert>, you can tweak how (by providing an SQL statement) and when (before or after the actual insertion) the id is generated or retrieved, and where in the object it is put.
It may be instructive to use the MyBatis generator to generate classes from a database schema and have a look at how inserts and updates are handled. Specifically, the generator produces "example" classes that are used as temporary containers to pass around data.
you can get your generated ids from save methods,
lets say a bean with ID and name properties,
bean.setName("xxx");
mapper.save(bean);
// here is your id
logger.debug(bean.getID);
I didn't like most of the answers I found online for returning generated keys because
All of the solutions I found called a "setter" on the inbound object
None of the solutions returned the generated column from the method
I came up with the following solution which addresses points 1 & 2 above which
Passes two parameters to mybatis "in" & "out" (mybatis does not mutate "in", it calls a setter on "out")
Requires an additional default method on the interface to return the value
public interface MyMapper {
/**
* this method is used by the mybatis mapper
* I don't call this method directly in my application code
*/
#Insert("INSERT INTO MY_TABLE (FOO) VALUES ({#in.foo})")
#Options(useGeneratedKeys=true, keyColumn="ID", keyProperty = "out.value")
void insert(#Param("in") MyTable in, #Param("out") LongReference out);
/**
* this "default method" is called in my application code and returns the generated id.
*/
default long insert(MyTable tableBean) {
LongReference idReference = new LongReference();
insert(tableBean, idReference);
return idReference.getValue();
}
}
This requires an additional class which can be re-used on similar methods in future
public class LongReference {
private Long value;
// getter & setter
}

Categories

Resources