Lightbend Activator

Play Slick with Typesafe IDs

Activator will be EOL-ed on May 24, 2017.

We’re making it easier and simpler for developers to get started with Lightbend technologies. This unfortunately means that future releases of Play, Akka and Scala will no longer include Activator support, and Lightbend’s Activator server will be decommissioned by the end of 2017. Instead of supporting Activator to create and set up development projects, we'll be supporting standard Giter8 templates for sbt users and Maven archetypes for Maven users. So going forward,

To create new Lightbend projects

Instead of using the Activator command, make sure you have sbt 0.13.13 (or higher), and use the “sbt new” command, providing the name of the template. For example, “$ sbt new akka/hello-akka.g8”. You can find a list of templates here.

Also, as a convenience, the Lightbend Project Starter allows you to quickly create a variety of example projects that you just unzip and run.

To create new templates

If you want to create new templates, you can now do that in Giter8.

To migrate templates from Activator to Giter8

If you created Activator templates in the past, please consider migrating them to Giter8 with this simple process.

Play Slick with Typesafe IDs

VirtusLab
Source
November 6, 2015
advanced playframework scala slick

Slick (the Scala Language-Integrated Connection Kit) is a framework for type safe, composable data access in Scala. This template combines Play Framework with Slick and adds tools to use type-safe IDs for your classes so you can no longer join on bad id field or mess up order of fields in mappings. It also provides way to create service with methods (like querying all, querying by id, saving or deleting), for all classes with such IDs in just 4 lines of code.

How to get "Play Slick with Typesafe IDs" on your computer

There are several ways to get this template.

Option 1: Choose play-slick-advanced in the Lightbend Activator UI.

Already have Lightbend Activator (get it here)? Launch the UI then search for play-slick-advanced in the list of templates.

Option 2: Download the play-slick-advanced project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for play-slick-advanced.

  1. Download the Template Bundle for "Play Slick with Typesafe IDs"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Lightbend Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\play-slick-advanced> activator ui 
    This will start Lightbend Activator and open this template in your browser.

Option 3: Create a play-slick-advanced project from the command line

If you have Lightbend Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME play-slick-advanced on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/VirtusLab/activator-play-advanced-slick#master.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

You've just created an application with the Play Slick with type-safe IDs template. This template combines Play Framework with Slick and adds tools to use type-safe IDs for your classes so you can no longer join on bad id field or mess up order of fields in mappings. It also provides way to create repository with methods (like querying all, querying by id, saving or deleting), for all classes with such IDs in just 4 lines of code. Idea for type-safe ids was derived from Slick creator's presentation on ScalaDays 2013.

Set up the database

The template you're using is already set up with an in-memory instance of the H2 database. You can leave this as it is and skip this section if you want to continue using this database.

It is sometimes advantageous to use H2 in file mode rather than in memory. This will allow debugging through things like the h2-browser or other methods of access to H2. To make this change, edit the conf/application.conf file and find the line that specifies the default url:

db.default.url="jdbc:h2:mem:play"
In order to use a file-based database, change this to
db.default.url="jdbc:h2:/path/to/file
You can then get access to the database by shutting down all processes (including Play) that are accessing it and using the following commands
activator
h2-browser
And then enter the same URL to access H2.

To use a different database technology, use the instructions on the Play Documentation site

Create tables

Play includes a function called evolutions that allows the management of database schema changes. To use evolutions, create an evolutions folder for the default database as conf/evolutions/default then create .sql scripts that will put the database into the appropriate state. Evolutions also has the ability to revert the database to a previous version. In this example, evolution file is called conf/evolutions/default/1.sql with the following content:

# --- !Ups

create table "COMMENTS" (
"ID" BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY,
"TEXT" VARCHAR NOT NULL,
"AUTHOR" BIGINT NOT NULL,
"DATE" TIMESTAMP NOT NULL
);

create table "USERS" (
"ID" BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY,
"EMAIL" VARCHAR NOT NULL,
"FIRST_NAME" VARCHAR NOT NULL,
"LAST_NAME" VARCHAR NOT NULL
);

alter table "COMMENTS"
add constraint "COMMENTS_AUTHOR_FK"
foreign key("AUTHOR")
references "USERS"("ID");

# --- !Downs

alter table "COMMENTS" drop constraint "COMMENTS_AUTHOR_FK";
drop table "COMMENTS";
drop table "USERS";

When you load up your application, you will be prompted to apply the script(s).

Create the models

This template uses small library called Unicorn that enables usage of type safe primary and foreign keys in your application. Example entity created with this library can be shown here.

Entity declaration contains of four parts:

  • Case class for your type-safe id. It has to mix BaseId and contain id field of type Long. It is also good to extend from AnyVal to get unboxed values.
  • Companion object for your id. It have to extend IdCompanion[YourEntityId]. This step provides you with implicits needed in slick queries and Play! mappings.
  • Your domain model entity. It has to extend WithId[YourEntityId].
  • Table definition for your entity. It extends IdTable[YourEntityId, YourEntity], which accepts table name (and optionally schema name).

All of those needs following imports to work:

import org.virtuslab.unicorn.LongUnicornPlay._
import org.virtuslab.unicorn.LongUnicornPlay.driver.api._
Those loads definitions from cake baked for Play! and `Long` keys. You can bake your own cake if you want to change something, more on this later in tutorial.

In case of any problems related to Slick its' site at slick.typesafe.com has an excellent set of documentation.

Gain basic repository for free!

When you have entity declaration you get (almost) free basic repository methods for it. Example is shown in UserRepository. That one line of code enables you to save your entities, query or delete them by id and some more.

Example usage can be shown in UsersRepositoryTest.

Adding next class - type-safe joins

Let's add new class to our model. It will be a Comment. With an exception of class names and fields it's the same as User.

In queries for comments (CommentRepository) we can now create query joining on type-safe userId and method for searching comments by userId:

import model.UserId
private[repositories] trait CommentQueries {
   //query
   private def commentsByAuthorIdQuery(authorId: Rep[UserId]) =
       for {
            comment <- Comments.query if comment.authorId === authorId
       } yield comment

   //compiled query
   protected lazy val commentsByAuthorIdQueryCompiled = Compiled(commentsByAuthorIdQuery _)
}

class CommentRepository
   extends BaseIdRepository[CommentId, CommentRow, Comments](Comments.query)
   with CommentQueries {

   //repository method using query from CommentQueries trait
   def findForAuthorId(authorId: UserId): DBIO[Seq[CommentRow]] = {
      commentsByAuthorIdQueryCompiled(authorId).result
   }
}

At last, let's test your new repository in CommentsRepositoryTest:

//when
val testCaseLogic = for {
    userId <- userRepo.save(userRow)
    comments = buildComments(userId)
    commentsIds <- commentRepo.saveAll(comments)
    queriedComments <- commentRepo.findForAuthorId(userId)
} yield (comments, commentsIds, queriedComments)

//then
createSchema.andThen(testCaseLogic).map {
   case (comments, commentsIds, queriedComments) =>
       queriedComments.size shouldEqual comments.size
       queriedComments.flatMap(_.id) should contain theSameElementsAs (commentsIds)
   }

Additional goodies

Your type-safe IDs are designed not only to work with Slick, but also with Play! framework internals. You can also use them in mappings (beloved Play! forms API) and routes without any added work and benefit from type-safety in you application.

Using IDs in form mappings:


val userMapping = Form(
    mapping(
        "id" -> of[UserId], // it will be read from Long and packed to UserId
        "email" -> email,
        "firstName" -> text,
        "lastName" -> text
    )(User.apply)(User.unappply)
)
    

Using IDs in routes:


// it will be read from Long and packed to UserId, so you can use it like that in controller method
/user/show/:id      controllers.UsersController.show(id: model.UserId)
    

Baking your own cake

If you want to use unicorn with other type of primary keys (not only Longs, or use it outside of Play! Framework you can bake your own cake instead of LongUnicornPlay one. For implementation details see org.virtuslab.unicorn.LongUnicornPlay and org.virtuslab.unicorn.Unicorn.

About Play! Framework

It is not a scope of this template to dwell into details of the Play! framework. If you have some questions or want to know more about it, you are welcome to browse Play! sources around the web.

The Play Documentation contains much more exhaustive details and also covers a number of other topics which haven't been addressed in this tutorial.
StackOverflow is a great place ask questions about Play.
The play-framework Google Group is a great place to discuss Play.

×

Welcome to the Lightbend Enterprise Suite


You are excited about Reactive applications. And you want to build, manage and monitor them easily and reliably in your environment.
We get it. So we built Lightbend Enterprise Suite to help you do that, and more, with a range of powerful Application Management, Intelligent Monitoring, Enterprise Integration and Advanced Tooling features.