Use Type Classes to Improve Type Safety of Persistent API
The changes in this release are many, and the overall picture is hard to grasp by looking through a
bullet list of the changes. For this reason, we present a quick migration guide here to get you from
0.22 to 0.23. Making the changes in the migration guide will probably get you 95-100% of the way there.
Quick Migration Guide
-
Replace this:
@domainModel object MyModel
with this:
@domainModel trait MyModel
-
Replace
@persistent
with@persistent[MyModel]
,@component
with@component[MyModel]
, and
@keyVal[P]
with@keyVal[MyModel, P]
-
Remove the
keySet = Set(key(props.a), key(props.b))
as an argument to the@persistent
annotation. Put the following lines in the companion object for your persistent class instead:implicit val aKey = key(props.a)
implicit val bKey = key(props.b)
-
Replace this:
val context = LongevityContext(MyModel)
with this:
val context = LongevityContext[MyModel]()
-
Replace references to
Repo[P]
withRepo[MyModel]
-
Replace calls like this:
context.repoPool[P]
with this:
context.repo
-
For repository methods
createSchema
andcloseSession
, replace calls like this:context.repoPool.createSchema()
with call chains like this:
context.repo.createSchema()
Changes
-
Merge
longevity.persistence.Repo
andlongevity.persistence.RepoPool
APIs. There is now single repository, and the create/retrieve/update/delete/query methods now all take the persisten type as a type parameter. To migrate, code that used to look like this:longevityContext.repoPool[User].create(user)
now looks like this:
longevityContext.repo.create[User](user)
In most cases, you can leave off type parameter, as the compiler can easily infer it:
longevityContext.repo.create(user)
-
Replace
longevity.model.DomainModel
with alongevity.model.ModelType
type-class. Everything that used to live inDomainModel
now lives inModelType
.
longevity.model.annotations.domainModel
now annotates a marker class or trait, instead of the object that was to become the oldDomainModel
. This annotation macro adds animplicit object modelType
into the companion object of the annotated class.ModelType
now takes a type parameterM
that refers to the phantom class annotated withdomainModel
.longevity.context.LongevityContext
now takes a type parameterM
for the model class. In place of the explicitDomainModel
argument, it now takes an implicitModelType[M]
, which can easily be found in the companion object ofM
, as built by the annotation macro.longevity.context.Repo
also now takes a type parameterM
. -
Add
longevity.model.ModelEv
type-class. ("Ev" is short for "evidence" here.) Thelongevity.model.annotations.domainModel
annotation macro now adds animplicit object modelEv
into the companion object of the annotated class. This evidence class is private to the package that the domain model is found in.longevity.model.PType
now has a type parameterM
for the model, and an implicitModelEv[M]
is required to initialize aPType[M, P]
. Because the generated model evidence is private to the model package, persistent types outside of the model package will not find the evidence, and will fail to compile. This prevents the user from accidentally creating a persistent type that falls outside the model. -
Add
longevity.model.PEv
type-class. ("Ev" is short for "evidence" here.) Thelongevity.model.PType
now includes animplicit val ev: PEv[M, P]
. Because the companion object of a persistent class is normally the correspondingPType
, this evidence should be available where needed.longevity.persistence.Repo
methods that used to take an implicitTypeKey[P]
argument, now take an implicitPEv[M, P]
argument. As users will not be able to find an implicitPEv[M, P]
available without the typeP
actually being part of the model, (excepting the case where the user goes to extended lengths to subvert our type system), it will now be a compile-time error to call these repository methods with a non-persistent object. This is a great improvement over the old situation, since aTypeKey[P]
is available for any typeP
for which there is aTypeTag[P]
available. -
Replace
longevity.model.KeyVal[P]
withlongevity.model.KVType[M, P, V]
, which includes an implicit vallongevity.model.KVEv[M, P, V]
.@longevity.model.annotations.keyVal
now takes a type parameterM
along with the type parameterP
. The@keyVal
annotation now creates or augments the companion object as aKVType[M, P, V]
. -
Methods
PType.key
andPType.primaryKey
now take implicitKVEv
arguments, to make sure the key value type provided matches aKVType
that is provided to theModelType
. -
The old constructors and factory methods for creating a
longevity.model.ModelType
have been replaced with a single constructor that takes lists oflongevity.model.PTypes
,longevity.model.CTypes
, andlongevity.model.KVTypes
. The runtime package scanning constructor has been replaced by a compile-time package scanning. The new scanner,longevity.model.annotations.packscanToList
, is used bylongevity.model.annotations.domainModel
, but you can use it yourself if you like. If you have been using the@domainModel
annotation, these changes should not affect you. -
longevity.model.PTypePool
andlongevity.model.CTypePool
have been removed. -
Instead of passing in a
keySet
to the@persistent
annotation, users should now specify their keys themselves, directly in the body of the companion object, as implicit values. ThePType.keySet
has been made private, and is populated by reflecting on the members of the companion object. -
Methods
longevity.persistence.Repo.retrieve
andlongevity.persistence.Repo.retrieveOne
now take an implicitKey[M, P, V]
instead of an implicitTypeKey[V]
. This will typically be found by implicit resolution in the companion object ofP
. -
Remove method
longevity.model.PType.prop
. You can extendlongevity.model.ptype.Prop
instead, but note that we advise you to use thelongevity.model.annotations.persistent
annotation to generate properties.