Injecting multiple dao into one service - java

if I have several DAOs to be injected into a Service that need to work together in a single transaction, how can I do this ?
#Component
public class CallerClass{
#Autowired
private TransactionClass1 class1;
#Autowired
private TransactionClass2 class2;
public void saveOperation(){
try{
class1.save();
class2.save();
}catch(Exception ex){
}
}
}
Like above simple codes. However, this code is lack

You would just inject all the DAOs in the same manner as you do normally i.e. setter or constructor using #Inject or #Autowired.
You then annotate your service method as Transactional and invoke the required operations on the multiple DAOs. The transaction will encompass all of the dao calls within it.
#Transactional
public void doStuff() {
dao1.doStuff();
dao2.doStuff();
}

You must open the transaction before you use the first dao (For example with #Transactional).
public class MyService{
#Inject
Dao1 dao1;
#Inject
Dao2 dao2;
#Transactional
public doStuffInOneTransaction{
Object x = dao1.load();
Object y = doSomething(x);
dao2.save(y);
}
}

Do you use JTA? Do you implement your transactions on your own? Please provide more information about your architecture so we can respond accordingly.
EDIT:
Check this one out, for example:
http://community.jboss.org/wiki/OpenSessionInView

Related

Does spring self injection lead to memory leakage?

In Spring, beans are encapsulated by a proxy object and when called from outer object the proxy object's method is also called. By using this trick transaction management is performed in proxy object's methods.
But when you are calling a method in the same bean and if you want the called method to be run in another transaction with the caller method it is not possible. Since the method calls in the same bean does not pass through the proxy object which performs transaction operations.
In order to solve this, self-injection of the bean inside itself is proposed.
like this
#Service
public class MyService{
#Autowired
public MyService myService;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void method1(){
method2();// runs in the same transaction with method1
myService.method2();// runs in separate transaction from method1
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void method2(){
}
}
I want to ask whether self-injection leads to memory leakage.
Outer bean MyService includes injected myService and myService should include attribute of type MyService, which should include attribute of type MyService ....
The service is not copied, it is only a reference. But I'd be wary of such a design: it creates a circular reference and the object graph is impossible to create at once. You first need to create the incomplete service, then set its field. Furthermore, it is kind of confusing and surprising to find such a dependency to self
I'd propose a different solution:
#Component
public class WithTransaction {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNew(final Runnable runnable) {
runnable.run();
}
}
#Service
public class MyService{
private final WithTransaction withTransaction;
#Autowired
public MyService(final WithTransaction withTransaction) {
this.withTransaction = withTransaction;
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void method1(){
method2();// runs in the same transaction with method1
withTransaction.requiresNew(this::method2); // runs in separate transaction from method1
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void method2(){
}
}
And if you can avoid it: don't call any public methods from your service on the service itself. Split the service into smaller components so that each "public" call must go through the proxy.

#Autowired annotaded method vs #PostConstruct annotaded method

What is the difference between using #Autowired or #PostConstruct on a method since they offer the same result (according to what I have understood from different sources)
UPDATE:
Here is an example of my class in which I get the same result if I use #Autowired or #PostCosntruct to annotate the method configClient()
#Service
public class AwsSTSService {
#Autowired
private AwsConfiguration awsConfiguration;
public CustomCredentials getCredentials() {
......
return customCredentials;
}
#Autowired // or #PostConstruct
private void configClient() {
CustomCredentials customCredentials = getCredentials();
awsConfiguration.setAwsAccessKey(customCredentials.getAccessKeyId());
awsConfiguration.setAwsSecretKey(customCredentials.getSecretAccessKey());
awsConfiguration.setExpiration(customCredentials.getExpiration());
awsConfiguration.setSessionToken(customCredentials.getSessionToken());
}
}
Actually, they don't have anything in common. #Autowired could be used to inject any dependency in your beans (components), on the other hand, #PostConstruct can be used on methods of your beans, and spring boot will call that method after that bean was created (for purposes like populating a database or calculating some initial data).
You can see how this article used these two annotations in its example codes https://www.baeldung.com/spring-postconstruct-predestroy#postConstruct

Difference between #Autowired final setter and non-final setter in spring

Assuming:
abstract class CommonService {
protected VipMapper vipMapper;
#Autowired
public final void setVipMapper(VipMapper vipMapper) {
this.vipMapper = vipMapper;
}
}
#Service
public class BookService extends CommonService {
public int find() {
return vipMapper.findVip(); // return 100
}
}
#SpringBootTest
class BookServiceTest {
#Autowired
private BookService bookService;
#Test
void find() {
VipMapper v = new VipMapper() {
#Override
public int findVip() { // This method will not execute
return 10;
}
};
bookService.setVipMapper(v);
int find = bookService.find(); // find = 100 (not 10)
}
}
1. What is the reason I cannot inject VipMapper when setVipMapper method is final and I can inject when setVipMapper method is not final?
2. How can I inject VipMapper in runtime but still use #Autowired final setter?
Update
I'm using Spring + Mybatis
Source code:
https://bitbucket.org/nguyentanh/stackoverflow
Using the above code, when run that test for findVipCustomerTop3, I get an error connection. But when to remove final in CommonService.java (or #Transactional in BookService.java), the test is success
You issue is not with autowiring. VipMapper get autowired correctly and you are trying to replace the mapper manually via bookService.setVipMapper(v); in your test. It does not replace the vipMapper you passed. To Check this behaviour, define a getter in your service to get the vipMapper and it will return the original vipMapper which was autowired by spring.
Just remember you are not working with an instance of your original BookService class, you are working with a sub class of BookService which is run time generated .
Your original question missed an important piece of info which is #Transactional annotation in your service. As soon as #Transactional annotation is there, Spring actually need to create a proxy. Now spring will choose JDK dynamic proxy or CGLIB proxy to create the proxy for your book service. Since your Service does not have an interface, JDK dynamic proxy choice is not possible so spring is left with CGLIB proxy.
CGLIB proxy has its limitations.
With CGLIB, final methods cannot be advised, as they cannot be overridden in runtime-generated subclasses
Here is technique if you want to actually replace it. Add the following in your test class instead of the line bookService.setVipMapper(v);
((BookService)AopProxyUtils.getSingletonTarget(bookService))
.setVipMapper(v);
The above option is doing it hardcore way but good for understanding the concept. There is another option telling spring to create BookService in your test with a mock vipMapper autowired when it creates BookService.
#SpringBootTest
class BookServiceTest {
#Autowired
private BookService bookService;
#MockBean
private VipMapper vipMapper;
#Test
void find() {
when(vipMapper.findVip()).thenReturn(10);
bookService.setVipMapper(v);
int find = bookService.find();
}
}
Reference
https://docs.spring.io/spring/docs/5.2.8.RELEASE/spring-framework-reference/core.html#aop-proxying
From my understanding, you are using #autowired for setVipMapper() so it already injected VipMapper with default findVip() returing 100. Therefore, defining setVipMapper() as final won't change the value you pass through anymore

#Asyn annotation with Spring

I am trying to use #Async annotation provided by spring. Going through some of the blogs I found there are the following constraints for using it:
It must be applied to public methods only
Self-invocation – calling the async method from within the same class – won’t work
I have a method which is getting called from the same class which I want to mark #Async. Is there any way of achieving it from the same class?
In Spring v4.3+ you can use self injection, and call the method on the self injected reference.
So for example:
#Component
public class SomeClass {
#Autowired
private SomeClass selfInjected;
public void someMethod() {
selfInjected.someOtherMethod();
}
#Async
public void someOtherMethod(){
...;
}
}
Updated as OP is using version before 4.3:
This will work for you.
#Component
public class SomeClass {
#Autowired
private ApplicationContext applicationContext;
private SomeClass selfInjected;
#PostConstruct
private void init() {
selfInjected = applicationContext.getBean(SomeClass.class);
}
}
Or
The other option is to extract the method to separate class and autowire it. I would personally explore this option before doing the above method.

Transactions with Spring and JPA

I am learning to use JPA. And I'm a little confused.
According JPA EntityManager manages transactions. But a design pattern is to inject the EntityManager in DAOs. So how is possible that are different EntityManager to the same transaction?
This is the case I want to solve
I have the DAOs defined
#Repository
JPARepository1 {
#PersistenceContext
protected EntityManager em;
....
.
#Repository
JPARepository2 {
#PersistenceContext
protected EntityManager em;
....
I have a Service
#Service
public class ServiceImpl1 {
#Autowired
private JPARepository1 repo1;
#Autowired
private JPARepository2 repo2;
public void mainMethod(){
Object o= transactionalMethod1();
try{
transactionalMethod2(o);
}catch (Exception e){
transactionalMethod3(o);
}
}
private Object transactionalMethod1(){
....
}
private void transactionalMethod2(Object o){
....
}
private void transactionalMethod3(Object o){
....
}
Then from #Controller I will invoke mainMethod().
What would be the right way to do transactional to transactionalMethod1, transactionalMethod2 and transactionalMethod3,within the same Service and using the same Repository's.
I would like it if there is an exeption in transactionalMethod2, this abort the transaction, but keep the transactions of transactionalMethod1 and transactionalMethod3
Thanks, sorry for my English
Usually you configure one EntityManager, so the wired manager is always the same, the one you configured. The instance of this manager though, is different in every wiring.
So, every transaction in your service uses a different instance of the EntityManager and thus every transaction invoked is seperated from each other.
As so, an exception in transactionalMethod2 doesn't necessarily affects the transactionalMethod1 and transactionalMethod3
What would be the right way to do transactional to transactionalMethod1, transactionalMethod2 and transactionalMethod3,within the same Service and using the same Repository's.
Now, you have two options to do service methods transactions
1) You could annotate your whole #Service like that:
#Service
#Transactional
public class ServiceImpl1 {
....
so every method declared here is also a transaction.
2) You could annotate each method as #Transactional:
#Transactional
private Object transactionalMethod1(){
....
}
#Transactional
private void transactionalMethod2(Object o){
....
}
#Transactional
private void transactionalMethod3(Object o){
....
}
If you want to use a single repository just #Autowired a single one and use it in your #Transactional method. E.g:
#Service
#Transactional
public class ServiceImpl1 {
#Autowired
private JPARepository1 repo1;
public void mainMethod(){
Object o= transactionalMethod1();
try{
transactionalMethod2(o);
}catch (Exception e){
transactionalMethod3(o);
}
}
private Object transactionalMethod1(){
return repo1.findOne();
}
private void transactionalMethod2(Object o){
repo1.create(o);
}
private void transactionalMethod3(Object o){
repo1.delete(o)
}

Categories