Transaction rollback does not work in spring - java

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/

Related

Autowire annotation working in Controller but not anywhere else?

I am using Spring 4.1.1.
I have the following in my spring-dispatcher-servlet.xml
<context:component-scan base-package="com.au.controller,com.au.util" />
<mvc:resources location="/" mapping="/**" />
<mvc:annotation-driven />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/caballocation" />
<property name="username" value="root" />
<property name="password" value="1234" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="AddImplDao" class="com.au.dao.AddressDaoImpl" />
I have the following controller class in package com.au.controller
#Controller
public class ControllerMain {
#Autowired
AddressDao obj;
#RequestMapping(value = "/test")
public #ResponseBody String test(){
//logger.debug("getWelcome is executed!");
obj.select();
return "1";
}
}
In the above code obj.select works as it gets autowired.
But the following class which is in com.au.util package have the value null of the object which is autowired.
public class DistanceCalculator {
#Autowired
AddressDao obj1;
public String calculate(String from, String to) throws IOException, JSONException {
..
Map output = obj1.calc(from, to);
..
}
Obj1 is null while execution. Getting java.lang.NullPointerException at obj1.calc(from,to)
Following is the interface and its implementation.
AddressDao.java
public interface AddressDao {
public void select();
public Map calc(String from,String to);
}
AddressDaoImpl.java
public class AddressDaoImpl implements AddressDao {
#Autowired
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall simpleJdbcCall;
#Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcCall = new SimpleJdbcCall(dataSource).withProcedureName("CheckForValuesInDB");
}
#Override
public void select() {
// TODO Auto-generated method stub
}
#Override
public Map calc(String from, String to) {
// TODO Auto-generated method stub}
What is the reason behind this?
you have to add extra space in com.au.controller,com.au.util so it should look like 'com.au.controller, com.au.util'. As for now only com.au.controller is scanned by your configuration.
//Edit
There should be #Component annotation in DistanceCalculator class

Using custom timeout in Spring transaction management

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>

Spring annotation based SAP connector

I'm trying to move from a xml based config to java annotations
I need your help getting this to work:
Obviously I can't set the RemoteJco interface to my SapConnector but what can I do to get this xml-config working?
#Bean
public RmiProxyFactoryBean jcoPool(){
RmiProxyFactoryBean jcoPool = new RmiProxyFactoryBean();
jcoPool.setServiceUrl("rmi://localhost/CH");
jcoPool.setServiceInterface(RemoteJco.class);
jcoPool.setRefreshStubOnConnectFailure(true);
return jcoPool;
}
#Bean
public SapConnector SapConnector(){
SapConnector sapConnector = new SapConnector();
sapConnector.setJcoPool(jcoPool());
return sapConnector;
}
this in the XML-Config works just fine:
<!-- JCO-Pool RMI Service -->
<bean id="jcoPool" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost/CH"/>
<property name="serviceInterface" value="com.itensis.jco.common.RemoteJco"/>
<property name="refreshStubOnConnectFailure" value="true" />
</bean>
<bean id="SapConnector" class="com.itensis.core.SapConnector">
<property name="jcoPool">
<ref bean="jcoPool" />
</property>
</bean>
this is my SAP-Connector
#Service
public class SapConnector {
#Autowired private RemoteJco jcoPool;
public RemoteJco getJcoPool() {
return jcoPool;
}
public void setJcoPool(RemoteJco jcoPool) {
this.jcoPool = jcoPool;
}
}
You have to make some changes on the jcoPool bean:
#Bean
public RemoteJco jcoPool(){
RmiProxyFactoryBean jcoPool = new RmiProxyFactoryBean();
jcoPool.setServiceUrl("rmi://localhost/CH");
jcoPool.setServiceInterface(RemoteJco.class);
jcoPool.setRefreshStubOnConnectFailure(true);
jcoPool.afterPropertiesSet();
return (RemoteJco) jcoPool.getObject();
}
Make sure that you return value has the same class as you used as service interface. And you have to call afterPropertiesSet() before calling getObject on the RmiProxyFacotoryBean instance.

Invoke static method from spring config

Is it possible to invoke static method in Spring configuration file?
public MyClass {
public static void staticMethod() {
//do something
}
}
<bean id="myBean" class="MyClass">
<!-- invoke here -->
</bean>
When the static method creates an instance of MyClass you an do it like this
config
<bean id="myBean" class="MyClass" factory-method="staticMethod">
<!-- invoke here -->
</bean>
code
public static MyClass staticMethod() {
//create and Configure a new Instance
}
If you want the method only to be called on bean instantiation spring can't do it this way.
config
<bean id="myBean" class="MyClass" init-method="init">
<!-- invoke here -->
</bean>
code
public static void staticMethod() {
//create and Configure a new Instance
}
public void init() {
staticMethod();
}
try this
<bean id="b1" class="org.springframework.beans.factory.config.MethodInvokingBean">
<property name="staticMethod" value="MyClass.staticMethod" />
</bean>
see http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/MethodInvokingBean.html
Try something like this:
<!-- call static method -->
<bean id="test" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="MyClass" />
<property name="targetMethod" value="staticMethod" />
<property name="arguments">
<list>
<value>anArgument</value>
</list>
</property>
</bean>
Remove arguments as you might not need them.
Taken from https://gist.github.com/bulain/1139874
I was needing to call a static method. The above code worked fine.
This might be useful as well: How to make spring inject value into a static field.
If you are using annotations for spring configuration you can add the following method into your #Configuration class:
#Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("MyClass.staticMethod");
return methodInvokingFactoryBean;
}

MongoDb Factory with spring

I want to use MongoDB with Spring.
I'm trying to inject a MongoDbFactory in my DAO main class.
I dont want to use MongoTemplate because I need to use the MongoDB-Driver
When I try to run a jUnit Test, for test my DAO class, i get an NullPointerExeption on my factory....
I think it's a problem with my injection.
My app-config :
<bean id="mongoFactoryBean" class="org.springframework.data.mongodb.core.MongoFactoryBean">
<property name="host" value="127.0.0.1" />
<property name="port" value="27017"/>
</bean>
<bean id="mongoDbFactory"
class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
<constructor-arg name="mongo" ref="mongoFactoryBean" />
<constructor-arg name="databaseName" value="agence_voyage" />
</bean>
<bean id="dao" class="dao.daoImpl.DaoImpl">
<property name="mongoFactory" ref="mongoDbFactory" />
</bean>
my DAO class :
public class DaoImpl implements Dao {
private MongoDbFactory mongoFactory;
private DB db;
#Required
public void setMongoFactory(MongoDbFactory myMongoFactory){
this.mongoFactory= myMongoFactory;
}
//TODO development mongodb://localhost
public void connect() throws UnknownHostException {
try{
this.db = mongoFactory.getDb("agence_voyage"); //NullPointer here
}
catch(DataAccessException d){
System.out.println(d);
}
}
public int getVoyageCount(String collection) {
DBCollection col = db.getCollection(collection);
return (int) col.count();
}
}
And then my little test :
public class TestDao {
#Test
public void test() {
Dao test = new DaoImpl();
try {
test.connect();
} catch (UnknownHostException e) {
System.out.println(e);
assertTrue(false);
}
assertTrue(test.getVoyageCount("voyage")== 1);
}
}
Have you got any solution ?
I'm sure it's an idiot error but I don't find it !
Thanks in advance !
It doesn't seem like your test is in any way connected to the Spring context. You actually create the object yourself instead of getting a Spring managed bean
Dao test = new DaoImpl();
Why would Spring do anything to this object?
Add these annotations to your class
#ContextConfiguration(locations = "yourfile.xml")
#RunWith(SpringJUnit4ClassRunner.class)
and inject the DaoImpl bean directly.
#Autowired
private Dao test;
Then use that in your test.
Read the chapter on unit testing in the Spring documentation.

Categories