Spring #Trasactional is not rolling back while there is an exception - java

I am new to spring and working on a sample program using Spring jdbc. this is to check how spring #Trsactional working and rolling back the changes to the Db if there is an exception.
But I am not able to achieve this. Through I am raising an exception in one of the DB update, still it's inserting the data to DB and not rolling back.
I know somewhere I am making mistake but not able to figure it out. Not sure if this is a correct approach.
What I am doing :-
in main methis I am calling load methos of Global class (which has jdbcTemplate as satic member as I will this jdbcTemplate to all other classes)
Global class load methos will initiate the bean using ApplicationContext.
Creating Dbclass instance in main method and sending the jdbcTemplate as parameter.
4.creating some sample data and calling executeDb method.
5.execute DB method will create the instance of other Dbclasss and setting the jdbcTemplate which earlier I initialized using bean in main method (I have separate class for each operation - like createuser, UpdataBalance etc)
then it will call the db opration method to insert data (I am using batchupdate)
EDIT - Removed all try-catch
DB opration code:-
#Transactional(rollbackFor={Exception.class})
public void executeDB(int count) throws Exception
{
CreateAccount newacc = new CreateAccount(jdbcTemplate);
CreateUser newusr = new CreateUser(jdbcTemplate);
//BalanceUpdate newbal = new BalanceUpdate(jdbcTemplate);
newacc.addList(acclist);
newusr.addToList(usrlist);
//newbal.addList(ballist);
newusr.execute(); // insert data to db
newacc.addAccount(); // insert data to db
//newbal.addBalance(); // insert data to db
newacc.getAccList().clear();
newusr.getUserList().clear();
//newbal.getBalanceList().clear();
if(count == 5000)
{
Thread.sleep(1000);
throw new Exception("Rollback");
}
count += 1000;
//throw new Exception();
}
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-xml -->
<context:component-scan base-package="com.example"></context:component-scan>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe"/>
<property name="username" value="system"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="startit" class="com.example.springtransaction.GlobalClass">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="dbupdate" class="com.example.springtransaction.DbUpdate">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>

You need to throw exception from your method not silently log it in catch block.
And for checked exceptions you need to use #Transactional(rollbackFor = {Exception.class}).
http://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/transactional-roll-back/
https://www.catalysts.cc/en/wissenswertes/spring-transactional-rollback-on-checked-exceptions/

You should remove the try - catch and define that the method throws an Exception. Something like that
#Transactional(rollbackFor={Exception.class})
public void executeDB() throws Exception
{
if(usrlist.size() >= 5)
{
CreateAccount newacc = new CreateAccount(jdbcTemplate);
CreateUser newusr = new CreateUser(jdbcTemplate);
BalanceUpdate newbal = new BalanceUpdate(jdbcTemplate);
newacc.addList(acclist);
newusr.addToList(usrlist);
newbal.addList(ballist);
newusr.execute(); // insert data to db
newacc.addAccount(); // insert data to db
newbal.addBalance(); // insert data to db - raise exception here
}
}
Update
The class that contain the executeDB() method should be a #Component and inject that component in the main class.
Not create a new Dbclass() instance by your own.
In high-level the reason is that the Spring creates proxy classes upon injection for classes that declare #Transactional.
You could read more about Aspect-Oriented Programming here.

Related

How to rollback a transactional database operation and save the error message into another table?

I'm using Spring and Hibernate trying to update a value of the database. It is needed that, in case of exceptions, save the error message into a table. I'm facing some errors because when I'm able to save the message into the database, the transaction doesn't rollback. Here is the pseudo-code
#Transactional
public class ObjectTable(){
//instanciate other objects
RelatedObject relatedObject = relatedObjectController.getObjectById(primaryKey)
Object object = objectController.getObjectByRelatedObject(relatedObject.getPrimaryKey())
#Transactional(rollbackFor = Exception.class)
public updateObject(Object object) throws MyCustomException{
try{
getHibernateTemplate().getSessionFactory.getCurrentSession().evict(object);
getHibernateTemplate().getSessionFactory.getCurrentSession().saveOrUpdate(object);
getSession.flush();
}catch(Exception ex){
saveErrorMessageIntoDatabase(ex.getMessage, this.relatedObject);
throw new MyCustomException(ex.getMessage)
}
}
public saveErrorMessageIntoDatabase(String message, RelatedObject relatedObject){
relatedObject.setErrorMessage(message);
getHibernateTemplate().getSessionFactory.getCurrentSession().evict(relatedObject);
getHibernateTemplate().getSessionFactory.getCurrentSession().saveOrUpdate(relatedObject);
getSession.flush();
}
}
With this kind of tought, I'm not being able to save the message in relatedObject and rollback the changes in object. Making a few variations, such as putting a propagation = Propagation.REQUIRES_NEW or propagation = Propagation.REQUIRED or removing the Excpection.class for rollback, I get some other behaviours, like save the error message but also writes the changes of object when there is an exception, or rollin back the changes but also not writing the error message into relatedObject.
I also tried with merge instead of saveOrUpdate, but with no success.
Could someone help me write a way to rollback changes in case of error but also save the error message into the database?
Thank you.
*I don't post the actual code because this is not a personal project.
EDIT:
My transactionManager is configured as a XML bean where first I create the objectTableTarget setting the sessionFactory property and bellow that I set another bean objectTable refering to the methods I want to set as transactional.
<bean id="objectTableTarget" class="br.com.classes.ObjectTable" singleton="true">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="objectTable" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="objectTableTarget" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
<prop key="updateObject*">PROPAGATION_REQUIRED,-br.com.package.exception.MyCustomException</prop>
</props>
</property>
</bean>
move your log method into separate service and annotate it with
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Service
public class ErrorLoggerService(){
#Transactional(propagation = Propagation.REQUIRES_NEW)
public saveErrorMessageIntoDatabase(String message, RelatedObject relatedObject){
relatedObject.setErrorMessage(message);
getHibernateTemplate().getSessionFactory.getCurrentSession().evict(relatedObject);
getHibernateTemplate().getSessionFactory.getCurrentSession().saveOrUpdate(relatedObject);
getSession.flush();
}
}
Then spring can create proxy on your method and error should be write in new transaction

How to load LDAP configs in Spring Application lazily.

I have application-context.xml which is having beans like below.
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" >
<property name="jndiName" value="java:/comp/env/DB_NAME" />
</bean>
<bean id="jdbcTemplate" name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
and one context.xml like
<ResourceLink name="DB_NAME1" global="application/cn=MyDB,ou=Database Connections" />
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
If you notice in my context.xml I kept my resource name as DB_NAME1
I also kept default-lazy-init="true" in beans tag at the top of my application-context.xml file. Still I am getting below error
javax.naming.NameNotFoundException: Name [DB_NAME] is not bound in this Context. Unable to find [DB_NAME].
So my question is, How to load my jdbcTemplate/dataSource lazily.
Because in my application some of the services are hitting DB and some are hitting other services. So in case even if DB is down the other services should not stop working.
So I found the temporary solution of my problem.
Wrote new method which is searching for JNDI config and creating jdbcTemplate and if it fails to do so, I am eating the exception.
public JdbcTemplate getJdbcTemplate() {
try {
Context initContext = new InitialContext();
myDataSource = (DataSource) initContext.lookup("java:/comp/env/DBNAME");
myjdbcTemplate = new org.springframework.jdbc.core.JdbcTemplate(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
return jdbcTemplate;
}
This method I am calling every time from my other DAOImpl methods like below
getJdbcTemplate().query("MySp", args, myBeanRowMapper);
Its working for now. But if you see any better approach, please answer here.
Thanks

How to verify whether a connection pool has beem set up in spring MVC web app?

In one of my question asked earlier I got to know that DriverManagerDataSource is NOT intended for production use. So I changed my configuration. I know I am using DBCP which is also outdated and a lot of other connection pools are available like HIkariCP and BOneCP but
I wish to understand the way how to verify that a pool has been setup
or not?
On searching a lot I got some answer at the following link
How would you test a Connection Pool
but I didn't get a way to verify programmatically. Also I cannot debug my jar files used for connection pooling because no source code is available. I dont know why but I can't change my jars for offical reasons.
The following are my configuration (OLD and NEW)
OLD
<bean id="webLogicXADataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="#[csa.db.driver]" />
<property name="url" value="#[csa.db.url]" />
<property name="username" value="#[csa.db.username]" />
<property name="password" value="#[csa.db.password]" />
</bean>
NEW
Using DBCP connection pool
<bean id="webLogicXADataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="#[csa.db.driver]" />
<property name="url" value="#[csa.db.url]" />
<property name="username" value="#[csa.db.username]" />
<property name="password" value="#[csa.db.password]" />
</bean>
OTHER ELEMENTS:(Thus far I have kept them same like they were earlier)
Place holder
<bean id="placeholderConfig"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:${DB_PROPERTIES}</value>
</list>
</property>
<property name="placeholderPrefix" value="#[" />
<property name="placeholderSuffix" value="]" />
</bean>
Transaction Manager
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="webLogicXADataSource" />
<qualifier value="inventoryTxManager"/>
</bean>
DAOIMPL SAMPLE BEAN
<bean id="inventoryDao"
class="com.lxnx.fab.ce.icce.inventoryRoutingInvoice.dao.InventoryDaoImpl">
<property name="dataSource" ref="webLogicXADataSource" />
<property name="transactionManager" ref="transactionManager" />
Right now all the DAO classes in my project are singleton(no prototype property set for any of the beans)
The following is the sample java code of the DAOImpl.java class where I need to do all the transactions:
DAOImpl.java
public class InventoryDaoImpl implements InventoryDao {
private final static ISmLog iSmLog = Instrumentation
.getSmLog(LNConstants.SYSTEM_LOG_NAME);
private JdbcTemplate jdbcTemplate;
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.dataSource = dataSource;
}
public void setTransactionManager(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
#Transactional private void insertRelatedInfoData(
InventoryModel inventoryModel) {
final List<String> relatedLniList = inventoryModel.getArrRelatedLni();
final String documentLni = inventoryModel.getDocumentLNI();
String sql = "INSERT INTO SCSMD_REPO.INV_RELATED_INFO(LNI, RELATED_LNI) VALUES(?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
String relatedLni = relatedLniList.get(i);
ps.setString(1, documentLni);
ps.setString(2, relatedLni);
}
#Override
public int getBatchSize() {
return relatedLniList.size();
}
});
}
}
I am not getting any errors. Just wanted to verify If a pool has been setup U wish to verify the same
Are all configurations fine or did I miss something?? Please help me out with you valuable answers. thanks
If you don't have logs enable then you can't verify it. however there is one more donkey logic.
every database server will have timeout functionality. if db not hit by application after some time connection will break. for example mysql server will break it's connection from application after 8 hour (if there is no hit from application). you check modify timeout to minimum time (say 30 min)in mysql config file and check after 30 minutes you get connection close exception in you appication, when you hit db
The easiest way, as explained, would be to examine the logs. It's quite likely that a connection pool will log something, at least if your logging level is low enough.
Another way would be to examine the class of the Connection that the datasource returns. If you're dealing with a connection pool, the class will be a wrapper or a proxy class for that pool. The wrapper/proxy class makes sure that when you call close() the connection isn't really closed, it's just returned to the pool for further use. For example if you were to use HikariCP as your pool, you could check if(connection instanceof IHikariConnectionProxy) to see if the pool is being used.
Adding that kind of code in your software would be a bad idea in practically all cases. If you don't know whether a connection pool is being used or not, it's not something you solve with code. It's something you solve by reading and studying more.
You've also named your bean webLogicXADataSource even though nothing seems to support it being an XA datasource. Are you perhaps working on things a bit too advanced for you?

Spring MVC 4 service #tTransactional doesn't work

1. Spring MVC application-context.xml
<tx:annotation-driven/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
<property name="url" value="jdbc:mysql://localhost:13306/test"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2. Service impl class
#Override
#Transactional
public void deleteCommentAndFiles(int commentId) {
int deletedCommentCount = commentDAO.deleteComment(commentId);
int deletedFileCount = fileDAO.deleteFiles(commentId);
if (deletedCommentCount != 1) {
throw new IncorrectUpdateSemanticsDataAccessException("Deleted comment not 1 [deletedCount : " + deletedCommentCount);
}
if (deletedFileCount != 1) {
throw new IncorrectUpdateSemanticsDataAccessException("Deleted file not 1 [deletedCount : " + deletedCommentCount);
}
}
3. Test Case
#Test
public void rollbackT() {
boolean hasException = false;
int sizeBeforDelete = commentDAO.selectCountByArticle(1);
try {
commentService.deleteCommentAndFiles(1);
} catch (RuntimeException e) {
hasException = true;
}
Assert.assertTrue(hasException);
Assert.assertEquals(sizeBeforDelete, commentDAO.selectCountByArticle(1));
}
in Test case
first Assert.assertTrue(hasException); is passed but
Assert.assertEquals(sizeBeforDelete, commentDAO.selectCountByArticle(1)) this case fail Expected : 1 but Actual : 0
this second TC fail mean Exception is occur but doesn't rollback delete comment
deleteCommentAndFiles method throw exception but doesn't rollback
Im trying to use #Transactional(propagation=Propagation.REQUIRED, rollbackFor={IncorrectUpdateSemanticsDataAccessException.class})
but same doesn't work
why #Transactional annotaion doesn't work?
I've also had the same issue. I moved the #transactional to the controller in order to works.
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services. See Section 21.2, “The DispatcherServlet” for more information.
The database has to support transactions. In case of MySQL you need to create a table with type of InnoDB, BDB or Genini.
Per default myisam does not support transactions.
I didn't see your main test class, but i assume you use the default spring configuration for junit :
By default, a test is launched on his own implicit transaction. when you call your service, it start a "sub" logical transaction (which is a part of the test transaction, because you didn't use require_new for the propagation). when this transaction failed, the main transaction is marked for rollback, but the rollback is not done until the main transaction has finished, when the test end
If you want to test a rollback, you shouldn't use this implicit transaction, launched by the test framework, or you can use TestTransaction.end() before your assertions to force this transaction to be committed or rollback.

How to set a global query timeout for hibernate / mysql? [duplicate]

I am doing some big queries on my database with Hibernate and I sometimes hit timeouts. I would like to avoid setting the timeout manually on every Query or Criteria.
Is there any property I can give to my Hibernate configuration that would set an acceptable default for all queries I run?
If not, how can I set a default timeout value on Hibernate queries?
JPA 2 defines the javax.persistence.query.timeout hint to specify default timeout in milliseconds. Hibernate 3.5 (currently still in beta) will support this hint.
See also https://hibernate.atlassian.net/browse/HHH-4662
JDBC has this mechanism named Query Timeout, you can invoke setQueryTime method of java.sql.Statement object to enable this setting.
Hibernate cannot do this in unified way.
If your application retrive JDBC connection vi java.sql.DataSource, the question can be resolved easily.
we can create a DateSourceWrapper to proxy Connnection which do setQueryTimeout for every Statement it created.
The example code is easy to read, I use some spring util classes to help this.
public class QueryTimeoutConfiguredDataSource extends DelegatingDataSource {
private int queryTimeout;
public QueryTimeoutConfiguredDataSource(DataSource dataSource) {
super(dataSource);
}
// override this method to proxy created connection
#Override
public Connection getConnection() throws SQLException {
return proxyWithQueryTimeout(super.getConnection());
}
// override this method to proxy created connection
#Override
public Connection getConnection(String username, String password) throws SQLException {
return proxyWithQueryTimeout(super.getConnection(username, password));
}
private Connection proxyWithQueryTimeout(final Connection connection) {
return proxy(connection, new InvocationHandler() {
//All the Statement instances are created here, we can do something
//If the return is instance of Statement object, we set query timeout to it
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(connection, args);
if (object instanceof Statement) {
((Statement) object).setQueryTimeout(queryTimeout);
}
return object;
});
}
private Connection proxy(Connection connection, InvocationHandler invocationHandler) {
return (Connection) Proxy.newProxyInstance(
connection.getClass().getClassLoader(),
ClassUtils.getAllInterfaces(connection),
invocationHandler);
}
public void setQueryTimeout(int queryTimeout) {
this.queryTimeout = queryTimeout;
}
}
Now we can use this QueryTimeoutConfiguredDataSource to wrapper your exists DataSource to set Query Timeout for every Statement transparently!
Spring config file:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<bean class="com.stackoverflow.QueryTimeoutConfiguredDataSource">
<constructor-arg ref="dataSource"/>
<property name="queryTimeout" value="1" />
</bean>
</property>
</bean>
Here are a few ways:
Use a factory or base class method to create all queries and set the timeout before returning the Query object
Create your own version of org.hibernate.loader.Loader and set the timeout in doQuery
Use AOP, e.g. Spring, to return a proxy for Session; add advice to it that wraps the createQuery method and sets the timeout on the Query object before returning it
Yes, you can do that.
As I explained in this article, all you need to do is to pass the JPA query hint as a global property:
<property
name="javax.persistence.query.timeout"
value="1000"
/>
Now, when executing a JPQL query that will timeout after 1 second:
List<Post> posts = entityManager
.createQuery(
"select p " +
"from Post p " +
"where function('1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(2) ) --',) is ''", Post.class)
.getResultList();
Hibernate will throw a query timeout exception:
SELECT p.id AS id1_0_,
p.title AS title2_0_
FROM post p
WHERE 1 >= ALL (
SELECT 1
FROM pg_locks, pg_sleep(2)
) --()=''
-- SQL Error: 0, SQLState: 57014
-- ERROR: canceling statement due to user request
For more details about setting a timeout interval for Hibernate queries, check out this article.
For setting global timeout values at query level - Add the below to config file.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
<property name="queryTimeout" value="60"></property>
</bean>
For setting global timeout values at transaction(INSERT/UPDATE) level - Add the below to config file.
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf" />
<property name="dataSource" ref="dataSource" />
<property name="defaultTimeout" value="60" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>

Categories

Resources