How to add cache mechanism when using anorm in Playframework
Asked Answered
C

3

5

I see anorm is not a ORM framework, it is querying data directly by SQL. For most of the application/website, we should not query the database everytime, we needs to cache the data either by SQL or item id. I wonder whether playframework has provided any kind of caching mechanism? If not how to add it?

Thanks.

Corydon answered 27/11, 2012 at 6:42 Comment(0)
B
11

You can use the Play cache in your controller, before querying your database. Here is a straightforward example derived from the Play cache documentation and the Scala API:

val user: User = Cache.getOrElse[User](key = "user" + userId, expiration = 10) {
  User.findById(userId)
}

In this code, before trying to query the database, we make a lookup in the cache to check if the User has not been loaded previously. If not found in the cache, we store it in the cache with an expiration in 10 seconds.

Bedraggle answered 27/11, 2012 at 10:6 Comment(2)
+1, but 10 second cache? I've been going with 2 minutes to 24 hours personally. Not sure if there's a point of diminishing returns in setting very low cache expiration times.Riordan
Sure, it was just for the exampleBedraggle
R
7

You can simply cache the answer of the Anorm methods. For example, real method that I use:

def findById(id: Long): Option[User] = {
    Cache.getOrElse(userCacheKey + id, 60*60) {
      DB.withConnection {
        implicit connection =>
          SQL("select * from publisher where id = {id}").on('id -> id).as(User.simple.singleOpt)
      }
    }
}

The code does the Select and stores the answer in the cache via getOrElse. If the value is in the Cache, it will ber etrieved and no query will be executed.

The only issue is that when you update the entity User you'll have to update the cache (so it doesn't keep stale data):

// Assumes a user: User object available with the updated user
Cache.set(userCacheKey + id, cached.copy(name = user.name, avatar = user.avatar, bio = user.bio, url = user.url, location = user.location), 60*60)
Ratite answered 27/11, 2012 at 10:13 Comment(6)
Same answer at the same time :-)Bedraggle
haha yeah, slightly different as I cache it in Anorm, you in the caller, but essentially the same :)Ratite
thank you guys, this approach worked, but needs too much coding and it embeds into business code. I expect the cache mechanism should be a common framework so that the end user only cares about the query logic, but not the cache and cache invalidation. It will be perfect if anorm can do this for me :)Corydon
@simon unfortunately I don't expect what you ask to happen. Cache has to be managed by the dev, as sometimes you can't/don't want to cache everything. I understand it is some extra code, but if you have the app broken properly into layers, it's only a small set of wrappers on the methods that manage the model. Hard to make it simpler :)Ratite
I totally agree with @PereVillegaBedraggle
@Pere, in some complicated application, there may be many entities object that needs to be queried with many kinds of SQL. If we added cache expression for all these entities and queries, the same code will written everywhere. All I want is the cache mechanism such as the cache mechanism in hibernate, which I can configure to enable or disable in different level as I wish. I don't want to pay attention to notify the entity change to cache by myself everywhere :(Corydon
L
0

Assuming you are using Play2 framework, it indeed does provide a cache mechanism. It has nice documentation here:

http://www.playframework.org/documentation/2.0/JavaCache (its called javacache, but it works from Scala)

Langton answered 27/11, 2012 at 8:9 Comment(1)
Thanks for your reply. I see it from play document, it is using ehcache, but it is not integrated with anorm, it's not easy to use it to cache the query results, I just wonder if Playframework provided a way to do that. If somebody has implemented the cache, it will also helpCorydon

© 2022 - 2024 — McMap. All rights reserved.