I am new to Spring Transaction. Something that I found really odd, probably I did understand this properly.
I wanted to have a transactional around method level and I have a caller method within the same class and it seems like it does not like that, it has to be called from the separate class. I don't understand how is that possible.
If anyone has an idea how to resolve this issue, I would greatly appreciate. I would like to use the same class to call the annotated transactional method.
Here is the code:
public class UserService {
#Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
It's a limitation of Spring AOP (dynamic objects and cglib).
If you configure Spring to use AspectJ to handle the transactions, your code will work.
The simple and probably best alternative is to refactor your code. For example one class that handles users and one that process each user. Then default transaction handling with Spring AOP will work.
Configuration tips for handling transactions with AspectJ
To enable Spring to use AspectJ for transactions, you must set the mode to AspectJ:
<tx:annotation-driven mode="aspectj"/>
If you're using Spring with an older version than 3.0, you must also add this to your Spring configuration:
<bean class="org.springframework.transaction.aspectj
.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="transactionManager" />
</bean>
In Java 8+ there's another possibility, which I prefer for the reasons given below:
#Service
public class UserService {
#Autowired
private TransactionHandler transactionHandler;
public boolean addUsers(List<User> users) {
for (User user : users) {
transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
}
}
private boolean addUser(String username, String password) {
// TODO call userRepository
}
}
#Service
public class TransactionHandler {
#Transactional(propagation = Propagation.REQUIRED)
public <T> T runInTransaction(Supplier<T> supplier) {
return supplier.get();
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public <T> T runInNewTransaction(Supplier<T> supplier) {
return supplier.get();
}
}
This approach has the following advantages:
It may be applied to private methods. So you don't have to break encapsulation by making a method public just to satisfy Spring limitations.
Same method may be called within different transaction propagations and it is up to the caller to choose the suitable one. Compare these 2 lines:
transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
It is explicit, thus more readable.
The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). One solutions is already mentioned. Another nifty one would be to simply have Spring inject an instance of the service into the service itself, and call your method on the injected instance, which will be the proxy that handles your transactions. But be aware, that this may have bad side effects too, if your service bean is not a singleton:
<bean id="userService" class="your.package.UserService">
<property name="self" ref="userService" />
...
</bean>
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
#Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
With Spring 4 it's possible to Self autowired
#Service
#Transactional
public class UserServiceImpl implements UserService{
#Autowired
private UserRepository repository;
#Autowired
private UserService userService;
#Override
public void update(int id){
repository.findOne(id).setName("ddd");
}
#Override
public void save(Users user) {
repository.save(user);
userService.update(1);
}
}
This is my solution for self invocation:
public class SBMWSBL {
private SBMWSBL self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(SBMWSBL.class);
}
// ...
}
You can autowired BeanFactory inside the same class and do a
getBean(YourClazz.class)
It will automatically proxify your class and take into account your #Transactional or other aop annotation.
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advised, as it may look strange to colleagues. But it works with singletons, is easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution as described in Espens answer.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
#Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
#Transactional
public void addUser(String username, String password) {
// call database layer
}
public void addUsers(List<User> users) {
for (User user : users) {
_personDao.addUser(user.getUserName, user.getPassword);
}
}
}
The issue is related to how spring load classes and proxies. It will not work , untill you write your inner method / transaction in another class or go to other class and then again come to your class and then write the inner nested transcation method.
To summarize, spring proxies does not allow the scenarios which you are facing. you have to write the 2nd transaction method in other class
There is no point to use AspectJ or Other ways. Just using AOP is sufficient. So, we can add #Transactional to addUsers(List<User> users) to solve current issue.
public class UserService {
private boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
#Transactional
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
Related
I try to understand why this code doesn't work
In component:
#PostConstruct
public void runAtStart(){
testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
And repository :
public interface PersonRepository extends JpaRepository<Person, Long> {
Stream<Person> findTop10ByFirstName(String firstName);
}
I get:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses #Transactional or any other way of declaring a (read-only) transaction.
One key thing about Spring is that many annotated features use proxies to provide the annotation functionality. That is #Transactional, #Cacheable and #Async all rely on Spring detecting those annotations and wrapping those beans in a proxy bean.
That being the case, a proxied method can only be used when invoked on the class and not from within the class. See this about the topic.
Try:
Refactoring and call this #Transactional method from another class in your context, or
By self-autowiring the class into itself and calling the #Transactional method that way.
To demonstrate (1):
public class MyOtherClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional`
myTestStreamClass.testStream();
}
}
To demonstrate (2):
#Component
public class MyTestStreamClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional` since it's self-autowired
myTestStreamClass.testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
}
For spring boot application.
I have my aspect listen on my private or public method inside my scheduled method.
But it doesn't work. However, the aspect can listen on my scheduled method.
Here is an example on my github.
https://github.com/benweizhu/spring-boot-aspect-scheduled
Does any know the answer or why? or how to resolve it?
Thanks
Aspects will not work on calling other methods within the same class as it cannot be proxied.
It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen
note on proxying private methods :
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
refer : How can I log private methods via Spring AOP?
change the code as below:
#Component
public class Test{
public void reportInPrivateMethod() {
System.out.println("private method");
}
public void reportInPublicMethod() {
System.out.println("public method");
}
}
Now invoke this method :
#Component
public class ScheduledTasks {
#Autowired
private Test test;
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
test.reportInPrivateMethod();
test.reportInPublicMethod();
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
Modify the aspects as per the changes :
#Aspect
#Component
public class Monitor {
#AfterReturning("execution(* com.zeph.aop.ScheduledTasks.reportCurrentTime())")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPrivateMethod())")
public void logServiceAccessPrivateMethod() {
System.out.println("Completed PRIVATE :");
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPublicMethod())")
public void logServiceAccessPublicMethod() {
System.out.println("Completed PUBLIC: ");
}
}
I have rest style controller in Spring. In controller I have injected dao interfaces. From controller I persist data. In the other words, I have like REST web service. people sends me data, and I persits it.
/**
* Payment rest controller which receives
* JSON of data
*/
#Controller
#RequestMapping("/data")
public class PaymentTransaction {
#Autowired
private TestDao dao;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
}
At the moment I have #transaction annotation in Dao classes. For instance:
import org.springframework.transaction.annotation.Transactional;
#Component
#Transactional
public interface TestDao {
#Transactional(propagation = Propagation.REQUIRED)
public void first();
}
I have read that this is very bad style. Using this answer at stackoverflow , here is explain and examples why is this bad - we must not add this annotation in DAO and in controller too. We must add it in service layer.
But I don't understand what is the service layer? Or where is it? I do not have anything like this.
where should I write #Transactional annotation?
Best regards,
According to the cited post, you should design your classes somehow like this (rather pseudocode):
controller (responsible for handling clients' requests/responses)
#Controller
#RequestMapping("/data")
public class TestREST {
#Autowired
private TestService service;
public void storePayment(PaymentDTO dto) {
service.storePayment(dto); //request from a client
}
public PaymentDTO getPayment(int paymentId) {
return service.getPayment(paymentId); //response to a client
}
}
service layer (also called business layer, responsible for business logic - knows what to do with incoming messages, but does not know where they come from).
public class TestServiceImpl {
#Autowired
private TestDao dao;
#Transactional(propagation=Propagation.REQUIRED) //force transaction
public void storePayment(PaymentDTO paymentDto) {
// transform dto -> entity
dao.storePayment(paymentEntity); //read-write hence transaction is on
}
#Transactional(propagation=Propagation.NOT_SUPPORTED) //avoid transaction
public Payment getPayment(int paymentId) {
return dao.findPayment(paymentId); //read-only hence no transaction
}
}
data access layer (also called persistence layer, responsible for accessing database - knows how to use entity model / ORM, does not know anything about the upper service layer)
public class TestDAOImpl {
#PersistenceContext
private EntityManager em;
public void storePayment(PaymentEntity paymentEntity) {
em.persist(paymentEntity);
}
public PaymentEntity getPayment(int paymentId) {
return em.find(PaymentEntity.class, paymentId);
}
}
By this approach you get separation of concerns mentioned in the post. From the other hand such an approach (business layer vs data access layer) got a little dose of criticism from Adam Bien's on his blog ("JPA/EJB3 killed the DAO"). As you can see there is no a single solution for the problem, but I encourage to read some other opinions and apply the solution you find the most suitable for your needs.
When you call two Dao methods first & second from controller, 2 transactions will be done, one with starts before first method and ends after it's execution and the second one starts before second method starts and ends after it's execution. Whereas you create an additional class in between controller and dao (usually this is called service layer) and annotate it with #Transactional and call multiple Dao methods in it, a transaction is started at the start of service method and all the dao calls will be executed and transaction will be closed, which is what you require. And inject the Service into Controller.
Controller -> Service -> Dao
#Controller
#RequestMapping("/data")
public class PaymentTransaction {
#Autowired
private TestService service;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
}
}
#Service
#Transactional
public class TestService {
#Autowired
private TestDao dao;
#Transactional
public void serviceCall(){
dao.first();
dao.second();
}
}
I'm using Spring for wiring dependencies specifically for DAO classes that use Hibernate, but I'm getting an exception that has me puzzled:
$Proxy58 cannot be cast to UserDao
My DAO is configured as follows:
<bean id="userDao" class="com.domain.app.dao.UserDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
And I have an interface, abstract base class and a final implementation as follows.
Interface:
public interface Dao {
public void save(Object object);
public Object load(long id);
public void delete(Object object);
public void setSessionFactory(SessionFactory sessionFactory);
}
Abstract Base Class:
public abstract class BaseDao implements Dao {
private SessionFactory sessionFactory;
#Transactional
#Override
public void save(Object object) {
PersistentEntity obj = (PersistentEntity) object;
currentSession().saveOrUpdate(obj);
}
#Transactional
#Override
public abstract Object load(long id);
#Transactional
#Override
public void delete(Object object) {
// TODO: this method!
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session currentSession() {
return sessionFactory.getCurrentSession();
}
}
Implementation:
public class UserDao extends BaseDao implements Dao {
#Transactional(readOnly=true)
#Override
public Object load(long id) {
Object user = currentSession().get(User.class, id);
return user;
}
}
The following throws the exception mentioned above:
UserDao dao = (UserDao) context.getBean("userDao");
This, however, does not throw an exception:
Dao dao = (Dao) context.getBean("userDao");
If anyone can offer any assistance or guidance as to why this exception is happening, I would be very appreciative.
Spring uses JDK dynamic proxies by default ($Proxy58 is one of them), that can only proxy interfaces. This means that the dynamically created type $Proxy58 will implement one or more of the interfaces implemented by the wrapped/target class (UserDao), but it won't be an actual subclass of it. That's basically why you can cast the userDao bean to the Dao interface, but not to the UserDao class.
You can use <tx:annotation-driven proxy-target-class="true"/> to instruct Spring to use CGLIB proxies that are actual subclasses of the proxied class, but I think it's better practice to program against interfaces. If you need to access some methods from the proxied class which are not declared in one of it's interfaces, you should ask yourself first, why this is the case?
(Also, in your code above there are no new methods introduced in UserDao, so there is no point in casting the bean to this concrete implementation type anyway.)
See more about different proxying mechanisms in the official Spring reference.
I was writing unit tests and needed to be able to stub out the DAOs for some calls.
Per This guys post:
http://www.techper.net/2009/06/05/how-to-acess-target-object-behind-a-spring-proxy/
I used his method provided:
#SuppressWarnings({"unchecked"})
protected <T> T getTargetObject(Object proxy, Class<T> targetClass) throws Exception {
if (AopUtils.isJdkDynamicProxy(proxy)) {
return (T) ((Advised)proxy).getTargetSource().getTarget();
} else {
return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class
}
}
Then you can easily call it with the proxy and get the object behind the proxy and manipulate the objects in it directly as needed.
I have a singleton that has a spring injected Dao (simplified below):
public class MyService<T> implements Service<T> {
private final Map<String, T> objects;
private static MyService instance;
MyDao myDao;
public void set MyDao(MyDao myDao) {
this. myDao = myDao;
}
private MyService() {
this.objects = Collections.synchronizedMap(new HashMap<String, T>());
// start a background thread that runs for ever
}
public static synchronized MyService getInstance() {
if(instance == null) {
instance = new MyService();
}
return instance;
}
public void doSomething() {
myDao.persist(objects);
}
}
My spring config will probably look like this:
<bean id="service" class="MyService" factory-method="getInstance"/>
But this will instantiate the MyService during startup.
Is there a programmatic way to do a dependency injection of MyDao into MyService, but not have spring manage the MyService?
Basically I want to be able to do this from my code:
MyService.getInstance().doSomething();
while having spring inject the MyDao for me.
Here is a solution, create a class with a static factory method:
public class MyService {
private static MyService instance;
private MyDao myDao;
public static MyService createInstance(final MyDao myDao) {
instance = new MyService(myDao);
return instance;
}
private MyService(final MyDao myDao) {
this.myDao = myDao;
}
public static synchronized MyService getInstance() {
return instance;
}
public void doSomething() {
// just do it!
myDao.justDoIt();
}
}
and use spring to initilize it:
<bean class="my.path.MyService" factory-method="createInstance" scope="singleton">
<constructor-arg ref="reference.to.myDao" />
</bean>
and now you should be able to do:
MyService.getInstance().doSomething();
without any problems.
If you want a singleton, why not just define that one class in the Spring configs, and it's automatically a singleton (by default).
To avoid initialising at start up, have you looked at Spring lazy initialisation ? Basically you need:
lazy-init="true"
in your bean definition.
As mentioned by others, you should let spring manage your singletons, but if you want to manage them yourself and just let spring inject dependencies, do this:
applicationContext.getAutowireCapableBeanFactory().autowireBean(yourService);
I believe the FactoryBean interface is a good alternative for you. It's a very good choice when you need to execute some initialization logic. For example to start an in memory database or some background processes in separate threads.
You can read more about it in the reference documentation.
An example that demonstrates how I instantiate a database and return a datasource everytime someone wants a bean from the FactoryBean implementation.
#PostConstruct
void init() {
embeddedDatabase = new EmbeddedDatabaseBuilder().addScript(schemaPath)
.addScript(dataPath).setType(embeddedDatabaseType).build();
}
public DataSource getObject() throws Exception {
return embeddedDatabase;
}
This enables loose coupling between the factory logic and the returned object. It's heavily used by the Spring framework internally.
If you want it to be initialized the first time you use it, then set lazy-initialization to true.
Another alternative if you want your code to interact with the Spring container is to create a factory that implements the ApplicationContextAware interface. Then you can do something like this:
myDao = context.getBean(MyDao.class);