Spring cache #Cacheable method ignored when called from within the same class - spring

I'm trying to call a #Cacheable method from within the same class:
#Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
return getSession().getPerson(id);
}
public List<Person> findPersons(int[] ids) {
List<Person> list = new ArrayList<Person>();
for (int id : ids) {
list.add(findPerson(id));
}
return list;
}
and hoping that the results from findPersons are cached as well, but the #Cacheable annotation is ignored, and findPerson method got executed everytime.
Am I doing something wrong here, or this is intended?

This is because of the way proxies are created for handling caching, transaction related functionality in Spring. This is a very good reference of how Spring handles it - Transactions, Caching and AOP: understanding proxy usage in Spring
In short, a self call bypasses the dynamic proxy and any cross cutting concern like caching, transaction etc which is part of the dynamic proxies logic is also bypassed.
The fix is to use AspectJ compile time or load time weaving.

Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
#Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
#Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
return getSession().getPerson(id);
}
public List<Person> findPersons(int[] ids) {
List<Person> list = new ArrayList<Person>();
for (int id : ids) {
list.add(_personDao.findPerson(id));
}
return list;
}
}

For anyone using the Grails Spring Cache plugin, a workaround is described in the documentation. I had this issue on a grails app, but unfortunately the accepted answer seems to be unusable for Grails. The solution is ugly, IMHO, but it works.
The example code demonstrates it well:
class ExampleService {
def grailsApplication
def nonCachedMethod() {
grailsApplication.mainContext.exampleService.cachedMethod()
}
#Cacheable('cachedMethodCache')
def cachedMethod() {
// do some expensive stuff
}
}
Simply replace exampleService.cachedMethod() with your own service and method.

Related

How to use #Autowired in an class annotated with #Entity?

I have an entity called TimeBooking. When I request this entity and return to the client I want to get a list of ActivityTimeBookings from a repository. But when the function get called the repo is null.
So I tried to #Autowired the repo and marked it as transient and also said Spring that there is a dependency which should be injected.
#Configurable(preConstruction = true)
#Entity
public class TimeBooking extends BaseEntity{
#Autowired
private transient ActivityTimeBookingRepository activityTimeBookingRepository;
...
#JsonProperty("activityTimeBookings")
private List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Any suggestions?
Using #Autowired in a class annotated with #Entity is a bad practice.
The solution is given below :
1. Create a service interface :
public interface TimeBookingService {
public List<ActivityTimeBooking> activityTimeBookings();
}
2. Create an implementation of the service interface :
#Service
public class TimeBookingServiceImpl implements TimeBookingService {
#Autowired
private ActivityTimeBookingRepository activityTimeBookingRepository;
public List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Usually its indeed a bad practice to inject something into JPA entities.
These are usually created by JPA implementation (like Hibernate) and spring as a DI framework doesn't really participate in this process.
Note, that there can be many instances of this class created as a result of query, so if you later use this for serialization of the list of this object you might end up running N queries to the database given N entities like this were retrieved.
Answering your question about "getting access to the repo" I believe you should consider refactoring:
In the service class (assuming you have a "regular" contoller, service and dao):
you can:
class MyService {
SomeResult.. doSomething() {
List<TimeBooking> allTimeBookings = dao.getAllTimeBooking();
LocalDateTime timeFrom = calculateTimeFrom(allTimeBookings);
List<ActivityTimeBooking> allActivityTimeBookings = dao.findByDate(timeFrom);
return calculateResults(allTimeBookings, allActivityTimeBooking);
}
}
class MyDao {
List<ActivityTimeBooking> findByDate(LocalDateTime timeFrom) {...}
List<TimeBooking> getAllTimeBookings() {...}
}
Regarding the service implementation, I've assumed this use case can't be covered by usual "JOIN between two tables" so that that creating an association between TimeBooking and ActivityTimeBooking is not an option.
Note 2, I've used one repository (dao) for brevity, in real application you might want to inject two different repositories into the service.

Spring - Injection of beans using Builder pattern

Context
An application that utilizes Spring 4.1.7. All configurations are in XML files (not using annotations) and I rather keep it that way (but I can change the ways things are done if I must).
Problem
I have created a new class that comes with a builder class.
Now I'd like to inject other beans into this new class. I can probably use lookup-methods and similar solutions to do that and then use the new class's builder in the caller beans to create an instance. However, I rather an instance of this new class to be injected to its caller beans then they creating one through the builder. This is where I'm not sure how I can do that. For example, this looks like an Abstract Factory to me, but I don't know how I can pass those parameters (which are passed to the builder) at runtime to the Abstract Factory and subsequently the factories it builds.
Some code snippets to make the question clearer:
public final class Processor {
private final StatusEnum newStatus;
private final Long timeOut;
// I'd like this to be be injected by Spring through its setter (below)
private DaoBean daoInstance;
private Processor() {
this.newStatus = null;
this.timeOut = null;
}
private Processor(Builder builder) {
this.newStatus = builder.getNewStatus();
this.timeOut = builder.getTimeOut();
}
// To be called by Spring
public void setDaoInstance(DaoBean instance) {
this.daoInstance = instance;
}
public void updateDatabase() {
daoInstance.update(newStatus, timeOut);
}
// Builder class
public static final class Builder {
private StatusEnum newStatus;
private Long timeOut;
// lots of other fields
public Long getTimeOut() {
return this.timeOut;
}
public StatusEnum getNewStatus() {
return this.newStatus;
}
public Builder withTimeOut(Long timeOut) {
this.timeOut = timeOut;
return this;
}
public Builder withNewStatus(StatusEnum newStatus) {
this.newStatus = newStatus;
return this;
}
public Processor build() {
return new Processor(this);
}
}
}
I'd like an instance of "DaoBean" to be injected to the "Processor" class. But to do that, Processor will have to be a bean or otherwise I have to utilize something like lookup-methods. On the other hand, wherever I want to use processor, I have to do something like this:
new Processor.Builder()
.withTimeOut(1000L)
.withNewStatus(StatusEnum.UPDATED)
.build()
.updateDatabase();
Instead of this, I wonder if I can make the Processor a bean that Spring can inject to its callers whilst maintaining its immutability. An instance of DaoBean can then be injected to the Processor by Spring. That way I'd be able to segregate the wiring code and the business logic.
It's worth mentioning that the Builder has a lot more than 2 fields and not all of them have to be set. This is why I thought an abstract factory is the way to go (building instances of the Processor in different ways).
One solution, while keeping the builder, would probably be to simply making the Builder itself a Spring bean...
This allows something like this..
#Autowired
private Builder builder;
public void someMethod() {
Result = builder.withX(...).doSomething();
}
This way, your Result object is immutable, can be created via a nice builder and the builder can inject the Spring bean (dao, in your case) into it without anyone even noticing that it's there.
And the only thing that changes is, that you don't create the builder yourself, but let Spring create it for you...
#Component
#Scope("prototype") // normally a good idea
public static class Builder {
#Autowired
private DaoBean dao;
// your logic here
}
(Same works with JavaConfig or XML config, if you don't want to scan.)
Especially with many combinations, I prefer a builder pattern, since a factory would need complex method signatures. Of course, the builder has the disadvantage that you cannot check at compile time if a given combination of attribute types is at least theoretically acceptable. Ok, you could simulate that with various builders, but that would probably be overkill.

Spring boot caching in #Service class does not work

I have problems with save some values in #Service method.
My code:
#Service(value = "SettingsService")
public class SettingsService {
...
public String getGlobalSettingsValue(Settings setting) {
getTotalEhCacheSize();
if(!setting.getGlobal()){
throw new IllegalStateException(setting.name() + " is not global setting");
}
GlobalSettings globalSettings = globalSettingsRepository.findBySetting(setting);
if(globalSettings != null)
return globalSettings.getValue();
else
return getGlobalEnumValue(setting)
}
#Cacheable(value = "noTimeCache", key = "#setting.name()")
public String getGlobalEnumValue(Settings setting) {
return Settings.valueOf(setting.name()).getDefaultValue();
}
My repository class:
#Repository
public interface GlobalSettingsRepository extends CrudRepository<GlobalSettings, Settings> {
#Cacheable(value = "noTimeCache", key = "#setting.name()", unless="#result == null")
GlobalSettings findBySetting(Settings setting);
It should work like this:
get value form DB if data exist,
if not save value from enum.
but it didn't save any data from DB or enum.
My cache config:
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public EhCacheCacheManager cacheManager(CacheManager cm) {
return new EhCacheCacheManager(cm);
}
#Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
}
I have some example to make sure that cache is working in my project in rest method:
#RequestMapping(value = "/system/status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> systemStatus() {
Object[] list = userPuzzleRepository.getAverageResponseByDateBetween(startDate, endDate);
...
}
public interface UserPuzzleRepository extends CrudRepository<UserPuzzle, Long> {
#Cacheable(value = "averageTimeAnswer", key = "#startDate")
#Query("select AVG(case when up.status='SUCCESS' OR up.status='FAILURE' OR up.status='TO_CHECK' then up.solvedTime else null end) from UserPuzzle up where up.solvedDate BETWEEN ?1 AND ?2")
Object[] getAverageResponseByDateBetween(Timestamp startDate, Timestamp endDate);
and it's work well.
What am I doing wwrong?
You have two methods in your SettingsService, one that is cached (getGlobalEnumValue(...)) and another one that isn't cached, but calls the other method (getGlobalSettingsValue(...)).
The way the Spring cache abstraction works however is by proxying your class (using Spring AOP). However, calls to methods within the same class will not call the proxied logic, but the direct business logic beneath. This means caching does not work if you're calling methods in the same bean.
So, if you're calling getGlobalSettingsValue(), it will not populate, nor use the cache when that method calls getGlobalEnumValue(...).
The possible solutions are:
Not calling another method in the same class when using proxies
Caching the other method as well
Using AspectJ rather than Spring AOP, which weaves the code directly into the byte code at compile time, rather than proxying the class. You can switch the mode by setting the #EnableCaching(mode = AdviceMode.ASPECTJ). However, you'll have to set up load time weaving as well.
Autowire the service into your service, and use that service rather than calling the method directly. By autowiring the service, you inject the proxy into your service.
The problem is in the place you call your cacheable method from. When you call your #Cacheable method from same class, you just call it from this reference, which means it doesn't wrapped by Spring's proxy, so Spring can't catch your invocation to handle it.
One on ways to solve this problem is to #Autowired service to itself and just call methods you expected spring have to handle by this reference:
#Service(value = "SettingsService")
public class SettingsService {
//...
#Autowired
private SettingsService settingsService;
//...
public String getGlobalSettingsValue(Settings setting) {
// ...
return settingsSerive.getGlobalEnumValue(setting)
//-----------------------^Look Here
}
#Cacheable(value = "noTimeCache", key = "#setting.name()")
public String getGlobalEnumValue(Settings setting) {
return Settings.valueOf(setting.name()).getDefaultValue();
}
}
But if you have such problems it means your classes are take on too much and aren't comply with the principle of "single class - single responsibility". The better solution would be to move method with #Cacheable to dedicated class.

Spring Boot equivalent to XML multi-database configuration

I would like to port two projects to Spring Boot 1.1.6. The are each part of a larger project. They both need to make SQL connections to 1 of 7 production databases per web request based region. One of them persists configuration setting to a Mongo database. They are both functional at the moment but the SQL configuration is XML based and the Mongo is application.properties based. I'd like to move to either xml or annotation before release to simplify maintenance.
This is my first try at this forum, I may need some guidance in that arena as well. I put the multi-database tag on there. Most of those deal with two connections open at a time. Only one here and only the URL changes. Schema and the rest are the same.
In XML Fashion ...
#Controller
public class CommonController {
private CommonService CommonService_i;
#RequestMapping(value = "/rest/Practice/{enterprise_id}", method = RequestMethod.GET)
public #ResponseBody List<Map<String, Object>> getPracticeList(#PathVariable("enterprise_id") String enterprise_id){
CommonService_i = new CommonService(enterprise_id);
return CommonService_i.getPracticeList();
}
#Service
public class CommonService {
private ApplicationContext ctx = null;
private JdbcTemplate template = null;
private DataSource datasource = null;
private SimpleJdbcCall jdbcCall = null;
public CommonService(String enterprise_id) {
ctx = new ClassPathXmlApplicationContext("database-beans.xml");
datasource = ctx.getBean(enterprise_id, DataSource.class);
template = new JdbcTemplate(datasource);
}
Each time a request is made, a new instance of the required service is created with the appropriate database connection.
In the spring boot world, I've come across one article that extended TomcatDataSourceConfiguration.
http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html That at least allowed me to create a java configuration class however, I cannot come up with a way to change the prefix for the ConfigurationProperties per request like I am doing with the XML above. I can set up multiple configuration classes but the #Qualifier("00002") in the DAO has to be a static value. //The value for annotation attribute Qualifier.value must be a constant expression
#Configuration
#ConfigurationProperties(prefix = "Region1")
public class DbConfigR1 extends TomcatDataSourceConfiguration {
#Bean(name = "dsRegion1")
public DataSource dataSource() {
return super.dataSource();
}
#Bean(name = "00001")
public JdbcTemplate jdbcTemplate(DataSource dsRegion1) {
return new JdbcTemplate(dsRegion1);
}
}
On the Mongo side, I am able to define variables in the configurationProperties class and, if there is a matching entry in the appropriate application.properties file, it overwrites it with the value in the file. If not, it uses the value in the code. That does not work for the JDBC side. If you define a variable in your config classes, that value is what is used. (yeah.. I know it says mondoUrl)
#ConfigurationProperties(prefix = "spring.mongo")
public class MongoConnectionProperties {
private String mondoURL = "localhost";
public String getMondoURL() {
return mondoURL;
}
public void setMondoURL(String mondoURL) {
this.mondoURL = mondoURL;
}
There was a question anwsered today that got me a little closer. Spring Boot application.properties value not populating The answer showed me how to at least get #Value to function. With that, I can set up a dbConfigProperties class that grabs the #Value. The only issue is that the value grabbed by #Value is only available in when the program first starts. I'm not certain how to use that other than seeing it in the console log when the program starts. What I do know now is that, at some point, in the #Autowired of the dbConfigProperties class, it does return the appropriate value. By the time I want to use it though, it is returning ${spring.datasource.url} instead of the value.
Ok... someone please tell me that #Value is not my only choice. I put the following code in my controller. I'm able to reliably retrieve one value, Yay. I suppose I could hard code each possible property name from my properties file in an argument for this function and populate a class. I'm clearly doing something wrong.
private String url;
//private String propname = "${spring.datasource.url}"; //can't use this
#Value("${spring.datasource.url}")
public void setUrl( String val) {
this.url = val;
System.out.println("==== value ==== " + url);
}
This was awesome... finally some progress. I believe I am giving up on changing ConfigurationProperties and using #Value for that matter. With this guy's answer, I can access the beans created at startup. Y'all were probably wondering why I didn't in the first place... still learning. I'm bumping him up. That saved my bacon. https://stackoverflow.com/a/24595685/4028704
The plan now is to create a JdbcTemplate producing bean for each of the regions like this:
#Configuration
#ConfigurationProperties(prefix = "Region1")
public class DbConfigR1 extends TomcatDataSourceConfiguration {
#Bean(name = "dsRegion1")
public DataSource dataSource() {
return super.dataSource();
}
#Bean(name = "00001")
public JdbcTemplate jdbcTemplate(DataSource dsRegion1) {
return new JdbcTemplate(dsRegion1);
}
}
When I call my service, I'll use something like this:
public AccessBeans(ServletRequest request, String enterprise_id) {
ctx = RequestContextUtils.getWebApplicationContext(request);
template = ctx.getBean(enterprise_id, JdbcTemplate.class);
}
Still open to better ways or insight into foreseeable issues, etc but this way seems to be about equivalent to my current XML based ways. Thoughts?

Retrieving data context changes with Spring Data JPA

In my application, I need to retrieve the lists of new, updated and removed entities per each transaction. Like this:
// useful functionality
#Transactional
public void createNewBlogPost(int userId, String title, String text) {
Post post = new Post();
post.title = title; // "hello"
post.text = text; // "there"
postRepository.save(post);
// more work with JPA repositories here
}
...
// gets called right after createNewBlogPost()
public void onTransaction(UnitOfWork uow) {
List<?> newEntities = uow.getNewEntities();
assertEquals(1, newEntities.size()); // 1 new entity
Object firstNewEntity = newEntities.get(0);
assertTrue(firstNewEntity instanceof Post); // this new entity
// is a Post
Post newPost = (Post)firstNewEntity;
assertEquals("hello", newPost.title);
assertEquals("there", newPost.text);
}
The most relevant thing I managed to find was an audit functionality that Spring provides with annotations like #CreatedBy, #CreatedDate, #LastModifiedBy, #LastModifiedDate. Though it's technically very close, yet it's not exactly what I want to achieve.
Does Spring Data JPA provide a mechanism to retrieve data changes per every single transaction?
Since your use case is Hibernate and JPA specific, you should take a look at Hibernate Envers and Spring Data Envers. They might give you some ideas, but be careful re: the projects themselves, I'm not sure if they're active.
I've spent some time for the research and managed to find a relatively straightforward Hibernate-specific solution. There are basically 2 problems to resolve:
Intercept data change events.
Do it on a per-request basis.
To address p.1, one can use EventListenerRegistry. Here's an example:
#Component
public class HibernateListenersConfigurer {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Autowired
private HibernateListeners hibernateListeners;
#PostConstruct
public void init() {
HibernateEntityManagerFactory hibernateEntityManagerFactory =
(HibernateEntityManagerFactory)entityManagerFactory;
SessionFactoryImpl sessionFactoryImpl =
(SessionFactoryImpl)hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.
getServiceRegistry().
getService(EventListenerRegistry.class);
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, hibernateListeners);
eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, hibernateListeners);
eventListenerRegistry.appendListeners(EventType.PRE_DELETE, hibernateListeners);
}
}
hibernateListeners object gets all these events and can do whatever required to audit them. Here's an example:
#Component
public class HibernateListeners implements
PreInsertEventListener,
PreUpdateEventListener,
PreDeleteEventListener {
#Autowired
private ChangeTracker changeTracker;
#Override
public boolean onPreInsert(PreInsertEvent event) {
// event has a bunch of relevant details
changeTracker.trackChange(event);
return false;
}
...other listeners here...
Then, to address p.2, changeTracker seen above is a request-scoped bean:
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ChangeTracker {
// a sort of "Unit of Work"
private List<Change> changes = new ArrayList<Change>();
public void trackChange(PreInsertEvent event) {
changes.add(makeChangeFromEvent(event));
}
public void handleChanges() {
// Do whatever needed :-)
}
}
Then, there are few options available to finally call handleChanges() once request processing is complete: call it manually, use HandlerInterceptor, use filter, use AOP. HandlerInterceptors and filters, are not as great, because in my case they were getting called after response has already been sent to the client, this caused inconsistency between "business data" and "changes data". I eventually switched to AOP and it seems to work just fine.
Here's a playground: https://github.com/loki2302/spring-change-tracking-experiment

Resources