I want insert multiple rows in db table.
I am using SpringJdbc.
How can i manage transaction in SpringJdbc connection.
My code is:
#Override
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public int addUserRelationshipMapping(final ArrayList<UserDO> userDOs, final long userId) throws UserDataException {
final JdbcTemplate jd = this.getJdbctemplate();
try {
jd.getDataSource().getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
int totalAdded = 0;
try {
int[] isAdded = jd.batchUpdate(ADD_USER_RELATION_MAPPING, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
final long userRelationId = jd.queryForObject(USER_RELATION_KEY, Long.class);
UserDO userDO = userDOs.get(i);
ps.setLong(1, userRelationId);
ps.setLong(2, userId);
ps.setLong(3, userDO.getprimaryUserId());
ps.setInt(4, 1);
ps.setInt(5, 0);
jd.getDataSource().getConnection().commit();
}
#Override
public int getBatchSize() {
return userDOs.size();
}
});
totalAdded = isAdded.length;
} catch (DuplicateKeyException dExp) {
log.info("error for duplicate key exception ",dExp);
log.error(dExp);
} catch (DataAccessException dExp) {
throw new UserDataException("error while adding user relation for userId is" + userId, dExp);
}
return totalAdded;
}
In this code userRelationId return always old values not updated table values.
So will use database connection commit.
SOF question:Java MYSQL/JDBC query is returning stale data from cached Connection
I got error message:
Caused by: java.sql.SQLException: Can't call commit when autocommit=true
So i need help for this.
Advance thanks.
See the example of how to set auto commit false
<bean id="database" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
...
<property name="defaultAutoCommit" value="false" />
...
</bean>
</property>
ref:
spring 3.1: jdbcTemplate auto commit to false.
You can also try the annotation based connection transaction management ?
How can I config to turn off autocommit in Spring + JDBC?
Related
Preamble - using Spring
I am confused as to the purpose of the spring #Transactional annotation. I thought from a few blog posts I've read that it would allow me to simplify transaction management and just write this, and it would handle connection/commit/rollback automagically:
public class DaoImpl implements Dao {
#Autowired
private SessionFactory sessionFactory;
#Transactional
public void saveOrUpdateOne(final AdditionalDataItem item) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(p_item);
}
}
However this gives me an exception: " Calling method 'saveOrUpdate' is not valid without an active transaction"
If I instead change the save method to this, it all works - so my question is, what is #Transactional doing?
#Override
#Transactional
public void saveOrUpdateOne(final AdditionalDataItem p_item) {
Session session = null;
Transaction trans = null;
try {
session = sessionFactory.getCurrentSession();
trans = session.beginTransaction();
TransactionStatus status = trans.getStatus();
session.saveOrUpdate(p_item);
trans.commit();
} catch (Exception e) {
LOGGER.error("Exception saving data: {}", e.getMessage());
if (trans != null) {
try {
trans.rollback();
} catch (RuntimeException rbe) {
LOGGER.error("Couldn’t roll back transaction", rbe);
}
}
} finally {
if (session != null && session.isOpen()) {
try {
session.close();
} catch (HibernateException ne) {
LOGGER.error("Couldn’t close session", ne);
}
}
}
}
For reference, I'm using Java 11 with Spring Framework 5.3.7 and hibernate 5.5.7 and have appropriate dao, session factory and tx manager beans:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="${sessionFactory.datasource}" />
<property name="configLocation" value="${sessionFactory.configLocation}" />
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="Dao" class="com.xxxxx.dao.DaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
It is because you do not enable #Transactional yet and also not begin the transaction when calling session.saveOrUpdate(), so it gives you the error 'not valid without an active transaction' .
To enable #Transactional, you have to use #EnableTransactionManagement or add <tx:annotation-driven/> in case you are using XML configuration . It basically does the following for you (source) :
#EnableTransactionManagement and <tx:annotation-driven/> are
responsible for registering the necessary Spring components that power
annotation-driven transaction management, such as the
TransactionInterceptor and the proxy- or AspectJ-based advice that
weaves the interceptor into the call stack when JdbcFooRepository's
#Transactional methods are invoked.
Your working example works because you manually manage the transaction by yourself .It is nothing to do with #Transactional since you never enable it.
Take the working codes as an example , what #Transactional does for you is that you no longer need to manually write the following transaction codes as all of them will be encapsulated in the TransactionInterceptor and execute around your #Transactional method based on AOP :
public Object invoke() {
Session session = null;
Transaction trans = null;
try {
session = sessionFactory.getCurrentSession();
trans = session.beginTransaction();
TransactionStatus status = trans.getStatus();
/***********************************************/
Here it will invoke your #Transactional Method
/************************************************/
trans.commit();
} catch (Exception e) {
LOGGER.error("Exception saving data: {}", e.getMessage());
if (trans != null) {
try {
trans.rollback();
} catch (RuntimeException rbe) {
LOGGER.error("Couldn’t roll back transaction", rbe);
}
}
} finally {
if (session != null && session.isOpen()) {
try {
session.close();
} catch (HibernateException ne) {
LOGGER.error("Couldn’t close session", ne);
}
}
}
}
So you can see that your #Transactional method will become very clean after removing these "ceremony" codes.
#Transactional is used if you want to update 2 tables if one of them failed the other one will rollback automatic
you can use it above method
and you can call save or update using bean of a Repository class
I want to insert two different table consecutively in a service class , i am using Spring JDBCTemplate.My code are.
#Transactional(rollbackFor=Exception.class,propagation= Propagation.REQUIRES_NEW)
public boolean insertRestorent(User user) throws GenericException {
logger.println(IMessage.INFO, new StringBuilder(CLASS_NAME).append("::insertRestorent() restorent registration start"));
user.setUserType(USER_TYPE.RESTORENT.ID);
User checkUser = userDao.checkUserByEmail(user.getEmail());
if (checkUser != null) {
GenericException exception = new GenericException();
exception.setMessage("Shop already registered!!");
throw exception;
}
long getuserUid=userDao.insertUser(user);
long retoId= restorentdao.insertRestorent(user, getuserUid);
return false;
}
My transactionManager code is
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" />
</bean>
My problem is when userDao.insertUser(user); is successful but if there is any exception in restorentdao.insertRestorent(user, getuserUid); then fist user table is not rollback.
Exceptions are.
SEVERE: Servlet.service() for servlet [MyDelivery] in context with path [/MyDelivery] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL []; Field 'shop_description' doesn't have a default value; nested exception is java.sql.SQLException: Field 'shop_description' doesn't have a default value] with root cause
java.sql.SQLException: Field 'shop_description' doesn't have a default value
userDao insert method is.
public long insertUser(User user) {
logger.println(IMessage.INFO, new StringBuilder(CLASS_NAME).append("::insertUser()"));
final String query = "INSERT INTO user(user_type,email,mobile_no,password,name,status,created_on)VALUES(?,?,?,?,?,?,now());";
KeyHolder keyHolder = new GeneratedKeyHolder();
getJdbcTemplate().update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(query, PreparedStatement.RETURN_GENERATED_KEYS);
int i = 1;
ps.setLong(i++, user.getUserType());
ps.setString(i++, user.getEmail());
ps.setString(i++, user.getMobileNo());
ps.setString(i++, user.getPassword());
if (!StringUtils.isEmpty(user.getName())) {
ps.setString(i++, user.getName());
} else {
ps.setString(i++, null);
}
ps.setInt(i++, STATUS.INACTIVE.ID);
return ps;
}
}, keyHolder);
user.setUserId(keyHolder.getKey().longValue());
return keyHolder.getKey().longValue();
}
I am inserting records in a couple of tables namely Dept and Emp. If the Dept table is successfully created then only I want to insert records in Emp table. Also, if any of the insert in Emp fails, then I want to rollback all the transaction which includes both rollback from Emp as well as Dept tables.
I tried this using Propagation.REQUIRED as shown below:
Java File
public void saveEmployee(Employee empl){
try {
jdbcTemplate.update("INSERT INTO EMP VALUES(?,?,?,?,?)",empl.getEmpId(),empl.getEmpName(),
empl.getDeptId(),empl.getAge(),empl.getSex());
} catch (DataAccessException e) {
e.printStackTrace();
}
}
#Transactional(propagation=Propagation.REQUIRED)
public void saveRecords(){
saveDepartment(dept);
saveEmployee(empl);
}
context.xml
<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>
Problem:
Even if an insertion in Emp table fails, the Dept insertion is getting persisted which I don't want. I want to rollback everything.
Please suggest.
The problem is your catch block. Since the exception is catched the tx don't rollback.
You must throw the exception :
public void saveEmployee(Employee empl){
try {
jdbcTemplate.update("INSERT INTO EMP VALUES(?,?,?,?,?)",empl.getEmpId(),empl.getEmpName(),
empl.getDeptId(),empl.getAge(),empl.getSex());
} catch (DataAccessException e) {
e.printStackTrace();
throw e;
}
}
And by the way, the semantic of Progation.Required just means : create a new tx if it don't exists OR use an existing one if there is tx running.
Following your comment here is a suggestion to see the effect of NEW tx :
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void saveEmployee(Employee empl){
try {
jdbcTemplate.update("INSERT INTO EMP VALUES(?,?,?,?,?)",empl.getEmpId(),empl.getEmpName(),
empl.getDeptId(),empl.getAge(),empl.getSex());
} catch (DataAccessException e) {
e.printStackTrace();
throw e;
}
}
#Transactional(propagation=Propagation.REQUIRED)
public void saveRecords(){
saveDepartment(dept);
try{
saveEmployee(empl);
}catch(Exception e){Logger.log("Fail to save emp !");}
}
The key point to see the effect of REQUIRES_NEW is to catch the exception around saveEmployee. If you don't catch it : the exception will propagate in the other tx (the one started when entering saveRecords() ) and it will rollback too.
I've been reading about transactions & jooq but I struggle to see how to implement it in practice.
Let's say I provide JOOQ with a custom ConnectionProvider which happens to use a connection pool with autocommit set to false.
The implementation is roughly:
#Override public Connection acquire() throws DataAccessException {
return pool.getConnection();
}
#Override public void release(Connection connection) throws DataAccessException {
connection.commit();
connection.close();
}
How would I go about wrapping two jooq queries into a single transaction?
It is easy with the DefaultConnectionProvider because there's only one connection - but with a pool I'm not sure how to go about it.
jOOQ 3.4 Transaction API
With jOOQ 3.4, a transaction API has been added to abstract over JDBC, Spring, or JTA transaction managers. This API can be used with Java 8 as such:
DSL.using(configuration)
.transaction(ctx -> {
DSL.using(ctx)
.update(TABLE)
.set(TABLE.COL, newValue)
.where(...)
.execute();
});
Or with pre-Java 8 syntax
DSL.using(configuration)
.transaction(new TransactionRunnable() {
#Override
public void run(Configuration ctx) {
DSL.using(ctx)
.update(TABLE)
.set(TABLE.COL, newValue)
.where(...)
.execute();
}
});
The idea is that the lambda expression (or anonymous class) form the transactional code, which:
Commits upon normal completion
Rolls back upon exception
The org.jooq.TransactionProvider SPI can be used to override the default behaviour, which implements nestable transactions via JDBC using Savepoints.
A Spring example
The current documentation shows an example when using Spring for transaction handling:
http://www.jooq.org/doc/latest/manual/getting-started/tutorials/jooq-with-spring/
This example essentially boils down to using a Spring TransactionAwareDataSourceProxy
<!-- Using Apache DBCP as a connection pooling library.
Replace this with your preferred DataSource implementation -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
init-method="createDataSource" destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/maven-test" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<!-- Using Spring JDBC for transaction management -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transactionAwareDataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSource" />
</bean>
<!-- Bridging Spring JDBC data sources to jOOQ's ConnectionProvider -->
<bean class="org.jooq.impl.DataSourceConnectionProvider"
name="connectionProvider">
<constructor-arg ref="transactionAwareDataSource" />
</bean>
A running example is available from GitHub here:
https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-spring-example
A Spring and Guice example
Although I personally wouldn't recommend it, some users have had success replacing a part of Spring's DI by Guice and handle transactions with Guice. There is also an integration-tested running example on GitHub for this use-case:
https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-spring-guice-example
This is probably not the best way but it seems to work. The caveat is that it is not the release but the commit method which closes the connection and returns it to the pool, which is quite confusing and could lead to issues if some code "forgets" to commit...
So the client code looks like:
final PostgresConnectionProvider postgres =
new PostgresConnectionProvider("localhost", 5432, params.getDbName(), params.getUser(), params.getPass())
private static DSLContext sql = DSL.using(postgres, SQLDialect.POSTGRES, settings);
//execute some statements here
sql.execute(...);
//and don't forget to commit or the connection will not be returned to the pool
PostgresConnectionProvider p = (PostgresConnectionProvider) sql.configuration().connectionProvider();
p.commit();
And the ConnectionProvider:
public class PostgresConnectionProvider implements ConnectionProvider {
private static final Logger LOG = LoggerFactory.getLogger(PostgresConnectionProvider.class);
private final ThreadLocal<Connection> connections = new ThreadLocal<>();
private final BoneCP pool;
public PostgresConnectionProvider(String serverName, int port, String schema, String user, String password) throws SQLException {
this.pool = new ConnectionPool(getConnectionString(serverName, port, schema), user, password).pool;
}
private String getConnectionString(String serverName, int port, String schema) {
return "jdbc:postgresql://" + serverName + ":" + port + "/" + schema;
}
public void close() {
pool.shutdown();
}
public void commit() {
LOG.debug("Committing transaction in {}", Thread.currentThread());
try {
Connection connection = connections.get();
if (connection != null) {
connection.commit();
connection.close();
connections.set(null);
}
} catch (SQLException ex) {
throw new DataAccessException("Could not commit transaction in postgres pool", ex);
}
}
#Override
public Connection acquire() throws DataAccessException {
LOG.debug("Acquiring connection in {}", Thread.currentThread());
try {
Connection connection = connections.get();
if (connection == null) {
connection = pool.getConnection();
connection.setAutoCommit(false);
connections.set(connection);
}
return connection;
} catch (SQLException ex) {
throw new DataAccessException("Can't acquire connection from postgres pool", ex);
}
}
#Override
//no-op => the connection won't be released until it is commited
public void release(Connection connection) throws DataAccessException {
LOG.debug("Releasing connection in {}", Thread.currentThread());
}
}
Easiest way,(I have found) to use Spring Transactions with jOOQ, is given here: http://blog.liftoffllc.in/2014/06/jooq-and-transactions.html
Basically we implement a ConnectionProvider that uses org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(ds) method to find and return the DB connection that holds transaction created by Spring.
Create a TransactionManager bean for your DataSource, example shown below:
<bean
id="dataSource"
class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="mysql://locahost:3306/db_name"
p:username="root"
p:password="root"
p:initialSize="2"
p:maxActive="10"
p:maxIdle="5"
p:minIdle="2"
p:testOnBorrow="true"
p:validationQuery="/* ping */ SELECT 1"
/>
<!-- Configure the PlatformTransactionManager bean -->
<bean
id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
<!-- Scan for the Transactional annotation -->
<tx:annotation-driven/>
Now you can annotate all the classes or methods which uses jOOQ's DSLContext with
#Transactional(rollbackFor = Exception.class)
And while creating the DSLContext object jOOQ will make use of the transaction created by Spring.
Though its an old question, Please look at this link to help configure JOOQ to use spring provided transaction manager. Your datasource and DSLContext have to be aware of Transacation.
https://www.baeldung.com/jooq-with-spring
You may have to change
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
to
#Bean
public DSLContext dsl() {
return new DefaultDSLContext(configuration());
}
I am using Hibernate in my application,
My connection code is as follows,
public class DBConnection{
private static SessionFactory factory;
private static ServiceRegistry serviceRegistry;
public DBConnection()
{
try
{
factory = new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public Session getSession()
{
return factory.openSession();
}
// Call this during shutdown
public static void close() {
factory.close();
serviceRegistry=null;
factory=null;
}
}
My Hibernate config file is ,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume test is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3307/Test
</property>
<property name="hibernate.connection.username">
root
</property>
<property name="hibernate.connection.password">
root
</property>
<property name=" hibernate.dbcp.max Wait">100</property>
</session-factory>
</hibernate-configuration>
The sample code for this process is
public String showurl(int id)
{
int i = 1;
String url= "";
Transaction tx= null;
Session session = null;
try
{
session = new DBConnection().getSession();
tx = session.beginTransaction();
Query query = session.createQuery(" FROM Test WHERE ID = :id");
query.setInteger("id", id);
List<Test> testlist= query.list();
for(Test nl:testlist)
{
url= nl.geturl();
}
tx.commit();
session.close();
DBConnection.close();
}catch(Exception ex)
{
if(tx!=null) tx.rollback();
session.close();
DBConnection.close();
ex.printStackTrace();
}
finally
{
if(tx!=null)
{
tx=null;
}
if(session.isOpen())
{
session.close();
}
if(session!=null)
{
session= null;
}
}
return url;
}
My problem here is , due to lot of sleep queries in the Database leads to slow down the process. How to solve this in my Application. I have more than 50 users using the application at the same time. how to solve this problem?
While Using this I am getting the following error frequently,
SEVERE: Servlet.service() for servlet [jsp] in context with path [] threw exception [org.hibernate.service.UnknownServiceException: Unknown service requested [org.hibernate.stat.spi.StatisticsImplementor]] with root cause
org.hibernate.service.UnknownServiceException: Unknown service requested [org.hibernate.stat.spi.StatisticsImplementor]
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:126)
at org.hibernate.internal.SessionFactoryImpl.getStatisticsImplementor(SessionFactoryImpl.java:1468)
at org.hibernate.internal.SessionFactoryImpl.getStatistics(SessionFactoryImpl.java:1464)
at org.hibernate.internal.SessionImpl.close(SessionImpl.java:346)
Any suggestion is highly appreciated.
Cheers!!
I would start from using other connection pool manager than Hibernate's build-in one - It it not design for production use. Try to use C3P0. Here is little hint how to configure it for hibernate. More informations can be found here
EDIT:
I have just noticed, that you are creating new session factory everytime you want to operate od db. That is SO WRONG! You should have only ONE instance of session factory, as service or application scope static field, and later use that for creating sessions as needed.
public static SessionFactory getSessionFactory(){
if (factory == null) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
}
return factory;
}
public static Session getSession() {
return getSessionFactory().openSession();
}
from code use getSession() directly.