Using custom timeout in Spring transaction management - java

I have several methods with spring #Transactional in my project as following:
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW)
public void addMember(InputParam input)
{
// do somthing...
}
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW)
public void blockMember(InputBlockParam param)
{
// do somthing...
}
Then I set different timeout per method as following:
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW,timeout = 40)
public void addMember(InputParam input)
{
// do somthing...
}
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW, timeout = 20)
public void blockMember(InputBlockParam param)
{
// do somthing...
}
I want in last step set timeout as configurable by a properties file but I don't know what.
Is there any solution for set timeout in spring Transactional annotaion configurable or dynamically?
EDIT:
I define sys.tx.mngr in spring context file as following:
<bean id="sys.tx.mngr" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>
<tx:annotation-driven transaction-manager="sys.tx.mngr" />
Or is there alternative way for define timeout in spring context file per method?

It can be done on following ways only :-
a) Using Reflection.
b) Using Instrumentation.
c) Using TransactionTemplate (Programatically transaction).
For (a) & (b) you can have your class like following :-
public class Test implements InitializingBean {
#Autowired
private Environment env;
public void afterPropertiesSet() throws Exception {
System.out.println("Sample prop 1 value : "+env.resolvePlaceholders("${prop1.value}"));
//Code to set/modify Transactional annotation "timeout"
// attribute values for all methods
}
}
Link on how to set/modify values can be found here
Modify field annotation value dynamically
Modify a class definition's annotation string parameter at runtime
For (c) you can have config like :-
public class MemberDaoImpl {
#Autowired
private Environment env;
#Autowired
private TransactionTemplate transactionTemplate;
public void addMember(InputParam input) {
transactionTemplate.setTimeout(Integer.parseInt(env.resolvePlaceholders("${addmember.timeout}")));
// do somthing...
}
}
<bean id="memberDao" class="com.xxx.impl.MemberDaoImpl">
<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="sys.tx.mngr" />
</bean>
</property>
</bean>
<bean id="sys.tx.mngr" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>

Related

Transaction rollback does not work in spring

Basically, i have a Sampleapiclass class in which some files are retrieved into a list and the list is looped and for each file performAction is called. if a file fails validation, i want the transaction to roll back. The problem is, i tried annotating with rollback, but even if one file validation fails, transaction is not rolled bak.eg. file 1, succeeds, gets inserted, file 2 fails, but the file 1 is in the database. Any idea what might be wrong ?
As an additonal note, Sampleapiclass is initialied from the main method , may be it has somethin to do that that ?
f.eks: from main class
class MainClass
{
public static void main(String[] args)
{
ApplContext ctx = new ClassPathxmlApplicationContext("application-context.xml");
MainClass app = ctx.getBean(MainClass.class);
app.run(args);
}
void run()
{
Sampleapiclass api = new Sampleapiclass();
DaoClass dao = new Sampleapiclass();
api.apimethod(dao)
}
}
class Sampleapiclass
{
void apimethod(Dao daoclass)
{
serviceClass serviceClass = new ServiceCLass(daoclass);
List<String> files = filesFromSomewhere
for (String file: filesFromSomewhere)
{
try
{
serviceClass.performAction(file);
}
catch(Exception e)
{
}
}
}
}
class serviceClass
{
private final Dao daoclass;
public serviceClass(DaoClass daoclass)
{
this.daoclass = daoclass;
}
#Transactional(rollbackFor=Exception.class)
void performAction(String file) throws CustomException
{
dovalidation(file); // this method can throw CustomExeption if validation fails
daoclass.insert(file)
}
dovalidation(String file) throws CustomExeption
{
if (file.endsWith("somethng") throw new CustomExeption();
}
}
class dao
{
void insert(String file)
{
getNamedParameterJdbcTemplate().update(parameters);
}
}
Contents of app contetxt:
<bean class="Configbean class />
<bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="${main.db.uri}"/>
<property name="username" value="${main.db.username}"/>
<property name="password" value="${main.db.password}"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
The problem is that #Transactional works with AOP. AOP doesn't work on inclass method calls (due to Proxying). Try throwing an exception inside of your #Transactional for it to work or call a method outside of your class also marked with #Transaction(propagation = Propagation.SUPPORTS)
also don't forget #EnableTransactionManagement over your #Configuration class
helpful resource https://javamondays.com/spring-transactions-explained/

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.

How to inject EntityManager in EntityListeners

I need to inject EntityManager in EntityListener class so that I can perform CRUD operation on it.
POJO:
#Entity
#EntityListner(AuditLogging.class)
class User
{
//Getter / setter of properties
}
AuditLogging (Listner class)
public class AuditInterceptor
{
#PersistenceContext
EntityManager entityManager;
public void setEntityManager(EntityManager entityManager)
{
this.entityManager = entityManager;
}
#PrePersist
public void prePersist(Object obj)
{
// Here I want to use ENTITY manager object so that I can perform CRUD operation
// with prePersist coming object.
entityManager.unwrap(Session.class).save(obj);
// But I am getting NULL POINTER EXCEPTION for entity manager object
}
}
JDBC-CONFIg.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan" value="com.XXXXX.entity" />
<property name="jpaProperties">
</bean>
<!-- Datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driver.classname}" />
<property name="jdbcUrl" value="${jdbc.url}" />
</bean>
<!-- transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
EntityListener is not managed by any of the container.EntityListeners are instanciated by JPA, so Spring does not have an opportunity to inject EntityManager.
My question is, how we can inject inject EntityManager in EntityListener class so that I can perform CRUD operation on it ???
I have faced a similar problem where I was trying to create history records for an entity using EntityListeners.
In order to resolve this problem, I have created utility class BeanUtil with a static method to get the bean and used this util class to get bean inside my Entitylistener class
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Now we can call BeanUtil.getBean() to get the bean of any type
public class FileEntityListener {
#PrePersist
public void prePersist(File target) {
perform(target, INSERTED);
}
#Transactional(MANDATORY)
private void perform(File target, Action action) {
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
entityManager.persist(new FileHistory(target, action));
}
}
We can use this BeanUtil class to get any spring managed bean from anywhere, To know more you can read my article JPA Auditing: Persisting Audit Logs Automatically using EntityListeners.
Anyways, I got this done by getting entityManager reference from EntityManagerFactory bean which is configured in my jdbc-config.xml. But again this is not what I wanted. I wanted to work around with #PersistenceContext.
#Autowired
EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
entityManager=entityManagerFactory.createEntityManager();
this.entityManagerFactory = entityManagerFactory;
}
Here are few notes that we need to keep in mind:
We can't inject an EntityManager into an EntityListener (through
#PersistenceContext). EntityListener is not managed by any of the
containers
#PersistenceContext class cannot be static. So we cant
attain the instance while class loading.
EntityListeners are
instantiated by JPA, so Spring does not have an opportunity to
inject EntityManager
Well, the first solution which came into my mind is a little "hack", but should work.
public class AuditInterceptor {
static setEntityManager emf;
#Autowired
public void setEntityManagerFactory(EntityManager emf) {
AuditInterceptor.emf = emf;
}
#PrePersist
public void prePersist(Object obj) {
EntityManager entityManager = emf.getEntityManager();
// Here I want to use ENTITY manager object so that I can perform CRUD operation
// with prePersist coming object.
entityManager.unwrap(Session.class).save(obj);
// But I am getting NULL POINTER EXCEPTION for entity manager object
}
}
Inside of your code use EntityManager entityManager = emf.getEntityManager()
Declare your AuditInterceptor as a spring bean (#Component with component-scan or define AuditorInterceptor as a bean)
I used a ThreadLocal to pass the Spring Application Context which contains EntityManager around. Though I am not sure if it is safe Is it safe to pass in the Spring Application Context into a ThreadLocal associated with a request? but so far it is working for me.
The listener can be modified to have autowiring like this. However this needs to be done on on the handlers and not the constructor (doing it on the constructor seems less predictable). You are also not limited to the EntityManager but you have access to the whole context.
#Autowired
private EntityManager entityManager;
#Autowired
private MyDao myDao;
#PrePersist
public void pre() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
Objects.requireNotNull(myDao);
myDao.doSomething();
}

Will called function be still in transaction if we have a #Transaction in caller function?

I understand we need to keep #Transactional boundaries as short as possible. Here is the code :
I am using userDAO object through Spring dependency injection :
private static ApplicationContext context ;
private UserDAO userDAO;
public TransactionsTest() {
userDAO = (UserDAO) context.getBean("userDAO");
}
I am calling testSaveUserAccounts() from TransactionsTest class trying to use userDAO for insertion/updation of data.
Case 1:
#Transactional
public void testSaveUserAccounts() {
UserAccounts userAccounts = new UserAccounts();
userAccounts.setCommunityId(10L);
userDAO.saveObject(userAccounts);
}
// This method is inside UserDAO
public void saveObject(Object object) {
entityManager.merge(object);
}
Case 2:
#Transactional
public void testSaveUserAccounts() {
UserAccounts userAccounts = new UserAccounts();
userAccounts.setCommunityId(10L);
userDAO.saveObject(userAccounts);
}
// This method is inside UserDAO
#Transactional(propagation=Propagation.REQUIRED)
public void saveObject(Object object) {
entityManager.merge(object);
}
Spring Context :
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceLocal" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceUnitName" value="spring-jpa" />
</bean>
UserDAO :
#Repository
public class UserDAO extends BaseDAO {
#Transactional(propagation=Propagation.REQUIRED)
public void saveObject(Object object) {
entityManager.merge(object);
}
}
BaseDAO :
public abstract class BaseDAO {
protected EntityManager entityManager;
protected HashMap<String,Long> eventIdMap = new HashMap<String,Long>();
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this. entityManager = entityManager;
}
public <T> T getById(final Class<T> clazz, final Serializable id) {
T object = clazz.cast(entityManager.find(clazz, id));
return object;
}
#Transactional
public Object save(Object ob) {
Object object = entityManager.merge(ob);
return object;
}
#Transactional
public void persist(Object ob) {
entityManager.persist(ob);
}
#SuppressWarnings("unchecked")
public <T> ArrayList<T> getAll(final Class<T> clazz) {
String hqlQuery = "from "+ clazz.getSimpleName();
ArrayList<T> list = (ArrayList<T>)entityManager.createQuery(hqlQuery).getResultList();
return list;
}
}
I have been experimenting around several transactional boundaries REQUIRED, REQUIRES_NEW, SUPPORTS, etc but couldn't confidently make out as to why Case 1 (when method2 is called which is inside transactional boundary of method1) does not merges the data, while, this is solved in Case 2.
Why do I need to specify #Transactional in inner methods as well when already I have marked calling function within Transaction boundary ?
Your transaction test class is not a Spring Bean that is why Case 1 does not work. Spring needs to detected that a method has #Transactional on it and it does that when spring registers the bean with the spring bean factory.
Also keep in mind that the if you are doing Proxy Based AOP calls within the same bean will not be caught by the transaction aspect unless you use AspectJ load time weaving or AspectJ compile time weaving.
Also putting #Transactional on your Dao's is not a really a good idea because transaction boundaries are best marked at the service layer. The reason why is that a particular service method might need to interact with multiple Dao's and you would want those Dao's actions to be part of the tx started by the service layer, rather than having to analyze the Dao's to see what the propagation behavior is.
Can you post the complete code for the test class?
#Transactional does nothing locally, it only has an effect when called from a different service. In other words, you have to leave your current context for transaction annotations to do anything. so calling method 1 is identical in both cases, Case 2 only does anything if method2 is called from another service.

Hibernate interceptors

I am trying to implement method described here, but can't make it work.
There are no errors during compilation, but the interceptor never fired.
DAO:
public class GeneralInvoicesDAO {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private Interceptor entityInterceptor;
#Transactional
public void update(GeneralInvoice object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, entityInterceptor, null);
session.saveOrUpdate(object);
}
}
The intercepter class:
public class NewEntityInterceptor extends EmptyInterceptor implements Interceptor {
/**
*
*/
private static final long serialVersionUID = 2914362528125673753L;
#Override
public Boolean isTransient(Object n) {
Logger logger = Logger.getLogger(getClass().getName());
try {
logger.warn("test");
Boolean result = Boolean.FALSE;
BaseEntity entity = (BaseEntity) n;
if (entity.getId() <= 0L) {
entity.setId(null);
result = Boolean.TRUE;
}
logger.warn(result.toString());
return result;
}
catch (Exception e) {
e.printStackTrace();
return super.isTransient(n);
}
}
}
Config:
<bean id="newEntityInterceptor" class="de.crm.interceptor.NewEntityInterceptor" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="entityInterceptor" ref="newEntityInterceptor" />
</bean>
What's wrong?
Thank you
Few observations from you code
You are NOT overriding any of the methods in EmptyInterceptor. Check out this link with a sample interceptor example.
EmptyInterceptor provides the default implementations of an entity life cycle events which you can intercept. You need to override the method of your interest.
I am not sure if transactionManager needs any reference to the interceptor. Its purely a hibernate thing. You can do away with it. Interceptors are needed either with session or session factory (you are using session level interceptor).

Categories