Heavily nested #Transactional annotations - java

I have inherited a Spring Java codebase where it seems pretty much every method from the business service down to the low level DAOs are tagged with #Transactional. It has some severe performance issues that I noticed are mitigated somewhat when certain annotations are changed from #Transactional(readOnly=false) to #Transactional(readOnly=true). It also seems to have periodic calls to EntityManager.flush() that can't be explained except that certain objects do not get written to the DB without them.
My hunch is that the original developers are misusing/overusing transactions, but I'm not sure of the best approach to clean this up. I would appreciate advice from those more well-versed in Spring Transactions than me.
A reduced example of just one segment of the code follows. There are others much more complex than this with 5-6 levels of nested transactions.
// MVC Controller REST Service
#Controller
#RequestMapping("/service")
public class Group {
#Inject private GroupService groupService;
public #ResponseBody Object update(#RequestBody Group group) {
return groupService.update(group);
}
}
// Business service
#Service
public class GroupService {
#Inject private GroupDAO groupDao;
#Inject private AuditService auditService;
#Transactional(readOnly=false)
public Group update(Group group) {
Group groupToUpdate = groupDao.get(group.getId());
// Do magic
groupDao.persist(groupToUpdate); // Shorthand to call EntityManager.persist()
auditService.logUpdate(group);
return groupToUpdate;
}
}
// DAO
#Repository
public class GroupDAO extends AbstractDAO {
#Transactional(readOnly=true)
public Group get(Long id) {
return entityManager.find(Group.class,id);
}
}
// Auditing service
#Component
public class AuditService {
#Inject AlertDAO alertDao;
#Transactional(readOnly=false)
public void logUpdate(Object o) {
Alert alert = alertDao.getFor(o);
// Do magic
alertDao.update(alert);
alertDao.flush() // Shorthand for EntityManager.flush() but WHY???
}
}
// DAO
#Repository
public class AlertDAO extends AbstractDAO {
#Transactional(readOnly=true)
public Alert getFor(Object forObj) {
// Magic here
return entityManager.find(Alert.class,foundId);
}
#Transactional(readOnly=false)
public void update(Alert a) {
// Magic here
entityManager.merge(a);
}
}

Given that the question is "how to clean up transaction annotations?" the answer would be - based on the above comments;
Do not use transaction annotations in DAOs, only in Services
(#Components)
Make sure DAOs are only called through the
service-layer.

Related

Spring RabbitMQ: implement additional validations without custom annotations

In a Spring RabbitMQ project I am looking for a way to programmatically validate an object that has JSR303 annotations (like #NotNull, #Size, etc) while at the same time requires some custom validation logic. I would normally use a ConstraintValidator in combination with a custom Annotation, but the use of custom Annotations is not an option in this case.
I have the following (simplified) class, which is generated by Swagger and therefore cannot be edited:
#ApiModel(description="User")
public class User {
private String name;
#NotNull
#Size(min = 1, max = 6)
public String getName() {
return this.name;
}
...
}
The additional validation logic is encapsulated in a validator:
#Component
public class UserValidator implements org.springframework.validation.Validator {
#Override
public boolean supports(Class<?> aClass) {
return User.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
User user = (User) o;
...
if(!valid) {
errors.reject("some rejection");
}
}
}
The service in which the validation occurs:
#Service
#RequiredArgsConstructor
public class SomeService {
private final javax.validation.Validator validator; // might as well be org.springframework.validation.Validator if that works better
public void someMethod(User user) {
if (!validator.validate(user).isEmpty()) {
// handle invalid user
}
...
}
}
However, the UserValidator is not being invoked. Is there some way to make Spring aware of the UserValidator? I have read some topics on using an InitBinder, however as this is not a web MVC project but a rabbitMQ project I'm not sure whether this can be used.
It is not clear from your description how this is relevant to Spring AMQP, but if you want to use a validator on the listener method level, you should configure it respectively:
#Configuration
#EnableRabbit
public class Config implements RabbitListenerConfigurer {
...
#Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setValidator(new MyValidator());
}
}
See docs for more info: https://docs.spring.io/spring-amqp/docs/current/reference/html/#rabbit-validation

Spring boot inject bean at runtime based on request endpoint/request param

I have an interface that has two implementations, and I'd like to conditionally inject either of the two implementations in a spring boot service.
The point is that the eligible implementation should be picked up based on the request message (JSON mapped to a POJO).
My searches leaded me to implement a FactoryBean to control selecting between those two implementations, and to keep the factory telling spring that the beans are not singleton (by returning false for the isSingleton method).
But if this is the right way, I am still not sure how to get the request message to check it and return the right bean.
Can you please tell me if I am on the right track for what I am trying to attain?
=============
UPDATE
I do not want to pollute my code and deal with managing the relation between my service and the dependencies' implementation in the service.
Considering that I will need to deal with more implementations in the future, I need my service to care only about its responsibility.
I need my service to have only one reference of the generic interface and deal with it in an abstracted way.
I need to find a spring-based way to choose the right implementation for each request based on a condition that is derived from the request itself, and inject it in the service.
One option is to inject both beans and conditionally pick the required bean. You can autowire classes implementing same interface into a Map.
Following example uses a factory class to hide the conditional check.
#Component("type1")
public class Type1 implements SomeInterface{}
#Component("type2")
public class Type2 implements SomeInterface{}
#Component
public class MyTypeFactory {
#Autowired
private Map<String, SomeInterface> typesMap;
public SomeInterface getInstance(String condition){
return typesMap.get(condition);
}
}
#Component
public class MyService {
#Autowired
private MyTypeFactory factory;
public void method(String input){
factory.getInstance(input).callRequiredMethod();
}
}
You could #Autowire both beans in the controller and decided based on the request which one to return.
Consider the below interface:
public interface MyInterface { ... }
Sample config:
#Configuration
public class MyConfig {
#Bean("first")
public MyInterface firstBean() { ... }
#Bean("second")
public MyInterface secondBean() { ... }
}
Sample controller:
#RestController
public class MyController {
#Autowire
#Qualifier("first")
public MyInterface first;
#Autowire
#Qualifier("second")
public MyInterface second;
#GetMapping
public MyInterface doStuff(#RequestBody body) {
if(shouldReturnFirst(body)){
return first;
} else {
return second;
}
}
}
Note that you should most likely not do it this way though, but have a single service, say MyService that should implement this logic for you.
#Component
public class MyService {
public MyInterface doStuff(body) {
if(shouldReturnFirst(body)){
// build your response here
} else {
// build your response here
}
}
}
And just delegate to the service from the controller
#GetMapping
public MyInterface doStuff(#RequestBody body) {
return myService.doStuff(body);
}
Spring has a concept of Conditional Bean...
Have a look here https://www.intertech.com/Blog/spring-4-conditional-bean-configuration/

TransactionalEventListener ignored if event is published from another listener

i have a service and two TransactionalEventListeners with phase BEFORE_COMMIT, one listens for EventA, the other for EventB. Service publishes EventA -> EventAListener is invoked and publishes another event - EventB. EventBListener is not invoked and the event is ignored.
Example code:
#Service
#Transactional
public class ExampleService {
private ExampleEntityRepository repository;
private ApplicationEventPublisher applicationEventPublisher;
public void exampleMethod() {
repository.save(new ExampleEntity("entity"));
applicationEventPublisher.publishEvent(new EventA(this));
}
}
//==================================================
#Service
#Transactional
public class EventAListener {
private ExampleEntityRepository repository;
private ApplicationEventPublisher applicationEventPublisher;
#TransactionalEventListener(value = EventA.class, phase = TransactionPhase.BEFORE_COMMIT)
public void handle(EventA event) {
repository.save(new ExampleEntity("entityA"));
applicationEventPublisher.publishEvent(new EventB(this));
}
}
//==================================================
#Service
#Transactional
public class EventBListener {
private ExampleEntityRepository repository;
#TransactionalEventListener(value = EventB.class, phase = TransactionPhase.BEFORE_COMMIT)
public void handle(EventB eventB) {
repository.save(new ExampleEntity("entityB"));
}
}
//==================================================
// Alternative EventAListener version
#Service
#Transactional
public class EventAListener {
private ExampleEntityRepository repository;
#TransactionalEventListener(value = EventA.class, phase = TransactionPhase.BEFORE_COMMIT)
public EventB handle(EventA event) {
repository.save(new ExampleEntity("entityA"));
return new EventB(this);
}
}
After service method is executed, there are 2 rows in database - "entity" and "entityA". Alternative EventAListener version works the same way.
Setting EventBListener fallbackExecution to 'true' does not make any change - EventBListener is not invoked.
Changing EventBListener phase to AFTER_COMMIT works - EventB is processed, but in another transaction.
Why is EventB not processed?
I think your problem might be related to the one posted by Wojtek here:
https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2
To which the Spring developer answers as follows:
BEFORE_COMMIT is not "anytime you want during the transaction". It's really before commit. What you're doing is using the transaction like nothing asked for a commit. Something did.
So it seems the chaining of BEFORE_COMMIT events is not possible, and perhaps performing another transactional operation during that phase is not supported (even though it appears to work)?
I think this area could be documented better, because to most people "before commit" simply means "before something was committed", and it isn't really clear what operations can be performed when that event handler is invoked.

Transaction Rollback in Controller if exception occurred in any service

I am newbie to spring and I face problem in Transaction.
I have created two Model as below:
UserDto - Stored user information
RoleDto - Stored user's role information
Service respected to both models are as below (both are annotated with #Transactional):
UserService - void saveUser(UserDto userDto) throws Exception;
RoleService - void saveRole(RoleDto roleDto) throws Exception;
now when user create account in the application at that time I called "add" method of the controller, which has the code snippet as below:
userService.saveUser(userDto);
roleService.saveRole(roleDto);
now in this code if exception occurred in Roleservice than it still insert user data into database table. but I want to rollback that too if roleService throws any exception. I tried to find the solution but could not get any good tutorial. any help will be appreciated.
The way you're calling the method makes each of it has its own transaction. Your code can be understood like this:
Transaction t1 = Spring.createTransaction();
t1.begin();
try {
//your first service method marked as #Transactional
userService.saveUser(userDto);
t1.commit();
} catch (Throwable t) {
t1.rollback();
} finally {
t1.close();
}
Transaction t2 = Spring.createTransaction();
t2.begin();
try {
//your second service method marked as #Transactional
roleService.saveRole(roleDto);
t2.commit();
} catch (Throwable t) {
t2.rollback();
} finally {
t2.close();
}
An option to solve this would be to create another service class where its implementation has RoleService and UserService injected, is marked as #Transactional and calls these two methods. In this way, both methods will share the same transaction used in this class:
public interface UserRoleService {
void saveUser(UserDto userDto, RoleDto roleDto);
}
#Service
#Transactional
public class UserRoleServiceImpl implements UserRoleService {
#Autowired
UserService userService;
#Autowired
RoleService roleService;
#Override
public void saveUser(UserDto userDto, RoleDto roleDto) {
userService.saveUser(userDto);
roleService.saveRole(roleDto);
}
}
A better design would be to make RoleDto a field of USerDto and that implementation of USerService has a RoleService field injected and perform the necessary calls to save each. Note that service classes must provide methods that contains business logic, this also means business logic rules. Service classes are not just wrappers for Dao classes.
This could be an implementation of the above explanation:
#Service
public class UserServiceImpl implements UserService {
#Autowired
RoleService roleService;
public void saveUSer(UserDto userDto) {
//code to save your userDto...
roleService.saveRole(userDto.getRoleDto());
}
}

Spring/Hibernate App only working without #Transactional

I got another problem when working on my current Spring and Hibernate application. I have built my DAO interfaces/classes, as well as my Service interfaces/classes and of course the Entities.
Everything is being deployed well but as soon as I add the #Transactional annotation to my XXXServiceImpl classes, I get the following exception during deployment (tested on Glassfish AND Tomcat):
Caused by: java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy25 implementing net.dreamcode.bleevle.persistence.service.IntranetService,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [net.dreamcode.bleevle.persistence.service.impl.IntranetServiceImpl] for property 'intranetService': no matching editors or conversion strategy found
Of course, I tried to find something about that and I guess it's basically because interface and class are not matching when adding the annotation. But I also tried adding the annotation on my interfaces, which didn't help along to solve the problem, producing the same error as stated above.
Here's some example code from my project (BasicService, UserService and UserServiceImpl):
BasicService (Interface):
public interface BasicService<T> {
T findById(String id);
void create(T entity);
void delete(T entity);
void update(T entity);
}
UserService (Interface):
import net.dreamcode.bleevle.data.User;
public interface UserService extends BasicService<User> {
User findByName(String name);
}
UserServiceImpl (Class):
public class UserServiceImpl implements UserService {
#Autowired
UserDao userDao;
#Override
public User findByName(String name) {
return userDao.findByName(name);
}
#Override
public User findById(String id) {
return userDao.findById(id);
}
#Override
public void create(User entity) {
userDao.create(entity);
}
#Override
public void delete(User entity) {
userDao.delete(entity);
}
#Override
public void update(User entity) {
userDao.update(entity);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Is there a specific thing to do when working with this kind of pattern (I guess it's some kind of design pattern with Service and Dao stuff)?
Any kind of help is greatly appreciated. Thanks in advance!
You have a property
#Autowired private IntranetServiceImpl intranetService;
(or an equivalent thereof, such as an annotated constructor parameter or a setter) whose type is the implementation type of your service. This is wrong: you should always use the interface type for your properties.
The reason why it fails as soon, but no earlier than, you annotate with #Transactional is that this annotation causes Spring to create a dynamic proxy of your interface where otherwise there would be the naked implementation class instance. This dynamic proxy fails to be downcast into your implemantation type, but would be successfully cast into the interface type.

Categories

Resources