EntityManager persist() not saving anything to database - java

My entityManager persist() gets id from sequence and puts it to my Image object but the Image object itself is not showing up in the database. EntityManager.flush() gives an error so I can't commit this way. Here is my code.
#Repository
public class ImageDaoImpl extends BaseDao implements ImageDao {
#PersistenceContext
protected EntityManager entityManager;
#Override
#Transactional
public void create(Image image) {
JpaTemplate jpaTemplate = getJpaTemplate(entityManager);
jpaTemplate.persist(image);
}
#Repository
public class BaseDao {
private JpaTemplate jpaTemplate;
public JpaTemplate getJpaTemplate(EntityManager entityManager){
if(jpaTemplate == null)
jpaTemplate = new JpaTemplate(entityManager);
return jpaTemplate;
}
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
</bean>
</property>
<property name="persistenceUnitName" value="sample"></property>
</bean>
<!-- DataSource Setup -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/imageCapture" />
<property name="username" value="myusername" />
<property name="password" value="mypassword" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

This generally happens when Transaction in not applied.. I doubt #Transactional interceptor is not intercepting properly.

persist() means "add object to list of managed entries". To save object to data base you must call flush() method. But remember you must call in inside the transaction.
//Edit:
Example save method.
public void save(T t){
// begin transaction
em.getTransaction().begin();
if (!em.contains(t)) {
// persist object - add to entity manager
em.persist(t);
// flush em - save to DB
em.flush();
}
// commit transaction at all
em.getTransaction().commit();
}
This is not the best that you can make, but good enough.

Check your server logs. Are you creating new EntityManger? and have not begun the transaction. I think, Where you have begun that is another EntityManager object.

Check EntityManager object is not getting created twice. In my case I was initialize EntityManager object in one method and mistakenly calling that method twice during opening transaction and persisting data.
Incorrect code:
EntityTrasaction transaction = getEntityManager().getTransaction().begin();
getEntityManager().persist(customer); // This line is calling another object of EntityManager
transaction.commit();
Correct code:
EntityManager em = getEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(customer);
transaction.commit();
public EntityManager getEntityManager(){
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("punit");
return entityManagerFactory.createEntityManager();
}

Check your mvc-dispatcher-servlet.xml . Here in <context:component-scan base-package="pass"/> pass should equal to package where your controllers are

I faced this problem in when running test cases with SpringJUnit4ClassRunner
I solved it by
wrapping the test function with
#Autowired
private PlatformTransactionManager transactionManager;
// in your test funciton
// Declare a transaction programmatically to be able to rollback.
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
// here test of Dao goes
} finally {
transactionManager.rollback(transaction);
}
hope this helps you

Related

Why is my transaction rolled back unexpectedly?

In our project, we are trying to get code that is currently deployed on WebSphere working on Liberty instead.
After receiving a message an MDB calls a method in a service which inserts the data into a DB2 database.
The problem is that when flush is called, we get the following errors in the log:
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1136) ~[hibernate-entitymanager-5.1.3.Final.jar:5.1.3.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1297) ~[hibernate-entitymanager-5.1.3.Final.jar:5.1.3.Final]
[ERROR ] WLTC0017E: Resources rolled back due to setRollbackOnly() being called.
There are no other errors, like SQL query not being correct or something like that.
Since this code is working fine in WebSphere, I'm wondering if there is something wrong in the configuration or if Liberty does something different in handling transactions.
Below are the relevant pieces of code and configuration:
#Service
public class IdentificationService implements IIdentificationService {
#Autowired
private IIdentificationDAO identificationDAO;
#Transactional
public Identification insertIdentificationData(RequestForIdentificationRequest request, String referenceNumber) {
final Identification identification = new Identification();
...
identificationDAO.create(identification);
LOGGER.debug("Identification record saved with id: {}", identification.getId());
return identification;
}
}
#Repository
public class IdentificationDAO extends BaseDAO implements IIdentificationDAO {
#Override
public void create(final Identification identification) {
insertRecord(identification);
}
}
public abstract class BaseDAO
{
#PersistenceContext(name = "UserData", unitName = "UserData")
private EntityManager em;
/**
* #param record
*/
public <T> void insertRecord(final T record)
{
em.persist(record);
em.flush();
}
}
public class Identification implements Serializable {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(sequenceName = "IDENTIFICATION_SEQ", name = "sequenceGenerator", allocationSize = 1)
private Long id;
...
}
applicationContext.xml
<jee:jndi-lookup id="userDataSource" jndi-name="jdbc/userdataDB" />
<bean id="userDataEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="UserData" />
<property name="persistenceXmlLocation" value="classpath:persistence-userdata.xml" />
<property name="dataSource" ref="userDataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.DB2Dialect" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="userDataEntityManagerFactory" />
</bean>
server.xml
<library id="DB2JCCLib">
<fileset dir="/etc/liberty/jdbc" includes="db2jcc4.jar, db2jcc_license_cisuz.jar, db2jcc_license_cu.jar"/>
</library>
<dataSource id="UserDB" jndiName="jdbc/userdataDB" type="javax.sql.XADataSource"
isolationLevel="TRANSACTION_READ_COMMITTED">
<jdbcDriver libraryRef="DB2JCCLib" javax.sql.XADataSource="com.ibm.db2.jcc.DB2XADataSource"/>
<properties.db2.jcc serverName="${datasource.userdb.serverName}" portNumber="${datasource.userdb.portNumber}"
databaseName="${datasource.userdb.databaseName}"
user="user" password="password"/>
</dataSource>
UPDATE: The problem turned out to be multiple transaction manager beans (named the same) in the configuration pointing at different databases.
Giving them unique names and referring to them explicitly in the Transactional annotation did the trick

Rollback transaction in functional tests started in another spring context

'm implementing functional tests. To avoid data corruption I need to rollback every test after execution. This is pretty trivial task - just mark rollback=true in test. But if I start another module with another spring context and first module interacts with it somehow (f.e. sends jms message and second one saves it to the same DB) then rollback is not working for second context. How to rollback second module too?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:/my-service-context.xml")
#Transactional(value = "myTransactionManager")
public class ParserServiceTest {
protected Logger l = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier(value = "earMessageDaoBean")
EARMessageDao dao;
#Autowired
#Qualifier(value = "myParserService")
ParserService service;
#Test
#Rollback(value = true)
public void testExecute() throws Exception {
service.execute("fff", "ttt");
EARMessage byId = dao.findById(1L);
assertNotNull(byId);
assertEquals("fff", byId.getFrom());
assertEquals("ttt", byId.getTo());
l.info("{}", byId);
}
}
if I take a look into db i will see no data and it's good
but if I will add another module
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:/my-service-context.xml")
#Transactional(value = "myTransactionManager")
public class ParserServiceTest {
protected Logger l = LoggerFactory.getLogger(getClass());
ClassPathXmlApplicationContext context2;
#Before
public void setUp() throws Exception {
context2 = new ClassPathXmlApplicationContext(
"my-service-context2.xml"
);
}
#Autowired
#Qualifier(value = "earMessageDaoBean")
EARMessageDao dao;
#Autowired
#Qualifier(value = "myParserService")
ParserService service;
#Test
#Rollback(value = true)
public void testExecute() throws Exception {
service.execute("fff", "ttt");
EARMessage byId = dao.findById(1L);
assertNotNull(byId);
assertEquals("fff", byId.getFrom());
assertEquals("ttt", byId.getTo());
l.info("{}", byId);
// theoretically there could be interaction with service2 via JMS
MyParserService2 service2 = (MyParserService2) context2.getBean("myParserService2");
service2.execute("FFF", "TTT");
}
}
data added by servie2 will not be rollbacked.
I can get EntityManager TransactionManager within test context but I can't roll them back since transactions are commited already.
I can add some marker into second module and mark it in test somehow but have now idea yet what to do
UPDATE
here are services and context configs for better understanding:
#Service(value = "myParserService")
#Transactional(value = "myTransactionManager")
public class ParserService {
protected Logger l = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier(value = "earMessageDaoBean")
EARMessageDao dao;
public void execute(String from, String to) {
l.info("-------started service 1---------");
EARMessage message = new EARMessage();
message.setFrom(from);
message.setTo(to);
message.setProcessingDate(new DateTime());
dao.persist(message);
l.info("-------ended service 1---------");
}
}
#Service(value = "myParserService2")
#Transactional(value = "myTransactionManager")
public class MyParserService2 {
protected Logger l = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier(value = "earMessageDaoBean")
EARMessageDao dao;
public void execute(String from, String to) {
l.info("-------started service 2---------");
EARMessage message = new EARMessage();
message.setFrom("666" + from);
message.setTo("666" + to);
message.setProcessingDate(new DateTime());
dao.persist(message);
l.info("-------ended service 2---------");
}
}
my-service-context.xml
<context:annotation-config/>
<context:component-scan base-package="com.dimas.tutorial.hibernate.simple"/>
<import resource="classpath:/my-service-config.xml"/>
<import resource="classpath:/my-data-source.xml"/>
<import resource="classpath:/my-entity-manager.xml"/>
<jdbc:initialize-database data-source="${dataSource.name}">
<jdbc:script location="classpath:/sql/my-schema.sql"/>
</jdbc:initialize-database>
my-service-context2.xml
<context:annotation-config/>
<context:component-scan base-package="com.dimas.tutorial.hibernate.simple"/>
<import resource="classpath:/my-service-config.xml"/>
<import resource="classpath:/my-data-source.xml"/>
<import resource="classpath:/my-entity-manager.xml"/>
<jdbc:initialize-database data-source="${dataSource.name}">
<jdbc:script location="classpath:/sql/my-schema.sql"/>
</jdbc:initialize-database>
UPDATE2: added entity manager config
<bean id="valettaEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="MyPersistence"/>
<property name="packagesToScan" value="com.dimas.tutorial.hibernate.simple.domain"/>
<property name="dataSource" ref="${dataSource.name}"/>
<property name="jpaVendorAdapter" ref="hibernateVendor"/>
<property name="jpaPropertyMap" ref="jpaPropertyMap"/>
</bean>
<util:map id="jpaPropertyMap">
<entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<entry key="hibernate.default_schema" value="public"/>
<entry key="hibernate.show_sql" value="false"/>
<entry key="hibernate.format_sql" value="false"/>
<entry key="hibernate.cache.use_second_level_cache" value="false"/>
<entry key="hibernate.max_fetch_depth" value="3"/>
<entry key="hibernate.jdbc.fetch_size" value="50"/>
<entry key="hibernate.jdbc.batch_size" value="10"/>
</util:map>
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false"/>
</bean>
<bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="valettaEntityManagerFactory"/>
</bean>
<bean id="valettaTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="myTransactionManager"/>
</bean>
<context:annotation-config/>
<tx:annotation-driven transaction-manager="myTransactionManager"/>
Use propagation required for your service.
eg .
First Transaction:
#Transactional(value = "myTransactionManager")
public class ParserServiceTest {
Join by require Transaction:
#Transactional(value = "myTransactionManager", propagation=Propagation.REQUIRED)
public class ParserService {
Join by require Transaction:
#Transactional(value = "myTransactionManager", propagation=Propagation.REQUIRED)
public class MyParserService2 {
PROPAGATION_REQUIRED:
Spring REQUIRED behavior means that the same transaction will be used if there is an already opened transaction in the current bean method execution context. Create a new one if none exists.
In short this means that if an inner(2nd Transaction) method causes a transaction to rollback, the outer(1st Transaction) method will fail to commit and will also rollback the transaction.

Spring JPA Hibernate handle large databases

I am new to JPA, Hibernate as well as Spring. Currently I am creating a spring web service which work with a database with a large number of tables. To access those tables I have created separate class annotating #Entity. Then I created a generic DAO class as all my entities need similar operations.
#Transactional
public class GenericJpaDao<T, ID extends Serializable> {
private Class<T> persistentClass;
private EntityManager entityManager;
public GenericJpaDao(Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
protected EntityManager getEntityManager() {
return entityManager;
}
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public Class<T> getPersistentClass() {
return persistentClass;
}
#Transactional(readOnly = true)
public T findById(ID id) {
T entity = (T) getEntityManager().find(getPersistentClass(), id);
return entity;
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true)
public List<T> findAll() {
return getEntityManager().createQuery("select x from " + getPersistentClass().getSimpleName() + " x").getResultList();
}
public T save(T entity) {
getEntityManager().persist(entity);
return entity;
}
public T update(T entity) {
T mergedEntity = getEntityManager().merge(entity);
return mergedEntity;
}
public void delete(T entity) {
entity = getEntityManager().merge(entity);
getEntityManager().remove(entity);
}
public void flush() {
getEntityManager().flush();
}
}
Now I tried to instantiate this GenericJpaDao in the code with relevant Entitiy Class as the persistentClass. But then I could not find a way to set the entitiyManager as I am configuring it via datasource-config.xml as
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"></property>
<property name="persistenceUnitName" value="hibernatePersistenceUnit" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
So according to what I understood from the samples available I need to create separate DAO classes for all my entity classes and instantiate them in spring-ws-servlet.xml.
<bean id="testDao" class="com.sample.dao.TestDao" />
<bean id="service"
class="com.sample.service.DefaultService">
<property name="testDao" ref="testDao" />
</bean>
I think this will be a problem in the long run as I need to have two separated classes for each table in the database, instaintiating them in the xml, keep track of all of them at my service class. Is there any method to overcome this or any best practice available?
You wont need to create a specific DAO for each of your classes.
But you will have to remove the constructor and change your method signatures to include the needed persistentClass (or an instance where you call getClass on).
Basically you need to remove the persistentClass property and change the methods to use the class dynamically from the generic parameter of type T or Class.
That way you have ONE spring managed DAO which is able to handle all of your entities.

Why can't I retrieve the entities I've just persisted?

I've got this web service that basically queries the database and returns all persisted entities. For testing purposes, I've created a TestDataManager that persists 2 example entities after Spring context is loaded (BTW, I'm using JAX-WS, Spring, Hibernate and HSQLDB).
My TestDataManager looks like this:
#Component
public class TestDataManager {
#Resource
private SessionFactory sf;
#PostConstruct
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void insertTestData(){
sf.openSession();
sf.openSession().beginTransaction();
sf.openSession().persist(new Site("site one"));
sf.openSession().persist(new Site("site two"));
sf.openSession().flush();
}
}
My JAX-WS endpoint looks like this:
#WebService
public class SmartBrickEndpoint {
#Resource
private WebServiceContext context;
public Set<Site> getSitesForUser(String user){
return getSiteService().findByUser(new User(user));
}
private ISiteService getSiteService(){
ServletContext servletContext = (ServletContext) context.getMessageContext().get("javax.xml.ws.servlet.context");
return (ISiteService) BeanRetriever.getBean(servletContext, ISiteService.class);
}
}
This my Service class:
#Component
#Transactional(readOnly = true)
public class SiteService implements ISiteService {
#Resource
private ISiteDao siteDao;
#Override
public Set<Site> findByUser(User user) {
return siteDao.findByUser(user);
}
}
This is my DAO:
#Component
#Transactional(readOnly = true)
public class SiteDao implements ISiteDao {
#Resource
private SessionFactory sessionFactory;
#Override
public Set<Site> findByUser(User user) {
Set<Site> sites = new LinkedHashSet<Site>(sessionFactory.getCurrentSession().createCriteria(Site.class).list());
return sites;
}
}
This is my applicationContext.xml:
<context:annotation-config />
<context:component-scan base-package="br.unirio.wsimxp.dao"/>
<context:component-scan base-package="br.unirio.wsimxp.service"/>
<context:component-scan base-package="br.unirio.wsimxp.spring"/>
<bean id="applicationDS" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:sites"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="applicationDS" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.connection.release_mode">on_close</prop>
<!--<prop key="hibernate.current_session_context_class">thread</prop>-->
<prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
This is what's going on now:
when the app is deployed, TestDataManager#insertTestData kicks-in (due to #PostConstruct) and persist does not raise any exception. I should have 2 entities in the DB by now.
Afterwards, I invoke the endpoint by a SOAP client, and the request goes all the way up to the DAO. The Hibernate invocation does not raise any exception, but the returned list is empty.
The odd thing is, in TestDataManager, if I switch from sf.openSession() to sf.getCurrentSession(), I get an error message: "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here".
What I am doing wrong here? Why is the query "not seeing" the persisted entities? Why do I need to invoke sf.openSession() on TestDataManager although it's annotated with #Transactional?
I have done some tests with hibernate.current_session_context_class=thread in application.xml, but then I just switch problems in each class. I'd like not needing to manually invoke sf.openSession() and leave that for Hibernate to take care.
Thanks a lot for any help!
I think you need to commit the transaction on insertTestData:
#PostConstruct
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void insertTestData(){
Session session = sf.openSession();
session.persist(new Site("site one"));
session.persist(new Site("site two"));
session.flush();
session.close();
}
(I use hibernate in jpa mode)
I think your transactional annotations are not intercepted properly. Have you specified the HibernateVendorAdapter? In jpa+hibernate the integration is not fully setuped without it! Most likely you are missing this declaration.
After you should be able to autowire directly the Session instead of the Factory.
As a side note. If you use opensession in your code at least call it only once and keep the session in a variable. Else you are always opening a new one on each call i believe.
#PostConstruct
public void insertTestData(){
Obejct o = new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//Your code here
}
});
}
source: http://forum.springsource.org/showthread.php?58337-No-transaction-in-transactional-service-called-from-PostConstruct&p=194863#post194863

Is Exception handling required in Spring Transaction?

I am having a doubt in exception handling with a Transaction. To state clearly my problem I would like to show my configuration:
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionInterceptor" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributeSource">
<bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
</property>
</bean>
<bean id="baseService" abstract="true">
<property name="daoProvider" ref="daoProvider" />
</bean>
<bean id="customerService" parent="transactionInterceptor">
<property name="target">
<bean class="com.edfx.adb.service.CustomerService" parent="baseService" />
</property>
</bean>
<bean id="daoProvider" class="com.edfx.adb.dao.provider.DaoProvider">
<property name="customerDao" ref="customerDao" />
</bean>
<bean id="customerDao" class="com.edfx.adb.dao.CustomerDao">
<constructor-arg value="#{T(com.edfx.adb.persist.entity.Customer)}" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
The active transaction class is:
#Transactional
public class CustomerService extends BaseService implements ICustomerService {
#Transactional(readOnly = true)
public Customer getCustomerById(String id) {
return getDaoProvider().getCustomerDao().getCustomerById(id);
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class })
public void addNewCustomer(CustomerDTO customerDTO) {
Customer customer = new Customer();
customer.setCustomerId(customerDTO.getCustomerId());
customer.setCustomerName(customerDTO.getCustomerName());
customer.setActive(customerDTO.isActive());
getDaoProvider().getCustomerDao().save(customer);
}
}
My doubts lies in the method addNewCustomer. I have set rollbackFor = { Throwable.class }.
How does it work?
Also do I need to explicitly handle exception like:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class })
public boolean addNewCustomer(CustomerDTO customerDTO) {
Customer customer = new Customer();
customer.setCustomerId(customerDTO.getCustomerId());
customer.setCustomerName(customerDTO.getCustomerName());
customer.setActive(customerDTO.isActive());
try {
getDaoProvider().getCustomerDao().save(customer);
} catch (Throwable throwable) {
return false;
}
return true;
}
Forcefully I have created an exception by deleting a column from the customer table, BUT that exception wasn't catch in the try-catch block, rather I can catch that exception from the managed bean where I have invoked the addNewCustomer method.
This is an excerpt from Spring docs
In its default configuration, the Spring Framework's transaction
infrastructure code only marks a transaction for rollback in the case
of runtime, unchecked exceptions; that is, when the thrown exception
is an instance or subclass of RuntimeException. (Errors will also - by
default - result in a rollback). Checked exceptions that are thrown
from a transactional method do not result in rollback in the default
configuration.
You set rollbackFor = Throwable.class, now Spring will rollback for any Exception / Error. By default, whether we like it or not, Spring will rollback only for RuintimeException, and commit otherwise
The Spring framework only throws RuntimeExceptions, technically you never have to catch any exceptions.

Categories