Package

model

persistence

Permalink

package persistence

Features and Benefits

Background

Scala uses case classes for modeling domain objects. quill-cache optimizes database access for read-mostly domain objects by providing a caching layer overtop Quill. This library depends on has-id, and case classes that need to be cached must extend HasId. HasId is generic and quite flexible, so you are encouraged to subclass all your domain objects from HasId, even if they do not require database caching.

The current version of this library has no provision for distributed caches. This could be retrofitted, however the author did not have the need, so the work was not done.

DAOs

The data access object pattern (DAO) is common across all computer languages. DAOs for case classes that require database caching must extend the persistence.CachedPersistence abstract class.

You are free to name DAOs anything you like; this library does not mandate any naming convention. Scala DAOs are often given the same name as the class that they persist, but with a suffix indicating plurality. For example, if a case class named Point needs to be persisted, the DAO is usually called Points. Unlike some other persistence libraries for Scala, Quill allows you to define your DAO in the case class's companion object, so you also have that option when using this library.

This library provides each DAO with its own cache. DAOs that extend CachedPersistence have a method called preload() which your application's initialization must invoke in order to fill that DAO's cache. A cache can be flushed by calling the DAO's flushCache() method. Because preload() always flushes the cache before loading it you probably won't ever need to explicitly call flushCache().

Cache Types

Two types of caches are supported by CachedPersistence:

Caches require an ExecutionContext, and the unit tests provide one:

package model.dao

import model.persistence.CacheExecutionContext
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor}

/** Just delegates to standard Scala ExecutionContext, you can make this do whatever you want */
object TestExecutionContext extends CacheExecutionContext {
  protected val ec: ExecutionContextExecutor = ExecutionContext.Implicits.global
  override def execute(runnable: Runnable): Unit = ec.execute(runnable)

  override def reportFailure(cause: Throwable): Unit = ec.reportFailure(cause)
}

Consistent APIs for Cached and Uncached DAOs

persistence.CachedPersistence subclasses persistence.UnCachedPersistence, which you can use to derive DAOs for case classes that must have direct access to the database so the case classes are not cached. You don't have to subclass UnCachedPersistence to get this behavior, but if you do then the DAOs for your cached domain objects will have the same interface as the DAOs for your uncached domain objects, and your code's structure will be more consistent.

Configuration

Your database configuration is specified by a HOCON file called application.conf on the classpath. Please see src/main/scala/resources/reference.conf for an example of how to set that up.

Here is an excerpt showing configuration for H2 and Postgres databases. Only one of these databases can be active per database context:

quill-cache {
  h2 {
    dataSourceClassName = org.h2.jdbcx.JdbcDataSource
    dataSource {
      url = "jdbc:h2:tcp://localhost/./h2data;DB_CLOSE_ON_EXIT=FALSE"
      url = ${?H2_URL}

      user = sa
      user = ${?H2_USER}

      password = ""
      password = ${?H2_PASSWORD}
    }
  }

  postgres {
    connectionTimeout = 30000
    dataSource {
      databaseName = ${?DB}
      password = ${?PGPASSWORD}

      portNumber = 5432
      portNumber = ${?PGPORT}

      serverName = localhost
      serverName = ${?PGHOST}

      ssl = true
      sslfactory = "org.postgresql.ssl.NonValidatingFactory"
      #url = ""

      user = postgres
      user = ${?USERID}
    }
    dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
    maximumPoolSize = 100
  }
}

The quill-cache section of the configuration file specifies parameters for this library:

See also the Quill test application.conf, Hikari initialization, HikariConfig.java, and Hikari pool sizing

Working with quill-cache

Quill Contexts

Quill-cache provides many flavors of Quill contexts, one for each type of supported database driver. Each context is exposed as an abstract class. Import the Quill context ctx from the appropriate type wherever you need to access the database.

Available abstract classes are: H2Ctx, MySqlCtx, PostgresCtx, and SqliteCtx. Subclass the appropriate abstract class for the type of database driver you need, like this:

class MyClass extends model.persistence.H2Ctx
Asynchronous Drivers

Asynchronous drivers are not currently supported by quill-cache, but there is an open issue for this enhancement. The database contexts MysqlAsyncCtx and PostgresAsyncCtx were written in anticipation of async support.

Best Practice

Define a trait called SelectedCtx, and mix it into all your DAOs. SelectedCtx merely extends the database context used in your application. The PersistenceTest DAO in test/scala/model/dao follows this pattern:

trait SelectedCtx extends model.persistence.H2Ctx

Now define your application's Quill context as a singleton, and mix in the predefined implicits for Quill-cache defined in QuillCacheImplicits.

package model

import model.dao.SelectedCtx
import persistence.QuillCacheImplicits

case object Ctx extends SelectedCtx with QuillCacheImplicits

If you have more implicits to mix in, define a trait in the same manner as QuillCacheImplicits and mix it in as well:

trait MyQuillImplicits { ctx: JdbcContext[_, _] =>
  // define Quill Decoders, Encoders and Mappers here
}

After adding in MyQuillImplicits, your revised application Quill context Ctx is now:

package model

import model.dao.SelectedCtx
import persistence.QuillCacheImplicits

case object Ctx extends SelectedCtx with QuillCacheImplicits with MyQuillImplicits

Now import the Quill context's internally defined implicits into your DAO's scope. Here are two examples of how to do that, one for cached and one for uncached persistence. Notice that Users and Tokens are singletons, which makes them easy to work with. Here is Users, a DAO with a strong cache, which means it needs an ExecutionContext like TestExecutionContext, which is in scope because it resides in the same package:

import model.{Ctx, User}
import model.persistence._

object Users extends CachedPersistence[Long, Option[Long], User]
    with StrongCacheLike[Long, Option[Long], User] {
  import Ctx._

  // DAO code for User goes here
}

Here is Tokens, a DAO without any cache, which means it does not need an ExecutionContext:

import model.{Ctx, Token}
import model.persistence._

object Tokens extends UnCachedPersistence[Long, Option[Long], Token] {
 import Ctx._

 // DAO code for Token goes here
}

Multiple Database Contexts

For circumstances where more than one database contexts need to share the same HikariCP pool, first construct a context, then other contexts can be created from the first context's dataSource. In the following example, a context for an H2 database is created using Ctx.dataSource:

case object Ctx2 extends H2Ctx(Ctx.dataSource) with MySpecialImplicits

Note that the new context need not have the same implicit decoders, encoders or mappers as the original context. See the ContextTest unit test for a working example.

Here is another variation:

/** This causes a new Hikari pool to be created */
object AuthCtx extends PostgresCtx with QuillCacheImplicits with IdImplicitLike

abstract class DerivedCtx(dataSource: DataSource with Closeable)
  extends PostgresCtx(dataSource) with QuillCacheImplicits with IdImplicitLike

/** Reuse the HikariCP pool from [[AuthCtx]] */
object Ctx extends DerivedCtx(AuthCtx.dataSource) with MySpecialImplicits

Working with DAOs

Quill-cache automatically defines a read-only property for each DAO, called className. This property is derived from the unqualified name of the case class persisted by the DAO. For example, if model.User is being persisted, className will be User.

Each DAO needs the following functions defined:

  1. _findAll – Quill query foundation - Encapsulates the Quill query that returns all instances of the case class from the database.
  2. _deleteById – Encapsulates the Quill query that deletes the instance of the case class with the given Id from the database.
  3. _findById – Encapsulates the Quill query that optionally returns the instance of the case class from the database with the given Id, or None if not found.
  4. _insert – Encapsulates the Quill query that inserts the given instance of the case class into the database, and returns the case class as it was stored, including any auto-increment fields.
  5. _update – Encapsulates the Quill query that updates the given instance of the case class into the database, and returns the entity. Throws an Exception if the case class was not previously persisted.

DAO CRUD

Here is an example of the CRUD-related functions, implemented in the DAO for model.User in the quill-cache unit test suite.

@inline def _findAll: List[User] = run { quote { query[User] } }

val queryById: IdOptionLong => Quoted[EntityQuery[User]] =
  (id: IdOptionLong) =>
    quote { query[User].filter(_.id == lift(id)) }

val _deleteById: (IdOptionLong) => Unit =
  (id: IdOptionLong) => {
    run { quote { queryById(id).delete } }
    ()
  }

val _findById: IdOptionLong => Option[User] =
  (id: Id[Option[Long]]) =>
    run { quote { queryById(id) } }.headOption

val _insert: User => User =
  (user: User) => {
    val id: Id[Option[Long]] = try {
      run { quote { query[User].insert(lift(user)) }.returning(_.id) }
    } catch {
      case e: Throwable =>
        logger.error(e.getMessage)
        throw e
    }
    user.setId(id)
  }

val _update: User => User =
  (user: User) => {
    run { queryById(user.id).update(lift(user)) }
    user
  }

With the above defined, quill-cache automatically provides the following CRUD-related methods for each DAO. Only finders can take advantage of a cache, if present:

@inline def deleteById(id: Id[_IdType]): Unit
@inline override def findAll: List[User]
def findById(id: Id[_IdType]): Option[User]
@inline def insert(user: User): User
@inline def update(user: User): User
@inline def remove(user: User): Unit
@inline def upsert(user: User): User
@inline def zap(): Unit

See the unit tests for examples of how to use this library.

Source
package.scala
Linear Supertypes
AnyRef, Any
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. persistence
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. All

Type Members

  1. abstract class AbstractCache[Key, Value] extends AnyRef

    Permalink

    Features strong values that might expire.

  2. trait CacheExecutionContext extends ExecutionContext

    Permalink
  3. trait CacheLike[Key, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends AnyRef

    Permalink
  4. trait CacheOps[Key <: AnyRef, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends AnyRef

    Permalink
  5. abstract class CachedPersistence[Key, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends UnCachedPersistence[Key, _IdType, CaseClass] with CacheLike[Key, _IdType, CaseClass]

    Permalink

    Overrides the Persistence methods which accesses the table so the cache is used instead.

    Overrides the Persistence methods which accesses the table so the cache is used instead. All instances of the domain model are expected to fit into the cache.

  6. trait ConfigParse extends AnyRef

    Permalink

    Parse application.conf and/or reference.conf for database parameters

  7. class Copier extends AnyRef

    Permalink

    Utility class for providing copying of a designated case class with minimal overhead.

  8. trait Crud[Key <: Long, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends AnyRef

    Permalink
  9. trait DBComponent extends AnyRef

    Permalink
  10. class DBModule extends AbstractModule with ScalaModule

    Permalink
  11. class DefaultExecutionContextProvider extends Provider[ExecutionContext]

    Permalink
  12. abstract class H2Ctx extends H2JdbcContext[TableNameSnakeCase.type]

    Permalink

    Mix this trait into any class that needs to access the H2 synchronous configuration.

    Mix this trait into any class that needs to access the H2 synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    class MyClass extends H2Ctx {
      import ctx._
    }

    The H2Configuration object mixes in this trait and provides an alternative mechanism.

  13. abstract class MySqlCtx extends MysqlJdbcContext[TableNameSnakeCase.type]

    Permalink

    Mix this trait into any class that needs to access the MySQL synchronous configuration.

    Mix this trait into any class that needs to access the MySQL synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    class MyClass extends MySqlCtx {
      import ctx._
    }

    The MysqlConfiguration object mixes in this trait and provides an alternative mechanism.

  14. abstract class MysqlAsyncCtx extends MysqlAsyncContext[TableNameSnakeCase.type]

    Permalink

    Mix this trait into any class that needs to access the MySQL asynchronous configuration.

    Mix this trait into any class that needs to access the MySQL asynchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    class MyClass extends MysqlAsyncCtx {
      import ctx._
    }

    The MysqlAsyncConfiguration object mixes in this trait and provides an alternative mechanism.

  15. abstract class PostgresAsyncCtx extends PostgresAsyncContext[TableNameSnakeCase.type]

    Permalink

    Mix this trait into any class that needs to access the Postgres asynchronous configuration.

    Mix this trait into any class that needs to access the Postgres asynchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    class MyClass extends PostgresAsyncCtx {
      import ctx._
    }

    The PostgresAsyncConfiguration object mixes in this trait and provides an alternative mechanism.

  16. abstract class PostgresCtx extends PostgresJdbcContext[TableNameSnakeCase.type]

    Permalink

    Mix this trait into any class that needs to access the Postgres synchronous configuration.

    Mix this trait into any class that needs to access the Postgres synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    class MyClass extends PostgresCtx {
      import ctx._
    }

    The PostgresConfiguration object mixes in this trait and provides an alternative mechanism.

  17. class ProcessEvolution extends AnyRef

    Permalink

    Extract the Up portion of a Play evolution file and execute SQL statements, including DDL

  18. trait QuillCacheImplicits extends IdImplicitLike

    Permalink
  19. implicit class RichThrowable extends AnyRef

    Permalink
  20. class SoftCache[Key, Value] extends AbstractCache[Key, Value]

    Permalink

    Features soft values that might expire

  21. trait SoftCacheLike[Key, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends CacheLike[Key, _IdType, CaseClass]

    Permalink

    SoftCache contains "soft" values that might expire by timing out or might get bumped if memory fills up.

    SoftCache contains "soft" values that might expire by timing out or might get bumped if memory fills up. Mix this trait into the DAO to provide this behavior. DAOs that mix in SoftCache do not assume that all instances of the case class can fit into memory.

    SoftCache finders that return at most one item from querying the cache will access the database, looking for that item after every cache miss. Because of this, those SoftCache finders run more slowly than StrongCache finders when the cache does not contain the desired value.

    SoftCache finders that return a list of items must always query the database and never look in the cache.

    The CachedPersistence trait implements the default caching strategy. This trait overrides the default finder implementations. This trait is experimental, do not use in production.

  22. abstract class SqliteCtx extends SqliteJdbcContext[TableNameSnakeCase.type]

    Permalink

    Mix this trait into any class that needs to access the Sqlite synchronous configuration.

    Mix this trait into any class that needs to access the Sqlite synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    class MyClass extends SqliteCtx {
      import ctx._
    }

    The SqliteConfiguration object mixes in this trait and provides an alternative mechanism.

  23. class StrongCache[Key, Value] extends AbstractCache[Key, Value]

    Permalink
  24. trait StrongCacheLike[Key, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends CacheLike[Key, _IdType, CaseClass]

    Permalink

    CachePersistence.prefetch must be called before any finders.

    CachePersistence.prefetch must be called before any finders. The CachedPersistence trait implements the default caching strategy. This trait overrides the default finder implementations.

  25. trait TableNameSnakeCase extends NamingStrategy with Escape with SnakeCase

    Permalink

    Ensures that table names are quoted and snake_case but never start with a leading _.

  26. trait Throwables extends AnyRef

    Permalink
  27. abstract class UnCachedPersistence[Key, _IdType <: Option[Key], CaseClass <: HasId[CaseClass, _IdType]] extends AnyRef

    Permalink

    Accesses the table for each query.

    Accesses the table for each query. You can use this abstract class to derive DAOs for case classes that must have direct access to the database so the case classes are not cached. You don't have to subclass UnCachedPersistence, but if you do then the DAOs for your cached domain objects will have the same interface as the DAOs for your uncached domain objects.

Value Members

  1. object ConfigParse extends ConfigParse

    Permalink
  2. object Copier

    Permalink

    From StackOverflow

  3. object CopierTest extends App

    Permalink
  4. object DBComponent

    Permalink
  5. object H2Configuration extends H2Ctx

    Permalink

    Object for exposing the H2 synchronous configuration.

    Object for exposing the H2 synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    import H2Configuration.ctx._
  6. object MysqlAsyncConfiguration extends MysqlAsyncCtx

    Permalink

    Object for exposing the MySQL asynchronous configuration.

    Object for exposing the MySQL asynchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    import MysqlAsyncConfiguration.ctx._
  7. object MysqlConfiguration extends MySqlCtx

    Permalink

    Object for exposing the MySQL synchronous configuration.

    Object for exposing the MySQL synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    import MysqlConfiguration.ctx._
  8. object PostgresAsyncConfiguration extends PostgresAsyncCtx

    Permalink

    Object for exposing the Postgres asynchronous configuration.

    Object for exposing the Postgres asynchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    import PostgresAsyncConfiguration.ctx._
  9. object PostgresConfiguration extends PostgresCtx

    Permalink

    Object for exposing the Postgres synchronous configuration.

    Object for exposing the Postgres synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    import PostgresConfiguration.ctx._
  10. object ProcessEvolution

    Permalink
  11. object SoftCache

    Permalink
  12. object SqliteConfiguration extends SqliteCtx

    Permalink

    Object for exposing the Sqlite synchronous configuration.

    Object for exposing the Sqlite synchronous configuration. Exposes a property called ctx, which is the Quill context. To use, simply import the context, like this:

    import SqliteConfiguration.ctx._
  13. object StrongCache

    Permalink
  14. object TableNameSnakeCase extends TableNameSnakeCase

    Permalink

Inherited from AnyRef

Inherited from Any

Ungrouped