I am building a restful webservice using Jax-RS and EJB3 running on wildfly 8. The system is splited into two parts. A rest module and a persistence module. The rest module consists of a rest servlet with a method, reacheable under "mycontext/rest/do". This method calls the method "do()" of a stateless bean which represents the interface of the persistence module. "do()" calls "persistEntity(Entity e)" of another stateless bean which represents a facade (kind of an interface pattern) of a entity class. The entity I want to persist has a few constraints. If they get violated an exception like "ConstraintViolationException" gets thrown (by the EJB container I think). I try to catch the exception in one of the two stateless beans in the persistence module but this is not working. That means that the code in the catch block is not executed. But catching the exception in the servlet works.
Rest servlet (catching exception works here):
#Path("/rest")
public class AccountRestService {
#Inject
private Service service;
#POST
public void do(Entity entity) {
service.do(entity);
}
}
Bean representing interface of the persistence module (catching exception is ignored):
#Stateless
#LocalBean
public class Service {
#Inject
private EntityFacade facade;
public void do(Entity entity) {
facade.persist(entity);
}
}
Entity Facade (catching exception is ignored):
#Stateless
public class EntityFacade{
#PersistenceContext(unitName = "primary")
private EntityManager em;
public void create(Entity entity){
em.persist(entity); // this line throws exception like ConstraintViolationException
}
}
That's expected. The exception is thrown at flush time. The flush happens at transaction-commit time. And the commit happens after the EJB method has returned successfully.
So, contrary to what you say, it's not the persist() method which throws the exception (the stack trace would confirm that), but the method of the transactional proxy which wraps your EJB and commits the transaction.
There is really nothing you can do with such an exception except
show an error message to the user. In that case, catching the exception in the rest service, or letting a generic exception handler handle it is the right thing to do
retry the transaction. In that case, you should have an intermediate layer (or method) between the rest service method and the EJB, which calls the transactional EJB, catches the exception and retries.
Related
I'm using a Java EE 7 + GlassFish and need to perform some operation against a number of JPA entities from a stateless bean.
#Stateless
public class JobRunner
{
public void do()
{
for (Entity entity:facade.findAll())
{
///do some work against entity
}
}
}
This JobRunner bean is injected into the servlet and I invoke do() method from the web UI.
The issue is that all entities are being changed within one transaction so if one fails everything is rolled back what is not desirable. Is there a way to start and close a new transaction for each entity (i.e. for each iteration of the loop)?
I can write an external client and make a loop there calling a stateless bean for each entity but it's not something that completely works for me as I prefer to keep an app monolithic. Can I somehow manage transactions form inside a container?
Maybe JMS helps? If I implement a doer as message listener and will be sending a message for each entity, will it start a new transaction for each one?
#Stateless
public class JobRunner
{
public void do()
{
for (Entity entity:facade.findAll())
{
sendMessageToRealDoer(entity);
}
}
}
Create another bean, specifying #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW), at method or bean level:
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
#Stateless
public class JobWork {
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doWork(Entity entity) {
// do what you would do in the loop with the Entity
// this runs in a new transaction
}
}
I wish I could tell you that you only need to annotate a method of the same bean (JobRunner) and simply call it. This is not possible (EDIT)without workarounds - check comment from Steve C(/EDIT) because when calling methods of this object in EJBs and CDI beans the interceptors do not get called. Transactions are implemented with interceptors in both cases.
Some notes:
If the total duration of the operations in the loop is expected to be long, you will get a timeout in the outer transaction, that is implicitly started for the JobRunner stateless EJB. You will want to take measure that no "outer" transaction is started.
Sending the data to a queue will work too; but queues will process them asynchronously, meaning that the execution will return to the servlet calling JobRunner.do() most probably before all items have been processed.
I am having a problem with Spring JPA Data and nested transactions. Following are two methods with a nested transaction of my service.
#Service
public UserService {
#Transactional
public User createUser(UserDto userDto) {
....
user = saveUser(user);
sendEmail(user);
....
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public User saveUser(User user) {
return userRepository.save(user);
}
It happens there is one scenario that the method userRepository.save() should throw an exception but somehow is not being thrown, it looks like it is waiting the parent transaction to be finished. I was expecting the exception being thrown on the saveUser method and the sendEmail method not even to be executed.
Because the method UserService.saveUser have the propagation set to Propagation.REQUIRES_NEW I was expecting that transaction to be commited (the SQL statement to be executed) and any exception being propagated.
I did not setup anything related with Transaction, so i believe that the flush mode is set to AUTO.
Can anyone spot what i am doing wrong or what is my misconception?
It's because you're invoking #Transactional method from within same bean.
#Transactional only works on methods invoked on proxies created by spring. It means, that when you create a #Service or other bean, method called from the outside will be transactional. If invoked from within bean, nothing will happen, as it doesn't pass through proxy object.
The easiest solution would be to move the method to another #Service or bean. If you really want to keep it within same component, then you need to invoke it, so that it gets wrapped in proxy by spring AOP. You can do this like that:
private YourClass self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(YourClass.class);
}
Then invoking method on self would result in opening a transaction.
In other words: you are not experiencing any of those anomalies, because #Transactional over saveUser does not work.
I have a REST Spring boot app with Hibernate. For simplicity let's assume this workflow:
Controller handles incoming requests, calls Service methods
Service methods are #Transactional, do some business logic and call Persistence methods
Persistence methods are handled by DAO objects, saving stuff into database.
The database has a unique constraint on username of a User. The way I have it working now is this:
Client sends request to Controller
Controller calls Service
Service attempts to save an object through DAO. If DataViolationException occurs, Service returns a custom Exception
Controller catches the custom Exception and sends back appropriate response
The pseudocode is this:
public class UserController {
#RequestMapping("/user")
public User createUser(...){
try{
return userService.createUser(...);
} catch (UserAlreadyExistsException e){
// Do some processing and return error message to client
}
}
}
public class UserService {
#Transactional
public User createUser(...){
(...)
try{
userDAO.save(newUserObject);
} catch(DataIntegrityViolationException e){
throw new UserAlreadyExistsException(username);
}
}
}
However, this way I am getting an error when a duplicate user is attempted to be created.
javax.persistence.RollbackException: Transaction marked as rollbackOnly
One way to fix this seems to be to let the DataIntegrityViolationException "bubble" up from the transaction (and not catch it in Service). But that means that the Controller has to handle persistence exceptions and I don't like that.
I prefer if the service threw "understandable" exceptions for the Controller to handle. The service knows what persistence exceptions to expect and when and is able to "translate" the broad DataIntegrityViolationException into a meaningful one.
Is there a way to handle the exceptions this way? I don't particularly like the idea of having a "2-layered service layer" to achieve this.
EDIT: Another reason I want to throw my custom Exception is that it is required by the compiler to be caught. I want to enforce the controller to handle all possible exceptions that may occur.
Your repository need to extends JpaRepository, and when you do that. You can use saveAndFlush method from that repository. That mean, you code will be immediately executed on database and exception will be throwed before finish transaction and you will be able to catch it in Catch block. I added also sample for deleting operation.
Repository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDAO extends JpaRepository<User, Long> {
}
Service:
public class UserService {
private UserDAO userDAO;
(...)
#Transactional
public User createUser(...){
(...)
try{
userDAO.saveAndFlush(newUserObject);
} catch(DataIntegrityViolationException e){
throw new UserAlreadyExistsException(username);
}
}
#Transactional
public void deleteUser(...){
(...)
try{
userDAO.delete(deletingUserObject);
userDAO.flush();
} catch(DataIntegrityViolationException e){
throw new UserException(username);
}
}
}
Annotate your service method with
#Transactional(rollbackFor = UserAlreadyExistsException.class)
it will tell spring to not commit the transaction if the Exception is thrown so you will be able to catch it in your controller
I want to rollback my transaction on an exception which works absolutely fine. But now I do not want to rollback all actions I made.
For example, a request to my application gets processed and several database actions on multiple databases are done. If the exception is thrown I want to rollback all actions on one of my databases and only 2 actions on the second database. How can I do that? I always end up rolling bback the whole transaction...
what I tried so far:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public <Param> GenericResponseMsg executeRequest (Param myParam) {
entityManager1.persist(someEntity); // rollback
...
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void addFailedRequestToDatabase() {
entityManager2.persist(otherEntity); // do not rollback
...
}
I also tried to annotate the class with
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
but this results in the following exception:
Internal Exception: java.sql.SQLException: Connection can not be used while enlisted in another transaction
Error Code: 0
Any ideas? I am somehow stuck and don't know what to do anymore...
EDIT:
Here is the workflow you asked for:
#Stateless
#Path("my/path")
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyRessource {
#EJB
private MyEJB myEjb;
#POST
#Path("method/Path")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public GenericResponseMsg doSomeStuff(Param param) throws Exception {
try {
return myEjb.executeRequest(param);
} catch(Throwable throwable) {
myEjb.addFailedRequestToDatabase();
throw throwable;
}
}
}
#TransactionAttribute is support for EJB as per the specification. #Transactional support is coming in tomee 7 (currently under vote #asf).
Side note: tomee and openejb standalone code is 100% the same for JTA support.
We have a simple stateless EJB timer that gets an instance of a spring service injected into it. The spring service has a method marked as transactional. When the EJB uses Transaction Management Type CONTAINER the spring service call results in an IllegalStateException: Operation not allowed. So we set the EJB Transaction Management Type to be BEAN and everything works as advertised. Is this normal? I was under the impression that the spring transaction management would join the CMT.
example EJB
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
#Interceptors(SpringBeanAutowiringInterceptor.class
public class TimerService {
#Autowired
IHelloService helloService;
#Schedule(second="*/1", minute="*",hour="*", persistent=false)
public void doWork(){
helloService.hello();
}
}
example spring service
#Service
public class HelloService implements IHelloService {
#Transactional
public void hello(){
}
}
Probably the problem can be fixed, if you would define propagation level for Spring transaction as SUPPORTS (default is REQUIRED). I do not remember exact value for default settings in EJB, but it seems like EJB method is not starting transaction (propagation is DEFAULT), but 'hello' tries to do so and that is causing conflict.