I've been implementing some business logic/validation with the Spring Data Rest and JPA repositories using repository validators as described in:
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#events.
After digging deeper into the SDR code I noticed that the validators (or more generally, repository listeners) are not invoked within a transaction.
From the source code of org.springframework.data.rest.webmvc.RepositoryEntityController:
private ResponseEntity<ResourceSupport> createAndReturn(Object domainObject, RepositoryInvoker invoker,
PersistentEntityResourceAssembler assembler, boolean returnBody) {
// validation logic is implemented in the listener, no transaction yet
publisher.publishEvent(new BeforeCreateEvent(domainObject));
// invoker calls repository, which is wrapped in the transactional proxy,
// only then transaction begins
Object savedObject = invoker.invokeSave(domainObject);
publisher.publishEvent(new AfterCreateEvent(savedObject));
PersistentEntityResource resource = returnBody ? assembler.toFullResource(savedObject) : null;
HttpHeaders headers = prepareHeaders(resource);
addLocationHeader(headers, assembler, savedObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, headers, resource);
}
As seen in the code, the listeners are not called within a transaction, which could lead to eventual data consistency issues.
Am I missing something? Or the framework simply sets the transactional boundary incorrectly?
In spring data rest the repository method would run its own transaction. I also think that this is problematic in some cases. At least the event handler should be run in the same transaction as the repository method.
There was a similar question here:
Handle spring-data-rest application events within the transaction
Especially this answer provides a workaround that allows you to wrap the whole RepositoryEntityController method in a transaction - I think this is what you need most of the time:
https://stackoverflow.com/a/30713264/5371736
Related
I am working on a fallback procedure for when the connection fail (or another error) occurs. I've created the CacheConfiguration/CacheErrorHandler to handle the errors and log them. The application successfully switches between using the cache and going through the normal process when Redis fails.
However, the way I've implemented cache eviction endpoint (via the #cacheEvict annotation), it is essentially an empty method.
#DeleteMapping(value = "/cache/clear")
#CacheEvict(value = {_values_}, allEntries = true)
public ResponseEntity<String> clearAllCache() {return ResponseEntity.ok("OK"); }
Current CacheErrorHandler
#Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
#Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
LOGGER.warn("Failure evicting from cache: " + cache.getName() + ", exception: " + exception);
}
}
Logger will output the cacheEvictError but the response will send back "OK" to the client.
Is there a way to catch the cache error and send a different response saying that the cache evict failed?
I've tried adding a try-catch to throw an exception inside the endpoint but that went nowhere. Couldn't seem to find any examples online to solve this specific issue.
One thing to keep in mind here is that Spring's #CacheEvict annotation and behavior is called "after" the method (by default) on which the annotation is declared, which in your case is the clearAllCache() Web service method.
Although, you can configure the cache eviction to occur before the (actual) clearAllCache() Web service method is called, like so:
#CacheEvict(cacheNames = { ... }, allEntries = true, beforeInvocation = true)
public ResponseEntity<String> clearAllCache() {
// ...
}
That is, using the beforeInvocation attribute on the #CacheEvict annotation, set to true, the cache eviction (for all entries) will occur before the actual clearAllCache() method is invoked.
NOTE: Logically, if the invocation happens after the clearAllCache() method has already been called, then you really have no way to respond if the cache eviction (or rather, the "clear" operation) was unsuccessful. So you must configure the cache eviction to occur before your Web service method gets invoked, first of all.
Next, you need someway to know that your custom CacheErrorHandler was invoked on an error occurring in your caching provider (e.g. Redis) during eviction (or technically, the Cache.clear() operation in this case, since you evicting "all entries").
Another thing to keep in mind here is that since you appear to be operating in Web environment (e.g. a Servlet container like Tomcat or Jetty, or other) then you need to keep "Thread Safety" in mind since each HTTP request and corresponding Web handler method, like the clearAllCache() method called on HTTP DELETE, will be invoked from a separate Thread (i.e. Thread per (HTTP) Request model).
So, you can solve that problem using a Java ThreadLocal declared inside your custom CacheErrorHandler class to capture the necessary state / information that is needed once the clearAllCache() method is called.
I have wrote one such example test class demonstrating how you could accomplish this. The key to this implementation (solution) is the proper configuration of the cache eviction and the use of the ThreadLocal in the custom CacheErrorHandler.
My test is not specifically configured as a Web-based service (e.g. using Spring Web MVC, or anything like that), but I modeled the test use case after your particular situation. I also made use of Mockito to spy on the Spring caching infrastructure to always throw a RuntimeException anytime a Cache eviction based operation occurs (e.g. evict(key) or clear(), etc).
Of course, there are probably better, more robust ways to implement this solution, but this at least demonstrates that it is possible.
Hopefully, this gives you more ideas.
I have a project running on Spring Boot 1.3.8, Hikari CP 2.6.1 and Hibernate (Spring ORM 4.2.8). The code on service layer looks like this:
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
Hikari configuration has this: spring.datasource.leakDetectionThreshold=2000.
The problem is that external REST service is quite slow and often takes 2+ seconds to respond, as a result we see a lot of java.lang.Exception: Apparent connection leak detected which are nothing else but false negatives, though the problem can be clearly seen: we hold DB connection for the time we executing rest request.
The question would be: how to properly decouple DB and REST stuff? Or how to tell hibernate to release connection in between? So that we return DB connection to pool while waiting for REST response.
I have tried setting hibernate.connection.release_mode=AFTER_TRANSACTION and it kind of helps, at least we do not have connection leak exceptions. The only problem is that our tests started showing this:
2018-04-17 15:48:03.438 WARN 94029 --- [ main] o.s.orm.jpa.vendor.HibernateJpaDialect : JDBC Connection to reset not identical to originally prepared Connection - please make sure to use connection release mode ON_CLOSE (the default) and to run against Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false`
The tests are using injected DAO to insert records in DB and later check them via application API. They are not annotated with #Transactional and the list of listeners looks like this:
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class
})
Any ideas what could be the problem with tests?
In the code
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
I see three tasks here - fetching entity A, connecting to remote service and updating entity A. And all these are in same transaction, so the underlying connection will be held till the method is complete.
So the idea is to split the tasks one and three into separate transactions, there by allowing the connection to be releases before making the call to remote service.
Basically, with spring boot you need to add spring.jpa.open-in-view=false. This will not register OpenEntityManagerInViewInterceptor and thus entityManager (in-turn connection) is not bound to the current thread/request.
Subsequently, split the three tasks into separate methods with #Transactional. This helps us bind the entityManager to the transaction scope and releasing connection at end of transaction method.
NOTE: And do ensure that there isn't any transaction started/in progress before (i.e., caller - like Controller etc) calling these methods. Else the purpose is defeated and these new #Transactional methods will run in the same transaction as before.
So the high-level approach could look like below:
In spring boot application.properties add property spring.jpa.open-in-view=false.
Next you need to split doStuff method into three methods in new service class. Intent is to ensure they use different transactions.
First method with #Transactionalwill call A a = dao.findByWhatever();`.
Second method makes remote call.
Third method with #Transactionalwill call rest of the code with JPA merge or hibernate saveOrUpdate on objecta`.
Now Autowired this new service in your current code and call the 3 methods.
I'm trying to disable a Cacheable function in Spring Boot based on a URL parameter. For example http://myapplication.com/some/request?cache=false
I tried the condition with SpEL but I haven't been able to get it work
#Cacheable(value = "value", keyGenerator = "keygenerator", condition = "#{someComponent.isCacheEnabled()}")
Any suggestions? Thanks in advance!
user4843-
I am assuming your "arbitrary component" (i.e. someComponent) is a Spring managed bean in your Spring ApplicationContext?
If so, then you have not properly referenced this Spring managed bean using the SpEL expression specified on the condition attribute inside your #Cacheable annotation declaration. It should read...
condition="#{#someComponent.isCacheEnabled()}"
Notice the use of the # symbol on someComponent, which is explained in more detail here. And, although the Spring documentation is less than clear on the matter, it does imply the SpEL evaluation context created and used inside Spring's Cache Abstraction when processing the caching annotations uses "the built-in parameters", or SpEL conventions, in addition to the ones listed in Table 36.1 - Cache SpEL available metadata. See examples here.
I would also caution you on your use of some arbitrary component, or Spring managed bean to manage caching state for your entire application like this.
First, if someComponent is a Singleton bean (the default in the Spring container) then this bean would need to be Thread-safe, particularly as its state is changing, possibly and seemingly with every HTTP request, which leads me to...
I am not sure you want a Spring managed bean managing the "cacheable state" of your application service/repository methods if the caching state is conditional on each and every individual HTTP request, particularly since most HTTP containers process each HTTP request in a separate Thread. You are most certainly going to run into race conditions. In this case, I'd rather use an additional cacheable service method parameter to determine the cacheable state of that particular service method invocation, stemming from the HTTP request.
Example:
#Cacheable(value = "value", keyGenerator="keyGenerator" conditional="#enableCaching")
public <return-type> someCacheableServiceMethod(..., boolean enableCaching) {
...
}
In fact, I'd even suggest using more intelligible business-oriented rules to determine the cacheable state of your service methods.
Hope this helps!
-John
While writing interceptor for my application I noticed HandlerInterceptor and WebRequestInterceptor here.
I noticed that HandlerInterceptor's method take HttpServletRequest, HttpServletResponse, Object(handler) and other params
while WebRequestInterceptor take WebRequest (a wrapper of HttpServletRequest).
But I don't know what is the difference between these two interceptors.
Although seeing Spring API I can guess that WebRequestInterceptor can not commit response while HandlerInterceptor can do that. Please correct me here if I am wrong.
With a HandlerInterceptor you can:
Change the HttpServletResponse in preHandle method
Apply filters based on handler object. For example, you can apply some filters based on presence of some annotations on HandlerMethod
Prohibit the execution of the handler by returning false in preHandle method.
HandlerInterceptor can be used for a large field of preprocessing aspects, e.g. for authorization checks, or common handler behavior like locale, theme changes or adding Cache-Control headers. Its main purpose is to allow for factoring out repetitive handler code.
One of the canonical use cases of WebRequestInterceptor is preparing context resources (such as a Hibernate Session) and expose them as request attributes or as thread-local objects. Also, you can modify those context resources after successful handler execution (for example, flushing a Hibernate Session). For example, OpenEntityManagerInViewInterceptor binds a JPA EntityManager to the thread for the entire processing of the request.
Although seeing Spring API I can guess that WebRequestInterceptor can
not commit response while HandlerInterceptor can do that. Please
correct me here if I am wrong.
WebRequestInterceptor interface is deliberately minimalistic to keep the dependencies of generic request interceptors as minimal as feasible. If you need to change the response, you should use HandlerIntercepter or Filters.
I am working on spring4 mvc to introduce in our new web application and currently we are using struts1.x and wanted to use new framework for support html5/ajax request as simple as possible and wanted to use the power of DI and spring webflow support.
Currently in our struts1.x application db transaction management is done at our custom GenericAction which is subclass of Action, In GenericAction we are getting the connection from data source and handover to the subclasses then any exception raised then caught and rollback, otherwise commit such that database atomicity (transaction management )is done at one place. All the module action classes should extends GenericAction such that database connection will be available and performs the module related stuff and after completing connection will be either rollback or commit in GenericAction as said above.
In spring, scope of the Transaction is started with #Transactional annotation and then ends with a method in Service Class since the service class marked as #Transactional. This is not feasible solution for me. I have read several documents related to the spring transaction before starting and below are my questions.
I am using HibernateTransactionManager as transaction manager
Transaction should start from interceptors in case web request or any class (in case of unit testing).
Transaction should ends with the after interceptor is executed in case of web request or any class in case of unit testing.
In case of any exception raised then our HandlerExceptionResolverImpl handler will execute then connection should be rollback.
Any workaround or best practices would be greatly appreciate.
Thanks
Dhorrairaajj
In my opinion, you can achieve this with minimal change on the current application by following what Spring does in its org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(Object, TransactionDefinition). In detail, I think something like following should work:
On the GenericAction, get connection from data source (as you've already done)
Bind the connection via its holder with the current data source:
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
Eventually, commit the transaction as org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DefaultTransactionStatus).
In any case, Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/support/TransactionTemplate.html can help to achieve any programmatic transactional scenario that you want. Just need to ensure that the right transactional resource is bound to the opening transaction.
Hope this helps.
Thanks for you reply. Your post/suggestion drives to do the following
I written an interceptor called TxtManager and below are methods to perform the transaction management.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
{
Session session = sessionFactory.openSession();
SessionHolder hold = new SessionHolder(
session);
TransactionSynchronizationManager.bindResource(sessionFactory, hold);
// passing null would accept the default transaction definition.
status = transactionManager.getTransaction(null);
return true;
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
Exception hanlderEx = (Exception) request
.getAttribute(HandlerExceptionResolverImpl.EXCEPTION_KEY);
if (hanlderEx != null)
{
transactionManager.rollback(status);
}
else
{
transactionManager.commit(status);
}
}
In HandlerExceptionResolverImpl class which responsible for handling things upon exception, put an exception object in request and read the same exception to rollback or commit in afterCompletion method of the interceptor.
In between(preHandler and afterCompletion) we will going to use the standard practices to perform the module related stuff including any other interceptos. Unit testing may not be supported, i will check other alternative for unit testing.
Finally we were able to simulate the existing frame work with your suggestion Thanks
Any suggestion to improves this would be much appreciate !!..
Thanks
Dhorrairaajj