Spring transactions for checked exceptions - spring-data

I'm struggling with a method in a Spring Service component which should be transactional. The method calls two other methods defined in the same class, both of them persisting data using Spring repositories extending JpaRepository (and therefore may throw unchecked exceptions), but one method (methodB) is also doing some filesystem operations which may throw checked exceptions. What is the correct to make this method transactional? So far I have this:
#Service
public class MyServiceImpl implements MyService {
#Transactional
public void doStuff() {
methodA();
try {
methodB();
} catch (Throwable t) {
throw new RuntimeException();
}
}
public void methodB() throws MyCheckedExcpetion() {
// some stuff
}
public void methodA() {
// some stuff
}
}
Is there a better way?

Related

Spring Boot Hibernate Postgresql #Transactional does not rollback [duplicate]

I want to read text data fixtures (CSV files) at the start on my application and put it in my database.
For that, I have created a PopulationService with an initialization method (#PostConstruct annotation).
I also want them to be executed in a single transaction, and hence I added #Transactional on the same method.
However, the #Transactional seems to be ignored :
The transaction is started / stopped at my low level DAO methods.
Do I need to manage the transaction manually then ?
Quote from legacy (closed) Spring forum:
In the #PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.
So if you would like something in your #PostConstruct to be executed within transaction you have to do something like this:
#Service("something")
public class Something {
#Autowired
#Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
#PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
I think #PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.
However you can use the spring event system to receive an event when the initialization of the application context is finished:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
See the documentation section Standard and Custom Events for more details.
As an update, from Spring 4.2 the #EventListener annotation allows a cleaner implementation:
#Service
public class InitService {
#Autowired
MyDAO myDAO;
#EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
#Transactional
public void initialize() {
// use the DAO
}
}
Inject self and call through it the #Transactional method
public class AccountService {
#Autowired
private AccountService self;
#Transactional
public void resetAllAccounts(){
//...
}
#PostConstruct
private void init(){
self.resetAllAccounts();
}
}
For older Spring versions which do not support self-injection, inject BeanFactory and get self as beanFactory.getBean(AccountService.class)
EDIT
It looks like that since this solution has been posted 1.5 years ago developers are still under impression that if a method,
annotated with #Transactional, is called from a #PostContruct-annotated method invoked upon the Bean initialization, it won't be actually executed inside of Spring Transaction, and awkward (obsolete?) solutions get discussed and accepted instead of this very simple and straightforward one and the latter even gets downvoted.
The Doubting Thomases :) are welcome to check out an example Spring Boot application at GitHub which implements the described above solution.
What actually causes, IMHO, the confusion: the call to #Transactional method should be done through a proxied version of a Bean where such method is defined.
When a #Transactional method is called from another Bean, that another Bean usually injects this one and invokes its proxied (e.g. through #Autowired) version of it, and everything is fine.
When a #Transactional method is called from the same Bean directly, through usual Java call, the Spring AOP/Proxy machinery is not involved and the method is not executed inside of Transaction.
When, as in the suggested solution, a #Transactional method is called from the same Bean through self-injected proxy (self field), the situation is basically equivalent to a case 1.
#Platon Serbin's answer didn't work for me. So I kept searching and found the following answer that saved my life. :D
The answer is here No Session Hibernate in #PostConstruct, which I took the liberty to transcribe:
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
The transaction part of spring might not be initialized completely at #PostConstruct.
Use a listener to the ContextRefreshedEvent event to ensure, that transactions are available:
#Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
#Transactional // <= ensure transaction!
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
Using transactionOperations.execute() in #PostConstruct or in #NoTransaction method both works
#Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
#Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
#Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
#PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
#NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}

Repository autowired returning null

I want to make a application with Spring boot on back-end and Swing on front-end. I need to use a repository autowired on my service class, how to make it when i need to instantiate the service class?
I already tried change the repository to BeanFactory, tried to change the location of the files but i can't escape! I need to instantiate the Service class and autowired doesn't work with this.
I have a Model called Permission.
Repository (PermissionRepository.java):
#Repository
public interface PermissionRepository extends JpaRepository<Permission, Long>{
Optional<Permission> findByDescription(String description);
}
Service (PermissionService.java):
#Autowired
BeanFactory beanFactory;
public List<Permission> loadAll() {
return this.beanFactory.getBean(PermissionRepository.class).findAll();
}
Use (BasicOperations.java):
public static void initialPermission() {
try {
if (new PermissionService().loadAll().isEmpty()) {
//logics
}
} catch(Exception e) {
...
}
}
I expect a List, java.util.List but, the error is a nullPointer on my autowired repository
Stack:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.riTrap.service.PermissionService.loadAll(PermissionService.java:20)
You should use autowired instead of new PermissionService. You use loadAll to check if the database contains elements. If the database is big, this approach can damage your application. Suggestion : use count instead
#Service
public class PermissionService {
#Autowired
PermissionRepository permissionRepository;
public List<Permission> loadAll() {
return permissionRepository.findAll();
}
public boolean isEmpty() {
return permissionRepository.count() == 0L;
}
}
If you need to initialize the bean before usage, you can use the constructor :
#Service
public class BasicOperations {
#Autowired
public BasicOperations(PermissionService permissionService){
if(permissionService.isEmpty()){
//DO STUFF
}
}
}

How to catch ConstraintViolationExceptions in a REST Method

Before marking this as a duplicate: I read here and there that an ExceptionMapper will solve my problem, but for some reason it does not catch the ConstraintViolationException.
Update
The problem is solved: Using a separate, more specific ExceptionMapper works (one that implements ExceptionMapper< ConstraintViolationException >). But I don't fully understand why a more general exception mapper (one that implements ExceptionMapper< Exception >) does NOT catch my ConstraintViolationException.
Original question:
I am introducing bean validation to my REST Methods:
#PUT
public Response updateUser(#NotNull #Valid UserUpdateDTO userUpdateDTO) {
return ResponseUtil.ok(userService.updateUser(userUpdateDTO));
}
When a validation fails, I get a 400 response:
[PARAMETER]
[updateUser.arg0.initials]
[Initials must be between 3 and 5]
[AD]
I would like to catch the ConstraintViolationException before the response is sent because I have my own ResponseFactory.
Here is my ExceptionMapper (that works with my other exceptions!)
#Provider
public class ApiExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception e) {
Throwable cause = (e instanceof EJBException) && e.getCause() != null ? e.getCause() : e;
if (cause instanceof BadRequestException) {
logger.error("BadRequest", cause);
return ResponseUtil.badRequest(cause.getMessage());
} else if (cause instanceof ForbiddenException) {
logger.error("Forbidden", cause);
return ResponseUtil.forbidden(cause.getMessage());
} else if (cause instanceof ServerException) {
logger.error("ServerException", cause);
return ResponseUtil.serverError(cause.getMessage());
} else if (cause instanceof ConstraintViolationException) {
return ResponseUtil.badRequest("Validation failed");
}
// Default
logger.error("unexpected exception while processing request", cause);
return ResponseUtil.serverError(cause);
}
}
The ExceptionMapper is not even called when a validation problem occurs, and I get the default 400 error right away.
What am I doing wrong ? I suspect that it has something to do with the fact that the exception is not thrown within the method's body, but rather in its signature.
I am using Wildfly 11 RC and its default validation
Given a Rest Service such as:
#Stateless
#Path("/people")
public class PersonService {
#PersistenceContext(name = "people")
private EntityManager em;
#POST
#Path("/")
#Consumes(APPLICATION_JSON)
public Response create(#Valid Person person) throws DuplicateKeyException {
em.persist(person);
return Response.created(UriBuilder.fromResource(PersonService.class)
.path(PersonService.class, "getPerson")
.resolveTemplate("id", person.getId()).build())
.build();
}
}
then the following ExceptionMapper works just fine by itself:
#Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException>{
#Inject
private Logger logger;
private static class ConstraintViolationBean {
private final String propertyName;
private final String message;
private final String invalidValue;
private ConstraintViolationBean(ConstraintViolation constraintViolation) {
final StringBuilder propertyPath = new StringBuilder();
for (Path.Node node: constraintViolation.getPropertyPath()) {
if (propertyPath.length() > 0) {
propertyPath.append('.');
}
propertyPath.append(node.getName());
}
this.propertyName = propertyPath.toString();
this.message = constraintViolation.getMessage();
this.invalidValue = constraintViolation.getInvalidValue().toString();
}
public String getPropertyName() {
return propertyName;
}
public String getMessage() {
return message;
}
public String getInvalidValue() {
return invalidValue;
}
}
#Override
public Response toResponse(ConstraintViolationException exception) {
logger.log(Level.WARNING, "Constraint violation: {}", exception.getMessage());
List<ConstraintViolationBean> messages = new ArrayList<>();
for (ConstraintViolation cv : exception.getConstraintViolations()) {
messages.add(new ConstraintViolationBean(cv));
}
return Response.status(Response.Status.BAD_REQUEST)
.entity(messages)
.build();
}
}
This is real working (not production) code that I have been messing with for fun. There is also an ExceptionMapper for the DuplicateKeyException.
You can find the source on github at jaxrs-people, which is essentially an experiment.
One thing I have noticed is that EJBExceptions seem to be unwrapped before the ExceptionMapper is selected and invoked.
Update:
Now, if I add the following implementation of ExceptionMapper<Exception> to the deployment, then this one is invoked and the remaining exception mappers are ignored.
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception exception) {
return Response.status(Response.Status.NOT_ACCEPTABLE)
.build();
}
}
Therefore it seems that because your ApiExceptionMapper is actually catching everything and your other ExceptionMappers will be ignored.
It looks like you need to either implement a separate ExceptionMapper for each of BadRequestException, ForbiddenException and ServerException, or some common ancestor class that is not Exception or RuntimeException.
I think that separate implementations would be better because code without if statements is easier to unit test.
What the Spec says:
§4.4 of "JAX-RS: Java™ API for RESTful Web Services (v2.0)" contains the statement:
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
This behaviour corresponds with what we have experienced here.

Jax-rs, JavaEE7, Java8: There was no object available for injection at SystemInjecteeImpl

I'm a software engineer at a medium sized software company and working on a rest framework for quiet a while. Their is that cool stuff about the default methods in Java8 interfaces. So I decided to use this default behavior to implement the CRUD functionality, each separated in it's own interface (ICreateResource). The logic which is executed in the CRUD methods is provided by an extra class which is also declared as an interface IResourceStateControl.
So here is the problem. When injecting RatingResourceStateControl which implements IResourceStateControl into RatingResourceStateBean which implements ICreateResource the exception "There was no object available for injection at SystemInjecteeImpl" is raised when doing a request.
public interface ICreateResource
{
IResourceStateControl getResourceStateControl();
#POST
#Consumes(APPLICATION_JSON)
#Produces(COLLECTION_JSON)
default Response post(#Context UriInfo uriInfo, ApplicationState representation)
{
try
{
Collection collection = getResourceStateControl().post(uriInfo, representation);
return Response.created(collection.getHref().get()).entity(collection).build();
}
catch (Exception exception)
{
throw new WebApplicationException(exception.getMessage(), exception, Response.Status.INTERNAL_SERVER_ERROR);
}
}
}
#Dependent
public class RatingResourceStateControl implements IResourceStateControl
{
#Override
public Collection get(UriInfo uriInfo, int start, int size, long parentResourceId)
{
// TODO Auto-generated method stub
return null;
}
#Override
public Collection get(UriInfo uriInfo, long id)
{
// TODO Auto-generated method stub
return null;
}
#Override
public Collection post(UriInfo uriInfo, ApplicationState representation)
{
// TODO Auto-generated method stub
return null;
}
#Override
public Boolean delete(long id)
{
// TODO Auto-generated method stub
return null;
}
#Override
public Collection put(UriInfo uriInfo, long id, ApplicationState representation)
{
// TODO Auto-generated method stub
return null;
}
#Override
public Collection patch(UriInfo uriInfo, long id, ApplicationState representation)
{
// TODO Auto-generated method stub
return null;
}
}
#Stateless
#Path("/" + RATINGS_PATH)
public class RatingResourceStateBean implements ICreateResource
{
#Inject
private RatingResourceStateControl ratingResourceControl;
#Override
public IResourceStateControl getResourceStateControl()
{
return ratingResourceControl;
}
}
But everything works when using an abstract class for providing the functionality.
public abstract class AbstractResourceState
{
protected abstract IResourceStateControl getResourceStateControl();
#Context
private UriInfo uriInfo;
#Context
private HttpServletRequest httpServletRequest;
#POST
#Consumes(APPLICATION_JSON)
#Produces(COLLECTION_JSON)
public Response post(ApplicationState representation)
{
try
{
Collection collection = getResourceStateControl().post(uriInfo, representation);
return Response.created(collection.getHref().get()).entity(collection).build();
}
catch (Exception exception)
{
throw new WebApplicationException(exception.getMessage(), exception, Response.Status.INTERNAL_SERVER_ERROR);
}
}
}
#Stateless
#Path("/" + RATINGS_PATH)
public class RatingResourceStateBean extends AbstractResourceState
{
#Inject
private RatingResourceStateControl ratingResourceControl;
#Override
protected IResourceStateControl getResourceStateControl()
{
return ratingResourceControl;
}
}
The api is working with the abstract class approach, but it would be very nice to have the control what CRUD method is available by simply implement the appropriate interface. Everything is deployed on a payara 4.1.1 app server.
Best Regards
Rudi
You are trying to inject a JAX-RS resourse #Context UriInfo uriInfo into ICreateResource.post, which is a default method. Since JAX-RS API is based on Java 7, you need to be careful how you use default methods, because the specification doesn't say anything about them. It all depends on how the reflection API exposes the information about default methods.
The problem may also be that you are overriding the default method, but you didn't copy the #Context annotation. When the server scans the class, it will not see any #Context annotation in the method signature (as if the default method didn't exist).

jpa #postpersist #postupdate only after transaction commit

I'm inserting/updating 3 tables while using manual transaction. I want to insert into an history table right after the transaction was committed. that means that there were 3 em.persist actions (for each table), and only after committing them I would like to call a method.
This is my code:
#Resource
private EJBContext context;
public void save(Object obj)
{
try
{
transaction = context.getUserTransaction();
transaction.begin();
em.persist(obj);
sb2.save(obj); //persist in sb2
sb3.save(obj); //persist in sb2
transaction.commit();
}
catch (Exception exp)
{
transaction.rollback();
}
}
Is there any kind of post commit method?
How can I call a method after the commit, and not after the persist?
You could use TransactionSynchronizationRegistry to register a callback to be executed after the transaction completes:
public class MyListener {
// if you can't inject it with #Resource, you'll have to fetch it from JNDI
#Resource
private javax.transaction.TransactionSynchronizationRegistry registry;
#PostUpdate
public void postUpdate(final Object entity) {
registry.registerInterposedSynchronization(new Synchronization() {
public void beforeCompletion() {}
public void afterCompletion(int status) {
if (status == javax.transaction.Status.STATUS_COMMITTED) {
// Do something with your entity
}
}
});
}
}
Then annotate your entities with #EntityListeners(MyListener.class) (or add it in your orm.xml).
If you are in a JPA 2.2 + CDI + JTA environment, you could leverage CDI events which can be observed at a specified transaction phase.
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface EntityEvent {}
public class MyListener {
#Inject
#EntityEvent
private Event<Object> event;
#PostUpdate
public void postUpdate(Object entity) {
event.fire(entity);
}
}
public class MyObserver {
public void afterCommit(#Observes(during = TransactionPhase.AFTER_SUCCESS) #EntityEvent Object entity) {
// do something...
}
}
#EntityListeners(MyListener.class)
public class MyEntity {}
JPA does not provide such events, but EclipseLink provides extended events through the SessionEventListener API.
http://eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_session_event_listener.htm
You could also consider the container JTA events.