How can I update cache with CachePut? - java

My #Cacheable method has next signature:
#Component
public class UpcomingFilter implements Filter<Entity> {
#Cacheable(value = {"upcoming"})
#Override
public List<Entity> filter(int limit) {
//retrieve from repository
}
}
This filter use the reporisoty, take limit as parameter for pagination and return List of Entities.
I'm trying to update cache when add Entity to the system:
#CachePut(value={"upcoming", "popular", "recentlyAdded", "recommendations", "thisWeek", "topRated"})
public Entity addEntity(RequestDto dto, User user) {
//do work, create and save entity to repository
return entity;
}
But after adding new entity to the system it is not updated. Filters returns old values.
I saw examples, where for CachePut and Cacheable the word 'key' is used, but how can I add
#Cacheable(key="#entity.id")
to the Filter signature ?
UPDATE
Tried to add my key:
#CachePut(value={"upcoming","popular", "recentlyAdded", "recommendations", "thisWeek", "topRated"},
key = "#root.target.FILTER_KEY", condition = "#result != null")
public Entity addEntity(RequestDto dto, User user) {
//do work, create and save entity to repository
return entity;
}
and also add key to #Cacheable:
public static final String FILTER_KEY = "filterKey";
#Cacheable(value = {"recentlyAdded"}, key = "#root.target.FILTER_KEY")
#Override
public List<Entity> filter(int limit) {
and than I get
java.lang.ClassCastException: com.java.domain.Entity cannot be cast to
java.util.List

Instead #CachePut the #CacheEvict should be used.
It works for me:
#CacheEvict(value={"upcoming", "popular", "recentlyAdded", "recommendations", "thisWeek", "topRated"},
allEntries = true, condition = "#result != null")
public Entity addEntity(RequestDto dto, User user) {
//do work, create and save entity to repository
return entity;
}

Related

Spring cache repository find one entity by id

I have a data repository DAO class that gets data from DB and it has following methods that my controller/service class calls:
#Override
public Account getOne(final String id) {
this.namedParameterJdbcTemplate.queryForObject(this.BY_ID,
namedParameters,
new Mapper());
}
#Cacheable(value = "accounts")
#Override
public List<Account> getAll() {
return this.namedParameterJdbcTemplate.query(this.ALL, new Mapper());
}
#CacheEvict(value = "accounts", allEntries = true)
public void evictAll() {
}
I am caching the result of an expensive getAll() call.
This is refreshed also by a scheduler calling evictAll().
My question is how can I cache getOne() as it takes a id param.
Should I create a new cache or can use existing one "accounts" with reference to the id. Any ideas with some samples or pointers to examples will be much appreciated.

Spring cache all elements in list separately

I'm trying to add caching to a CRUD app, I started doing something like this:
#Cacheable("users")
List<User> list() {
return userRepository.findAll()
}
#CachePut(value = "users", key = "#user.id")
void create(User user) {
userRepository.create(user)
}
#CachePut(value = "users", key = "#user.id")
void update(User user) {
userRepository.update(user)
}
#CacheEvict(value = "users", key = "#user.id")
void delete(User user) {
userRepository.delete(user)
}
The problem I have is that I would like that create/update/delete operations can update the elements already stored in the cache for the list() operation (note that list() is not pulling from database but an data engine), but I am not able to do it.
I would like to cache all elements returned by list() individually so all other operations can update the cache by using #user.id. Or perhaps, make all operations to update the list already stored in cache.
I read that I could evict the whole cache when it is updated, but I want to avoid something like:
#CacheEvict(value = "users", allEntries=true)
void create(User user) {
userRepository.create(user)
}
Is there any way to create/update/remove values within a cached collection? Or to cache all values from a collection as individual keys?
I'll self answer my question since no one gave any and could help others.
The problem I had when dealing with this issue was a problem of misconception of Cache usage. My need posted on this question was related to how to update members of a cached list (method response). This problem cannot be solved with cache, because the cached value was the list itself and we cannot update a cached value partially.
The way I wanted to tackle this problem is related to a "map" or a distributed map, but I wanted to use the #Cacheable annotation. By using a distributed map would have achieved what I asked in my question without using #Cacheable. So, the returned list could have been updated.
So, I had (wanted) to tackle this problem using #Cacheable from other angle. Anytime the cache needed to update I refreshed it with this code.
I used below code to fix my problem:
#Cacheable("users")
List<User> list() {
return userRepository.findAll()
}
// Refresh cache any time the cache is modified
#CacheEvict(value = "users", allEntries = true")
void create(User user) {
userRepository.create(user)
}
#CacheEvict(value = "users", allEntries = true")
void update(User user) {
userRepository.update(user)
}
#CacheEvict(value = "users", allEntries = true")
void delete(User user) {
userRepository.delete(user)
}
In addition, I have enabled the logging output for spring cache to ensure/learn how the cache is working:
# Log Spring Cache output
logging.level.org.springframework.cache=TRACE
Not Sure if, using Spring's #Cacheable is a hard constraint for you, but this essentially worked for me.
I tried using Spring's RedisTemplate and Redis HasMap data-structure for storing the list elements.
Store a single User:
redisTemplate.opsForHash().put("usersRedisKey" ,user.id,user);
Storing List of Users:
Need to map this with user.id first
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
redisTemplate.opsForHash().putAll("usersRedisKey", userMap);
Get single user from Cache:
redisTemplate.opsForHash().get("usersRedisKey",user.id);
Get list of users:
redisTemplate.opsForHash().multiGet("usersRedisKey", userIds); //userIds is List of ids
Delete user from List:
redisTemplate.opsForHash().delete("usersRedisKey",user.id);
Similarly you could try using other operations from Redis HashMap to update individual objects based on ids.
I understand I am quite late to the party here, but do let me know if this works for you.
Try below given solution:
#Caching(put = #CachePut(cacheNames = "product", key = "#result.id"),
evict = #CacheEvict(cacheNames = "products", allEntries = true))
public Product create(ProductCreateDTO dto) {
return repository.save(mapper.asProduct(dto));
}
#Caching(put = #CachePut(cacheNames = "product", key = "#result.id"),
evict = #CacheEvict(cacheNames = "products", allEntries = true))
public Product update(long id, ProductCreateDTO dto) {
return repository.save(mapper.merge(dto, get(id)));
}
#Caching(evict = {
#CacheEvict(cacheNames = "product", key = "#result.id"),
#CacheEvict(cacheNames = "products", allEntries = true)
})
public void delete(long id) {
repository.delete(get(id));
}
#Cacheable(cacheNames = "product", key = "#id")
public Product get(long id) {
return repository.findById(id).orElseThrow(() -> new RuntimeException("product not found"));
}
#Cacheable(cacheNames = "products", key = "#pageable")
public Page<Product> getAll(Pageable pageable) {
return repository.findAll(pageable);
}

Trouble deleting entity with specific property set in Java GAE

I have the entities created like this:
public String addNewStockName(String newStock) throws DelistedException {
Entity stock = new Entity("Stocks");
stock.setProperty("Name", newStock);
ds.put(stock);
return "OK";
}
Trying delete the specific entity like this:
public String deleteStockName(String stockName){
Key key = KeyFactory.createKey("Stocks", stockName);
ds.delete(key);
return "OK";
}
And it does not delete the entity which has property 'stockName'. Why?
If you want to create an entity that you can fetch by stockName, you need something like
public String addNewStockName(String stockName) throws DelistedException {
Key key = KeyFactory.createKey("Stocks", stockName);
Entity stock = new Entity(key);
stock.setProperty("foo", "bar");
ds.put(stock);
return "OK";
}
You can then use your deleteStockName() method as is. This of course assumes your key name is unique, but it also means you can always fetch the Stock by key, rather than query.
Your Stocks entity has a property named "Name". That is not the same as the key name.
You have to perform a query to get the entities or entity keys matching the filter of "Name=?".
Something like this:
public String deleteStockName(String stockName) {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Filter f = new FilterPredicate("Name", FilterOperator.EQUAL, stockName);
Query q = new Query("Stocks").setFilter(f).setKeysOnly();
List<Entity> results = ds.prepare(q)
.asList(FetchOptions.Builder.withDefaults());
if (results.isEmpty())
return "Not Found!";
ds.delete(results.get(0).getKey());
return "OK";
}

Grails difficultly in using Hibernate OneToMany model

In my new project I am trying to use Hibernate model class, here one user domain class having OneToMany relation userProfile like
class User {
//Some fields and getter setter
//Problematic thing
#javax.persistence.OneToMany(mappedBy = "User")
private Set<UserProfile> userProfiles;
//getter is like
public Set<userProfile> getProfile() {
// the logic
}
public void setProfile() {
// the logic
}
}
So when I try to access this field in grails criteria like
def criteria = User.createCriteria()
List<User> userList = criteria.list() {
userProfiles {
eq("id",1 as long)
}
}
I getting the error like No signature of method: UserService.userProfiles(). I think it might be because of different getter and setter name, 'cause for remaining OneToMany fields the criteria is working fine.
Is there any possible and standard way to address this issue.
This is a more common thing to do:
class User {
static hasMany = [userProfiles: UserProfile]
}
Methods like getUserProfiles(), setUserProfiles(), addToUserProfiles(UserProfile p) etc. are automatically generated. See http://grails.org/doc/latest/ref/Domain%20Classes/hasMany.html.
Then you could do something like this:
def userList = User.withCriteria {
userProfiles {
idEq 1
}
}
I hope that helps.

em.Persist(book) wont update entity

I have a new JPA entity with auto-generated id and I persist it.
After that I want to get it again, modify it and persist the changes.
The new entity gets persisted into the database with an auto-generated id but the entity's bookid remains null.
This way I cannot use the getBook(id) function to find it again.
#Stateless
public class BookManager implements BookManagerRemote {
#PersistenceContext
EntityManager em;
#EJB
Authenticator auth;
public BookManager() {
}
#Override
public void addBook(String user, Konyv konyv) throws InsufficentPrivilegesException {
if (auth.isAdmin(user)) {
em.persist(konyv);
} else {
throw new InsufficentPrivilegesException();
}
}
#Override
public Konyv getBook(String user, Integer bookid) throws InsufficentPrivilegesException {
if (auth.isAdmin(user)) {
return em.find(Konyv.class, bookid);
} else {
throw new InsufficentPrivilegesException();
}
}
}
-
Book mistypedBook = new Book("Stanislaw Lem", "Kibeliada");
bookmanager.addBook(name, mistypedBook);
Book whopsBook = bookmanager.getBook(name, mistypedBook.getBookid()/*<-ID IS NULL*/);
How can I sync the newly persisted entity with the database?
I've seen JPA Container managed entity update after persist question, but I'm trying to use the entity's id after the method ended. Shouldn't it have the id by then?
Your addBook method needs to return the persisted entity.
em.persist(konyv);
konyv = em.merge(konyv);
return konyv;
The entity returned will contain the generated id. As you have the persisted entity back, you won't need to call the getBook method.

Categories

Resources