Why hibernate is saving values without transaction? - spring-boot

i'm using spring boot(2.1.4) with hibernate(5.3.9).
public class BaseDao{
#Autowired
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
#Autowired
private EntityManagerFactory entityManagerFactory;
#PersistenceContext
private EntityManager entityManager;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public EntityManager getEntityManager() {
return entityManager;
}
public Session getSession() throws Exception{
if(session == null) {
session = getSessionFactory().openSession();
}
if(transaction == null) {
transaction = session.beginTransaction();
}
return session;
}
public void commit() throws Exception{
if(transaction != null) {
transaction.commit();
transaction = null;
}
if(session != null) {
session.close();
session = null;
}
}
public void rollback() throws Exception{
if(transaction != null) {
transaction.rollback();
transaction = null;
}
if(session != null) {
session.close();
session = null;
}
}
protected void save(Object object) throws Exception {
getSessionFactory().openSession().save(object); //saves data in db
getSession().save(object); //is not saving data
}
getSessionFactory().openSession().save(object); this code is saving data to db even without commit
getSession().save(object); this code required commit to be called as txn is created but not commited
hibernate log
i see below log for both the line of code
insert
into
TEST_ENTITY
(CREATED_BY, CREATED_DATE, ENABLED, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, NAME)
values
(?, ?, ?, ?, ?, ?)
i have few questions on this behavior.
I know write operation will not happen without commit, so any idea what is wrong here or what causing commit in first scenario ?
Is it ok to use above code i.e. first scenario ?
If first approach is not right then do i need to create and commit txn for each object, any better approach so that even if i have to commit txn, i don't want to replicate the txn.commit() in every new method i write in BaseDao.java i.e. say i have create(), update(),delete() methods can i move this txn.commit() out of methods ?
Few places i'm using spring data jpa for fetching/saving record (given below), how txn is being handled in spring data jpa ? any references ?
#Repository
public interface TestEntitytRepo extends JpaRepository<TestEntity, Long> {
...
}
Please let me know if i missed any details to capture here.
Thanks in advance.

In hibernate, the Save() method stores an object into the database. It will Persist the given transient instance, first assigning a generated identifier. It returns the id of the entity created. When a session in hibernate is created using SessionFactory.openSession(), no transaction is created, so all the operations are executed outside of the transaction context !! In order to ensure the data gets saved into the database, a new transaction needs to be created.
I am a bit skeptical about the behaviors explained by you above. Seems like auto-commit option is enabled. If so, then this is not an issue as save() method is done, commit automatically happens backstage !!

Related

How to rollback child transaction if any exception in parent transaction?

I have two transaction manager for two database. I need to persist same data into both databases. If one transaction failed, other one need rollback. I have done like below
public interface DataService {
void saveData();
}
#Service
public class DataServiceImpl implements DataService {
#Autowired
private DataRepository dataRepository;
#Autowired
private OrDataRepository orDataRepository;
#Autowired
#Qualifier("orService")
private OrService orDataServiceImpl;
#Override
#Transactional(transactionManager = "transactionManager", rollbackFor = {RuntimeException.class})
public void saveData() {
Data data = new Data();
data.setCompKey(UUID.randomUUID().toString().substring(1,5));
data.setName("data");
dataRepository.save(data);
orDataServiceImpl.save();
//throw new RuntimeException("");
}
}
public interface OrService {
void save();
}
#Service("orService")
public class OrDataServiceImpl implements OrService {
#Autowired
private OrDataRepository orDataRepository;
#Override
#Transactional(rollbackFor = {RuntimeException.class})
public void save() {
OrData data = new OrData();
data.setCompKey(UUID.randomUUID().toString().substring(1,5));
data.setName("ordata");
orDataRepository.save(data);
}
}
I have two transaction manager (entityManager & orEntityManager) for two different DB.
If any exception in OrDataServiceImpl save method, data is not getting persisted in both DB. But if any exception in DataServiceImpl saveData method, data is getting persisted into OrData table.
I want to rollback the data from both DB if any exception.
chainedTransactionManager is deprecated. So can't use. atomikos and bitronix also can't use due to some restrictions. Kindly suggest better way to achieve distributed transation
The code need to be refactored, edit the DataServiceImpl.save() method.
Comment the orDataServiceImpl.save() line
public void saveData() {
Data data = new Data();
data.setCompKey(UUID.randomUUID().toString().substring(1,5));
data.setName("data");
dataRepository.save(data);
//orDataServiceImpl.save();
//throw new RuntimeException("");
}
Refactor/Edit the OrDataService Interface
public interface OrDataService {
void save(String uuid);
void delete(String uuid);
//will be use for compensating transaction
}
Update the OrDataServiceImpl class to implement above interface
Write new orchestration Method and use compensating transaction to rollback
pseudo code
call OrDataServiceImpl.save()
if step#1 was success
-> DataServiceImpl.saveData()
if Exception at step#3,
->OrDataServiceImpl.delete() [//to rollback]
else if, Exception at step#1
//do nothing

Calling Stored Procedure using Spring Data JPA

I want to know whether it is possible to call stored procedure using Spring Data JPA which is having resultset and multiple out parameter.
I found Git issue for same https://github.com/spring-projects/spring-data-examples/issues/80
If it is resolved, could someone provide one example with Spring Boot?
The way I've accomplished this in the past is to add custom behavior to a Spring Data JPA repository (link). Inside that I get the EntityManager and use java.sql.Connection and CallableStatement
Edit: Adding high level sample code. Sample makes the assumption that you are using Hibernate but idea should be applicable to others as well
Assuming you have an EntityRepository
public interface EntityRepositoryCustom {
Result storedProcCall(Input input);
}
public class EntityRepositoryImpl implements EntityRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public Result storedProcCall(Input input) {
final Result result = new Result();
Session session = getSession();
// instead of anonymous class you could move this out to a private static class that implement org.hibernate.jdbc.Work
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
CallableStatement cs = null;
try {
cs = connection.prepareCall("{call some_stored_proc(?, ?, ?, ?)}");
cs.setString(1, "");
cs.setString(2, "");
cs.registerOutParameter(3, Types.VARCHAR);
cs.registerOutParameter(4, Types.VARCHAR);
cs.execute();
// get value from output params and set fields on return object
result.setSomeField1(cs.getString(3));
result.setSomeField2(cs.getString(4));
cs.close();
} finally {
// close cs
}
}
});
return result;
}
private Session getSession() {
// get session from entitymanager. Assuming hibernate
return em.unwrap(org.hibernate.Session.class);
}
}

Save an Object after an error Occured in the current Transaction

Hi i have the following construction, which runs a raw query and then saves the result. But when an exception occoured on the raw queries, i cannot save my Object any more to indicate that an error happened
[c3p0] A PooledConnection that has already signalled a Connection error is still in use!
The classes:
#Service("myService")
#Transactional(propagation = Propagation.REQUIRED)
public class MyService {
cron(){
Job job = myDao.nextJob();
try {
myDao.myFunction(job);
job.setStatus(Status.COMPLETE);
} catch (Exception e) {
job.setStatus(Status.ERROR);
}
myDao.save(job);
}
}
#Repository("myDao")
public class MyDao extends HibernateDaoSupport {
#Autowired
public void setHibernateSessionFactory(#Qualifier("sessionFactory") SessionFactory sessionFactory) {
setSessionFactory(sessionFactory);
}
public void myFunction(final Job job) {
final Session session = getCurrentSession();
session.flush();
session.doWork(new Work() {
#Override
public void execute(Connection con) throws SQLException {
// do something which creates an SQL error
}
}
}
}
what can i do to get a new working session to get my object saved?
1) It looks like you are not releasing the connection to the connection pool after an error, and trying to reuse it.
2) Instead of final Session session = getCurrentSession() create a new session using openSession() as below:
SessionFactory sessionFactory = HibernateUtil.getSessionAnnotationFactory();
final Session session = sessionFactory.openSession();
Try to create new session for each transaction, if you use getCurrentSession and any error occurs or not in the transaction, then the maintainence(closing/flushing etc) of the session object is done by hibernate and not by the programmer. Make use of getCurrentSession for single threaded environment.
3) By default a Connection object is in auto-commit mode, which means that it automatically commits changes after executing each statement. Check whether have you altered its property? Use Connection.commit() to commit the connection.

Spring #Transational no rollback for jdbctemplate.update(PreparedStatementCreator, KeyHolder)

I'm newbie with spring framework. I used spring.xml to define the datasouce and DataSourceTransactionManager, so that I could insert data with jdbctemplate object.
And now I want to add the rollback to the transaction.
Unfortunately this rollback only works for JdbcTemplate.updata (String SQL), not for JdbcTemplate.update(PreparedStatementCreator, Keyholder), which I used to get the generated ID by insert.
#Override
#Transactional("txManagerTest")
public SQLQueryObjectIF process(SQLQueryObjectIF queryObj) {
KeyHolder keyHolder = new GeneratedKeyHolder();
for (final String query : queryObj.getQueries()) {
System.out.println(query);
// Rollback works fine for the "update" below.
//jdbcTemplate.update(query);
// Rollback doesn't work for the "update" below. Don't why...
jdbcTemplate.update(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
jdbcTemplate.getDataSource().getConnection().setAutoCommit(false);
PreparedStatement ps = jdbcTemplate.getDataSource().getConnection().prepareStatement(query,Statement.RETURN_GENERATED_KEYS);
return ps;
}
}, keyHolder);
//log.info(keyHolder.getKeys().toString());
}
//just for rollback test
if (keyHolder.toString().length()>-1){
throw new RuntimeException("Test Error");
}
return queryObj;
}
That code should be used like this (you need to use the connection given as parameter), otherwise with your code you will get a connection that Spring doesn't know about, by directly accessing the DataSource instance (if Spring doesn't know about it, it will not know to rollback in case of exception):
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(query,Statement.RETURN_GENERATED_KEYS);
return ps;
}

TransactionTemplate vs JdbcTemplate

The Spring framework provides two means of programmatic transaction management:
Using the TransactionTemplate.
Using a PlatformTransactionManager implementation directly.
The above is described here: http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html
The Spring site hasnot mentioned JdbcTemplate here. As per my understanding JdbcTemplate also manages the transaction internally and this is all done in programme too.
So what's the basic difference between TransactionTemplate and JdbcTemplate?
JdbcTemplate is not a transaction manager. It's merely a helper class for native JDBC operations:
This is the central class in the JDBC core package. It simplifies the use of JDBC and helps to avoid common errors. It executes core JDBC workflow, leaving application code to provide SQL and extract results. This class executes SQL queries or updates, initiating iteration over ResultSets and catching JDBC exceptions and translating them to the generic, more informative exception hierarchy defined in the org.springframework.dao package.
TransactionTemplate by the way is also not a transaction manager, it's a
Template class that simplifies programmatic transaction demarcation and transaction exception handling.
The PlatformTransactionManager (and other subclasses of AbstractPlatformTransactionManager) is a transaction manager, as in it
determines if there is an existing transaction;
applies the appropriate propagation behavior;
suspends and resumes transactions if necessary;
checks the rollback-only flag on commit;
applies the appropriate modification on rollback (actual rollback or setting rollback-only);
triggers registered synchronization callbacks (if transaction synchronization is active).
So this class is responsible for the actual transaction handling, as opposed to the TransactionTemplate, which is to be used if you instead of declarative transaction handling you want to implement it programmetically. (see this blog, though quite outdated, you will see the difference between declarative and manual)
Quotes from Spring 3 Reference.
Note: Throughout the Spring Framework you will find other *Template classes as well: HibernateTemplate, JmsTemplate etc. They all follow the same pattern: template classes which radically reduce the amount of code you need to write, because all the so-called boilerplate code will be handled by them. Example (from here):
Without JdbcTemplate:
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insert(Customer customer){
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, customer.getCustId());
ps.setString(2, customer.getName());
ps.setInt(3, customer.getAge());
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
}
And with JdbcTemplate:
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insert(Customer customer){
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update(sql, new Object[] { customer.getCustId(),
customer.getName(),customer.getAge()
});
}

Resources