Working with Databases

Exclusive offer: get 50% off this eBook here
Lift Application Development Cookbook

Lift Application Development Cookbook — Save 50%

Over 50 practical recipes to build web applications using Lift, the most secure web framework available with this book and ebook

£16.99    £8.50
by Gilberto T. Garcia Jr. | October 2013 | Cookbooks Open Source

In this article by Gilberto T. Garcia Jr., the author of the book Lift Application Development Cookbook, we will cover the following topics:

  • Configuring a connection to database
  • Mapping a table to a Scala class
  • Creating one-to-many relationships
  • Creating many-to-many relationships
  • Creating CRUD features with CRUDify
  • Paginating result sets
  • Using an in-memory database in application tests

(For more resources related to this topic, see here.)

We have learned how to create snippets, how to work with forms, and Ajax, how to test your code, and how to create a REST API, and all this is awesome! However, we did not learn how to persist data to make it durable.

In this article, we will see how to use Mapper, an object-relational mapping ( ORM ) system for relational databases, included with Lift.

The idea is to create a map of database tables into a well-organized structure of objects; for example, if you have a table that holds users, you will have a class that represents the user table.

Let's say that a user can have several phone numbers. Probably, you will have a table containing a column for the phone numbers and a column containing the ID of the user owning that particular phone number.

This means that you will have a class to represent the table that holds phone numbers, and the user class will have an attribute to hold the list of its phone numbers; this is known as a one-to-many relationship .

Mapper is a system that helps us build such mappings by providing useful features and abstractions that make working with databases a simple task. For example, Mapper provides several types of fields such as MappedString, MappedInt, and MappedDate which we can use to map the attributes of the class versus the columns in the table being mapped. It also provides useful methods such as findAll that is used to get a list of records or save, to persist the data. There is a lot more that Mapper can do, and we'll see what this is through the course of this article.

Configuring a connection to database

The first thing we need to learn while working with databases is how to connect the application that we will build with the database itself.

In this recipe, we will show you how to configure Lift to connect with the database of our choice. For this recipe, we will use PostgreSQL; however, other databases can also be used:

Getting ready

  1. Start a new blank project.
  2. Edit the build.sbt file to add the lift-mapper and PostgreSQL driver dependencies:

    "net.liftweb" %% "lift-mapper" % liftVersion % "compile", "org.postgresql" % "postgresql" % "9.2-1003-jdbc4" % "compile"

  3. Create a new database.
  4. Create a new user.

How to do it...

Now carry out the following steps to configure a connection with the database:

  1. Add the following lines into the default.props file:

    db.driver=org.postgresql.Driver db.url=jdbc:postgresql:liftbook db.user=<place here the user you've created> db.password=<place here the user password>

  2. Add the following import statement in the Boot.scala file:

    import net.liftweb.mapper._

  3. Create a new method named configureDB() in the Boot.scala file with the following code:

    def configureDB() { for { driver <- Props.get("db.driver") url <- Props.get("db.url") } yield { val standardVendor = new StandardDBVendor(driver, url, Props.get("db.user"),
    Props.get("db.password")) LiftRules.unloadHooks.append(standardVendor.closeAllConnections_! _) DB.defineConnectionManager(DefaultConnectionIdentifier, standardVendor) } }

  4. Then, invoke configureDB() from inside the boot method.

How it works...

Lift offers the net.liftweb.mapper.StandardDBVendor class, which we can use to create connections to the database easily.

This class takes four arguments: driver, URL, user, and password. These are described as follows:

  • driver : The driver argument is the JDBC driver that we will use, that is, org.postgresql.Driver
  • URL : The URL argument is the JDBC URL that the driver will use, that is, jdbc:postgresql:liftbook
  • user and password : The user and password arguments are the values you set when you created the user in the database.

After creating the default vendor, we need to bind it to a Java Naming and Directory Interface (JNDI) name that will be used by Lift to manage the connection with the database. To create this bind, we invoked the defineConnectionManager method from the DB object. This method adds the connection identifier and the database vendor into a HashMap, using the connection identifier as the key and the database vendor as the value. The DefaultConnectionIdentifier object provides a default JNDI name that we can use without having to worry about creating our own connection identifier. You can create your own connection identifier if you want. You just need to create an object that extends ConnectionIdentifier with a method called jndiName that should return a string.

Finally, we told Lift to close all the connections while shutting down the application by appending a function to the unloadHooks variable. We did this to avoid locking connections while shutting the application down.

There's more...

It is possible to configure Lift to use a JNDI datasource instead of using the JDBC driver directly. In this way, we can allow the container to create a pool of connections and then tell Lift to use this pool.

To use a JNDI datasource, we will need to perform the following steps:

  1. Create a file called jetty-env.xml in the WEB-INF folder under src/main/webapp/ with the following content:

    <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
    "http://www.eclipse.org/jetty/configure.dtd"> <Configure class="org.eclipse.jetty.webapp.WebAppContext"> <New id="dsliftbook" class="org.eclipse.jetty.plus.jndi.Resource"> <Arg>jdbc/dsliftbook</Arg> <Arg> <New class="org.postgresql.ds.PGSimpleDataSource"> <Set name="User">place here the user you've created</Set> <Set name="Password">place here the user password</Set> <Set name="DatabaseName">liftbook</Set> <Set name="ServerName">localhost</Set> <Set name="PortNumber">5432</Set> </New> </Arg> </New> </Configure>

  2. Add the following line into the build .sbt file:

    env in Compile := Some(file("./src/main/webapp/WEB-INF/jetty-env.xml") asFile)

  3. Remove all the jetty dependencies and add the following:

    "org.eclipse.jetty" % "jetty-webapp" % "8.0.4.v20111024" % "container", "org.eclipse.jetty" % "jetty-plus" % "8.0.4.v20111024" % "container",

  4. Add the following code into the web.xml file.

    <resource-ref> <res-ref-name>jdbc/dsliftbook</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>

  5. Remove the configureDB method.
  6. Replace the invocation of configureDB method with the following line of code:

    DefaultConnectionIdentifier.jndiName = "jdbc/dsliftbook"

The creation of the file jetty-envy.xml and the change we made in the web.xml file were to create the datasource in jetty and make it available to Lift.

Since the connections will be managed by jetty now, we don't need to append hooks so that Lift can close the connections or any other configuration when shutting down the application. All we need to do is tell Lift to get the connections from the JNDI datasource that we have configured into the jetty. We do this by setting the jndiName variable of the default connection identifier, DefaultConnectionIdentifier as follows:

DefaultConnectionIdentifier.jndiName = "jdbc/dsliftbook"

The change we've made to the build.sbt file was to make the jetty-env.xml file available to the embedded jetty. So, we can use it when we get the application started by using the container:start command.

See also...

Mapping a table to a Scala class

Now that we know how to connect Lift applications to the database, the next step is to learn how to create mappings between a database table and a Scala object using Mapper.

Getting ready

We will re-use the project we created in the previous recipe since it already has the connection configured.

How to do it...

Carry out the following steps to map a table into a Scala object using Mapper:

  1. Create a new file named Contact.scala inside the model package under src/main/scala/code/ with the following code:

    package code.model import net.liftweb.mapper.{MappedString, LongKeyedMetaMapper, LongKeyedMapper,
    IdPK} class Contact extends LongKeyedMapper[Contact] with IdPK { def getSingleton = Contact object name extends MappedString(this, 100) } object Contact extends Contact with LongKeyedMetaMapper[Contact] { override def dbTableName = "contacts" }

  2. Add the following import statement in the Boot.scala file:

    import code.model.Contact

  3. Add the following code into the boot method of the Boot class:

    Schemifier.schemify( true, Schemifier.infoF _, Contact )

  4. Create a new snippet named Contacts with the following code:

    package code.snippet import code.model.Contact import scala.xml.Text import net.liftweb.util.BindHelpers._ class Contacts { def prepareContacts_!() { Contact.findAll().map(_.delete_!) val contactsNames = "John" :: "Joe" :: "Lisa" :: Nil contactsNames.foreach(Contact.create.name(_).save()) } def list = { prepareContacts_!() "li *" #> Contact.findAll().map { c => { c.name.get } } } }

  5. Edit the index.html file by replacing the content of the div element with main as the value of id using the following code :

    <div data-list="Contacts.list"> <ul> <li></li> </ul> </div>

  6. Start the application.
  7. Access your local host (http://localhost:8080), and you will see a page with three names, as shown in the following screenshot:

How it works...

To work with Mapper, we use a class and an object. The first is the class that is a representation of the table; in this class we define the attributes the object will have based on the columns of the table we are mapping. The second one is the class' companion object where we define some metadata and helper methods.

The Contact class extends the LongKeyedMapper[Contact] trait and mixes the trait IdPK. This means that we are defining a class that has an attribute called id (the primary key), and this attribute has the type Long. We are also saying that the type of this Mapper is Contact.

To define the attributes that our class will have, we need to create objects that extend "something". This "something" is the type of the column. So, when we say an object name extends MappedString(this, 100), we are telling Lift that our Contact class will have an attribute called name, which will be a string that can be 100 characters long. After defining the basics, we need to tell our Mapper where it can get the metadata about the database table. This is done by defining the getSingleton method. The Contact object is the object that Mapper uses to get the database table metadata. By default, Lift will use the name of this object as the table name. Since we don't want our table to be called contact but contacts, we've overridden the method dbTableName.

What we have done here is created an object called Contact, which is a representation of a table in the database called contacts that has two columns: id and name. Here, id is the primary key and of the type Long, and name is of the type String, which will be mapped to a varchar datatype.

This is all we need to map a database table to a Scala class, and now that we've got the mapping done, we can use it. To demonstrate how to use the mapping, we have created the snippet Contacts.

This snippet has two methods. The list method does two things; it first invokes the prepareContacts_!() method, and then invokes the findAll method from the Contact companion object. The prepareContacts_!() method also does two things: it first deletes all the contacts from the database and then creates three contacts: John, Joe, and Lisa. To delete all the contacts from the database, it first fetches all of them using the findAll method, which executes a select * from contacts query and returns a list of Contact objects, one for each existing row in the table. Then, it iterates over the collection using the foreach method and for each contact, it invokes the delete_! method which as you can imagine will execute a delete from contacts where id = contactId query is valid.

After deleting all the contacts from the database table, it iterates the contactsNames list, and for each element it invokes the create method of the Contact companion object, which then creates an instance of the Contact class. Once we have the instance of the Contact class, we can set the value of the name attribute by passing the value instance.name(value).

You can chain commands while working with Mapper objects because they return themselves. For example, let's say our Contact class has firstName and lastName as attributes. Then, we could do something like this to create and save a new contact:

Contact.create.firstName("John").lastName("Doe").save()

Finally, we invoke the save() method of the instance, which will make Lift execute an insert query, thus saving the data into the database by creating a new record.

Getting back to the list method, we fetch all the contacts again by invoking the findAll method, and then create a li tag for each contact we have fetched from the database. The content of the li tags is the contact name, which we get by calling the get method of the attribute we want the value of. So, when you say contact.name.get, you are telling Lift that you want to get the value of the name attribute from the contact object, which is an instance of the Contact class.

There's more...

Lift comes with a variety of built-in field types that we can use; MappedString is just one of them. The others include, MappedInt, MappedLong, and MappedBoolean.

All these fields come with some built-in features such as the toForm method, which returns the HTML needed to generate a form field, and the validate method that validates the value of the field.

By default, Lift will use the name of the object as the name of the table's column; for example, if you define your object as name—as we did—Lift will assume that the column name is name.

Lift comes with a built-in list of database-reserved words such as limit, order, user, and so on. If the attribute you are mapping is a database-reserved word, Lift will append _c at the end of the column's name when using Schemifier. For example, if you create an attribute called user, Lift will create a database column called user_c.

You can change this behavior by overriding the dbColumnName method, as shown in the following code:

object name extends MappedString(this, 100) { override def dbColumnName = "some_new_name" }

In this case, we are telling Lift that the name of the column is some_new_name. We have seen that you can fetch data from the database using the findAll method. However, this method will fetch every single row from the database.

To avoid this, you can filter the result using the By object; for example, let's say you want to get only the contacts with the name Joe. To accomplish this, you would add a By object as the parameter of the findAll method as follows:

Contact.findAll(By(Contact.name, "Joe")).

There are also other filters such as ByList and NotBy. And if for some reason the features offered by Mapper to build select queries aren't enough, you can use methods such as findAllByPreparedStatement and findAllByInsecureSQL where you can use raw SQL to build the queries.

The last thing left to talk about here is how this example would work if we didn't create any table in the database. Well, I hope you remember that we added the following lines of code to the Boot.scala file:

Schemifier.schemify( true, Schemifier.infoF _, Contact )

As it turns out, the Schemifier object is a helper object that assures that the database has the correct schema based on a list of MetaMappers. This means that for each MetaMapper we pass to the Schemifier object, the object will compare the MetaMapper with the database schema and act accordingly so that both the MetaMapper and the database schema match. So, in our example, Schemifier created the table for us. If you change MetaMapper by adding attributes, Schemifier will create the proper columns in the table.

See also...

Creating one-to-many relationships

In the previous recipe, we learned how to map a Scala class to a database table using Mapper. However, we have mapped a simple class with only one attribute, and of course, we will not face this while dealing with real-world applications.

We will probably need to work with more complex data such as the one having one-to-many or many-to-many relationships. An example of this kind of relationship would be an application for a store where you'll have customers and orders, and we need to associate each customer with the orders he or she placed. This means that one customer can have many orders.

In this recipe, we will learn how to create such relationships using Mapper.

Getting ready

We will modify the code from the last section by adding a one-to-many relationship into the Contact class. You can use the same project from before or duplicate it and create a new project.

How to do it...

Carry out the following steps:

  1. Create a new class named Phone into the model package under src/main/scala/code/ using the following code:

    package code.model import net.liftweb.mapper._ class Phone extends LongKeyedMapper[Phone] with IdPK { def getSingleton = Phone object number extends MappedString(this, 20) object contact extends MappedLongForeignKey(this, Contact) } object Phone extends Phone with LongKeyedMetaMapper[Phone] { override def dbTableName = "phones" }

  2. Change the Contact class declaration from:

    class Contact extends LongKeyedMapper[Contact] with IdPK

    To:

    class Contact extends LongKeyedMapper[Contact] with IdPK with OneToMany[Long, Contact]

  3. Add the attribute that will hold the phone numbers to the Contact class as follows:

    object phones extends MappedOneToMany(Phone, Phone.contact,
    OrderBy(Phone.id, Ascending)) with Owned[Phone] with Cascade[Phone]

  4. Insert the new class into the Schemifier list of parameters. It should look like the following code:

    Schemifier.schemify( true, Schemifier.infoF _, Contact, Phone )

  5. In the Contacts snippet, replace the import statement from the following code:

    import code.model.Contact

    To:

    import code.model._

  6. Modify the Contacts.prepareContacts_! method to associate a phone number to each contact. Your method should be similar to the one in the following lines of code:

    def prepareContacts_!() { Contact.findAll().map(_.delete_!) val contactsNames = "John" :: "Joe" :: "Lisa" :: Nil val phones = "5555-5555" :: "5555-4444" :: "5555-3333" :: "5555-2222" ::
    "5555-1111" :: Nil contactsNames.map(name => { val contact = Contact.create.name(name) val phone = Phone.create.number(phones((new Random()).nextInt(5))).saveMe() contact.phones.append(phone) contact.save() }) }

  7. Replace the list method's code with the following code:

    def list = { prepareContacts_!() ".contact *" #> Contact.findAll().map { contact => { ".name *" #> contact.name.get & ".phone *" #> contact.phones.map(_.number.get) } } }

  8. In the index.html file, replace the ul tag with the one in the following code snippet:

    <ul> <li class="contact"> <span class="name"></span> <ul> <li class="phone"></li> </ul> </li> </ul>

  9. Start the application.
  10. Access http://localhost:8080.

Now you will see a web page with three names and their phone numbers, as shown in the following screenshot:

How it works...

In order to create a one-to-many relationship, we have mapped a second table called phone in the same way we've created the Contact class. The difference here is that we have added a new attribute called contact. This attribute extends MappedLongForeignKe y, which is the class that we need to use to tell Lift that this attribute is a foreign key from another table.

In this case, we are telling Lift that contact is a foreign key from the contacts table in the phones table. The first parameter is the owner, which is the class that owns the foreign key, and the second parameter is MetaMapper, which maps the parent table.

After mapping the phones table and telling Lift that it has a foreign key, we need to tell Lift what the "one" side of the one-to-many relationship will be.

To do this, we need to mix the OneToMany trait in the Contact class. This trait will add features to manage the one-to-many relationship in the Contact class. Then, we need to add one attribute to hold the collection of children records; in other words, we need to add an attribute to hold the contact's phone numbers.

Note that the phones attribute extends MappedOneToMany, and that the first two parameters of the MappedOneToMany constructor are Phone and Phone.contact. This means that we are telling Lift that this attribute will hold records from the phones table and that it should use the contact attribute from the Phone MetaMapper to do the join. The last parameter, which is optional, is the OrderBy object. We've added it to tell Lift that it should order, in an ascending order, the list of phone numbers by their id.

We also added a few more traits into the phones attribute to show some nice features. One of the traits is Owned; this trait tells Lift to delete all orphan fields before saving the record. This means that if, for some reason, there are any records in the child table that has no owner in the parent table, Lift will delete them when you invoke the save method, helping you keep your databases consistent and clean.

Another trait we've added is the Cascade trait. As its name implies, it tells Lift to delete all the child records while deleting the parent record; for example, if you invoke contact.delete_! for a given contact instance, Lift will delete all phone records associated with this contact instance. As you can see, Lift offers an easy and handy way to deal with one-to-many relationships.

See also...

Lift Application Development Cookbook Over 50 practical recipes to build web applications using Lift, the most secure web framework available with this book and ebook
Published: September 2013
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:

Creating many-to-many relationships

Many-to-many relationships are also very common in real-world applications. In this recipe, we will learn how to use Mapper to create such relationships. To do this, we will create a model for a school where we can have one student attending many subjects and one subject having many students.

Getting ready

You can use any of the projects we have used in the previous sections of this article. It is up to you whether to change an existing project or duplicate it.

How to do it...

To create a many-to-many relationship, carry out the following steps:

  1. Create a class called Student in the model package with the following code:

    package code.model import net.liftweb.mapper._ class Student extends LongKeyedMapper[Student] with ManyToMany with IdPK { def getSingleton = Student object name extends MappedString(this, 50) object courses extends MappedManyToMany(CourseStudents, CourseStudents.student,
    CourseStudents.course, Course) } object Student extends Student with LongKeyedMetaMapper[Student] { override def dbTableName = "students" }

  2. Create a class called Course in the model package with the following code:

    package code.model import net.liftweb.mapper._ class Course extends LongKeyedMapper[Course] with ManyToMany with IdPK { def getSingleton = Course object name extends MappedString(this, 50) object students extends MappedManyToMany(CourseStudents, CourseStudents.course,
    CourseStudents.student, Student) } object Course extends Course with LongKeyedMetaMapper[Course] { override def dbTableName = "courses" }

  3. Create a class called CourseStudents in the model package with the following code:

    package code.model import net.liftweb.mapper.{MappedLongForeignKey, LongKeyedMetaMapper, IdPK,
    LongKeyedMapper} class CourseStudents extends LongKeyedMapper[CourseStudents] with IdPK { def getSingleton = CourseStudents object course extends MappedLongForeignKey(this, Course) object student extends MappedLongForeignKey(this, Student) } object CourseStudents extends CourseStudents with
    LongKeyedMetaMapper[CourseStudents] { override def dbTableName = "courses_students" }

  4. Create a new snippet called Courses. Here is the code:

    package code.snippet import net.liftweb.util.4BindHelpers._ import code.model.{Course, Student} class Courses { def prepareCourses_!() { Student.findAll().map(_.delete_!) Course.findAll().map(c => { c.students.delete_! c.delete_! }) val studentNames = "John" :: "Joe" :: "Lisa" :: Nil val courseNames = "Math" :: "Biology" :: "Physics" :: Nil val students = studentNames.map(student =>
    Student.create.name(student).saveMe()) courseNames.map(courseName => { val course = Course.create.name(courseName) course.students.appendAll(students) course.save }) } def list = { prepareCourses_!() ".course *" #> Course.findAll().map { c => { ".name *" #> c.name.get & ".student *" #> c.students.map(_.name.get) } } } }

  5. Add those three classes we created in the model package to the Schemifier list of parameters as follows:

    Schemifier.schemify( true, Schemifier.infoF _, Course, Student, CourseStudents )

  6. Modify the content of div with main as the value of id in the index.html file so that it will have the following code:

    <div data-lift="Courses.list"> <ul> <li class="course"> <span class="name"></span> <ul> <li class="student"></li> </ul> </li> </ul> </div>

  7. Start the application.
  8. Access http:localhost:8080.

Now you will see a web page containing a list of courses and its students:

How it works...

To create a many-to-many relationship, we need to set one attribute in each class of the relationship that will be a link to the other class; for example, the Student class will have an attribute called courses, while the Course class will have an attribute called students.

However, this is not enough. We will also need a third class, CourseStudents, to map the extra table needed to create the many-to-many relationship. This class will have a foreign key for both Student and Courses classes.

With this is mind, let's take a look at the code we've created and see how it accomplishes the task. The Course and Student classes are identical since they both mix in the ManyToMany trait. This is a trait similar to the OneToMany trait in the sense that while the OneToMany trait added features to manage one-to-many relationships in the Contact class, the ManyToMany trait will also add features to manage many-to-many relationships into the Course class.

Both classes also define an attribute that extends MappedManyToMany, which is a class to track the relationship. The difference between the Course and Student classes lies in the order of the parameters passed to the MappedManyToMany class. The order of the parameters is as follows:

  • The class that defines the CourseStudents join table is the same for both classes.
  • The attribute of the join Mapper, CourseStudents, which defines the link with the class that is defining the MappedManyToMany attribute. In case of the Course class, it will be CourseStudents.course, while for the Student class, it will be CourseStudents.student.
  • The attribute of the join Mapper, CourseStudents, which defines the other side of the relation. For the Course class, it will be the foreign key of the Student class, CourseStudents.student, and for the Student class, it will be the foreign key of the Course class, CourseStudents.course.
  • The singleton object of the other Mapper side of the relation. It will be Student for the Course class and Course for the Student class.

This takes care of the first part of the mapping. Now, we need to map the join table, which leads us to the CourseStudents class. As you can see, this is a simple mapping which has two attributes and both of them are mapped as MappedLongForeignKey. As I said before, this is the class that we need to use to tell Lift that this attribute is a foreign key from another table.

As you can imagine, the join table is a table that has two foreign keys—one for each side of the relation. The snippet uses the prepareCourses_! method to clean up the table and create the necessary data to show how the mapping works. You can see that it works just like the example from the Creating one-to-many relationships section.

This is true to the list method, which just iterates over the list of courses and fetches the list of students for each course, then creates the necessary CssSel to render the HTML that will produce the page you saw on the screenshot in the How to do it... section of the Creating many-to-many relationships recipe. You can see that for a given course, we can get the collection of students directly using c.students.map(_.name.get) due to the mapping we've built.

See also...

Creating CRUD features with CRUDify

Another interesting feature offered by Lift is the CRUDify trait. Imagine if there was a way to get your mappings and generate all the pages necessary to perform CRUD operations without you having to manually create them one by one. What I am saying is that, given a mapping, your application will automatically have all the pages needed to perform the CRUD operations. Wouldn't it be nice if we had such a feature? Well, it happens that we do have such a feature, as the CRUDify trait does exactly this!

CRUD is an acronym for the four basic functions performed to databases and persistent storage, and it stands for Create, Read, Update, and Destroy.

Getting ready

To demonstrate how the CRUDify trait works, we will use the project we created in the Mapping a table to a Scala class recipe.

You can duplicate it or just modify the same project.

How to do it...

Carry out the following steps:

  1. Mix-in the CRUDify trait in the Contact object as follows:

    object Contact extends Contact with LongKeyedMetaMapper[Contact]

  2. With CRUDify[Long, Contact], remove the prepareContacts_! method from the Contacts snippet.
  3. Modify the entries variable in the Boot.boot method to include the Contact.menus variable. It should look like the following code:

    val entries = List( Menu.i("Home") / "index" :: // the simple way to declare a menu // more complex because this menu allows anything in the // /static path to be visible Menu(Loc("Static", Link(List("static"), true, "/static/index"), "Static Content")) :: Contact.menus ).flatten

  4. Start the application.
  5. Access http://localhost:8080.

At this point, you should see two new menu items List contacts and Create contacts .

If you click on the List contacts link, you will see a web page similar to the following screenshot:

How it works...

Did you see how easy it was to use the CRUDify trait to add the CRUD features in the application? All we needed to do was to add it to the Contact MetaMapper and add the menu entries in the site map.

Let's start with the menu entries. The CRUDify trait comes with a method called menus. This method defines a menu item for each of the CRUD operations—list, edit, create, view, and delete—and binds each of them to a given template, which are defined in the CRUDify trait. For example, when you click on the List Contacts link, Lift will use the template defined by the method _showAllTemplate from the CRUDify trait, resulting in the list you saw on the screenshot in the How to do it... section in the Creating CRUD features with CRUDify recipe. The same holds for each one of the other operations—view, edit, create, and delete. This is why it was so easy to add the CRUD features to the application.

There's more...

You can define your own templates for each of the web pages that are used by the CRUDify trait.

To do this, you need to override the following methods:

  • _ viewTemplate: Override this method to change the template for the View page
  • _editTemplate : Override this method to change the template for the Edit page
  • _deleteTemplate: Override this method to change the template for the Delete page
  • _createTemplate: Override this method to change the template for the Create contacts page
  • _showAllTemplate: Override this method to change the template for the List contacts all page

You can also disable the menu entries by defining it's LocParam as Empty; for example, you can disable the delete menu item by overriding the deleteMenuLoc method and making it return Empty - override def deleteMenuLoc = Empty.

If you do this and try to click on the Delete link, you will get an HTTP response with status code 404 Not Found .

Paginating result sets

Sometimes, we need to deal with so much data that presenting them at once to the user is not an option. The reasons are as follows:

  • It will consume too many resources
  • It will be slow to render the page to the user
  • The user is probably not interested in all the data but just a small part of it

This is why we paginate result sets, to make the user's life easier.

In this section, we will see how to paginate a page using another built-in feature from Lift.

Getting ready

We will use the project from the previous recipe. You can duplicate it or modify the same project.

How to do it...

Carry out the following steps to paginate result sets:

  1. In the Contacts snippet, add the following import statements:

    import net.liftweb.http.PaginatorSnippet import net.liftweb.mapper.{MaxRows, StartAt}

  2. Modify the Contacts snippet declaration from:

    class Contacts {

    To:

    object Contacts extends PaginatorSnippet[Contact]

  3. Add the count method.

    def count = Contact.count

  4. Override the itemsPerPage method:

    override def itemsPerPage = 3

  5. Create the page method as follows:

    override def page = Contact.findAll( StartAt(curPage * itemsPerPage), MaxRows(itemsPerPage) )

  6. Modify the list method by replacing the invocation to Contact.findAll() by the invocation to the page method.

    def list = { "li *" #> page.map { c => c.name.get } } }

  7. Start the application.
  8. Access http://localhost:8080.

Now you will see a web page similar to the following screenshot:

How it works...

The PaginatorSnippet trait provides everything we need to add pagination to a given web page. It calculates how many pages are necessary, the links to the first and last pages, and also the links to the previous and next pages.

The only things we need to do are as follows:

  • Define the count method, which defines the total number of records the collection that will be paginated has
  • Override the page method, which is used to retrieve the slice of the collection that will be shown in the current page

We have also overridden the itemsPerPage method because we didn't want to use the default value, which is 20.

The page method uses the curPage method, which is provided by the PaginatorSnippet trait and controls the current page that is being shown to the user. So, by multiplying the value returned from the curPage method by the value returned from the itemsPerPage method, we can figure out what should be the first record we need to retrieve from the database.

We have also used two query parameters to delimit the range of records that the Mapper should retrieve from the database: the StartAt class defines the start point, and the MaxRows class defines how many records should be retrieved. This is all it takes in the snippet side to add pagination to a web page. Now, let's take a look at what is necessary to do on the HTML side.

To render the pagination controls in HTML, we need to invoke the paginate method from the PaginatorSnippet - <div data-lift="Contacts.paginate">. This method will bind the nav tags and will generate the HTML necessary to create the controls. It will also generate the links with the correct values so that the user can navigate through the web pages.

Each XML tag will generate one control to navigate between the pages :

  • The nav:first tag will generate the link that takes the user to the first page, it is represented by the << symbol
  • The nav:last tag will generate the link represented by the >> symbol, which takes the user to the last page
  • The nav:prev and nav:next tags will generate the link to go to the previous and next pages respectively. They are represented by the < and > symbols
  • The nav:allpages tag will generate all the page numbers with a link so that the user can go directly to the desired page—the current page is represented by a number with no link
  • The nav:records tag renders the text Displaying start-end of total , where start is the number of the first record of the current page, end is the number of the last record, and total is the number of elements in the collection represented by the count method

There's more...

You can change the symbols that represent the pagination controls by overriding the following methods, which should return a NodeSeq object:

  • prevXml: It is used for changing the previous page control symbol
  • nextXml: It is used for changing the next page control symbol
  • firstXml: It is used for changing the first page control symbol
  • lastXml: It is used for changing the last page control symbol

Using an in-memory database in application tests

In the previous recipes, we have learned how to use Mapper to map database tables into Scala classes and how to use those classes to communicate with the database.

However, there is a small problem; how to test our application without messing with the database. In other words how can we use a database that provides a controlled environment for tests.

The answer to this is an in-memory database that we can reset every time before running the tests.

Getting ready

We will use the project from the previous section. You can duplicate it or use the same project. You will first need to carry out the following steps:

  1. Add the following line in librariesDependenciesSeq in the build.sbt file:

    "com.h2database" % "h2" % "1.3.167" % "test"

  2. Add the following line at the end of the build.sbt file:

    parallelExecution in Test := false

  3. Create a file named test.default.props in props under src/test/resources/ with the following content:

    db.driver=org.h2.Driver db.url=jdbc:h2:mem:liftbook db.user=sa db.password=sa

  4. Create a trait named InMemoryDB in lib under src/test/scala/code/ with the following code:

    package code.lib import code.model.Contact import net.liftweb.http.LiftRules import net.liftweb.mapper._ import net.liftweb.util.Props trait InMemoryDB { def prepareDB_!() { for { driver <- Props.get("db.driver") url <- Props.get("db.url") } { val vendor = new StandardDBVendor(driver, url, Props.get("db.user"),
    Props.get("db.password")) LiftRules.unloadHooks.append(vendor.closeAllConnections_! _) DB.defineConnectionManager(DefaultConnectionIdentifier, vendor) } Schemifier.schemify( true, Schemifier.infoF _, Contact ) } def prepareContacts_!() { Contact.findAll().map(_.delete_!) val contactsNames = "John" :: "Joe" :: "Lisa" :: "Dave" :: "Sam" :: Nil contactsNames.map(Contact.create.name(_).save()) } prepareDB_!() prepareContacts_!() }

How to do it...

Now carry out the following steps:

  1. Create a class named ContactsTest in snippet under src/test/scala/code/ with the following code:

    package code.snippet import org.specs2.mutable.Specification import code.lib.InMemoryDB class ContactsTest extends Specification with InMemoryDB { "concats snippet" should { "retrieve three contacts" in { val contacts = Contacts.page contacts.size must be equalTo(3) contacts.map(_.name).mkString(",") must be equalTo("John,Joe,Lisa") } } }

  2. Start an sbt session.
  3. Run the test command. After running the test, you should get a result similar to the following screenshot:

How it works...

Since we don't have a JNDI datasource because we are not running the tests with a running jetty instance, we need to configure the database using the JDBC URL.

To configure it, we defined the test.default.props file under the src/test/resources folder so that the Props object could find it and read the values it should use to configure the connection. You can see that the URL we used is jdbc:h2:mem:liftbook. This means that we'll connect to an H2 database whose name is liftbook. The mem parameter is there to tell the H2 database that it should create an in-memory database instead of creating a database in the filesystem.

The InMemoryDB trait was created to provide the tests with a convenient way to get the database connection for the tests. All you'll need to do is to mix this trait in your test classes and they will have a connection available.

As you can see, this is exactly what we did in the ContactsTest specification, and since the connection to the database is available and the InMemoryDB trait has created some contacts for us, we can test whether the Contacts snippet is doing what it is supposed to do. Since we've invoked the page method directly, the PaginatorSnippet trait will assume that we are in the first page; so, the page method will only return the first three contacts.

There's more...

The modification we did in the build.sbt file to prevent parallel execution of tests, that is, parallelExecution in Test := false, was to avoid issues during the preparation of the database between the execution of tests.

Imagine if we have two or more tests and they are executed in parallel; this means that while we are executing one test and an instance of the database is created, another test can try to create the same schema in the database where one already exists. This will cause Schemifier to throw an exception and the test will fail. This is why we don't want our tests to run in parallel.

Summary

This article explains how to use Mapper to integrate the application with a database. It also covers the basics of ORM mapping and how to use an in-memory database to test an application that uses Mapper.

Resources for Article:


Further resources on this subject:


Lift Application Development Cookbook Over 50 practical recipes to build web applications using Lift, the most secure web framework available with this book and ebook
Published: September 2013
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:

About the Author :


Gilberto T. Garcia Jr.

Gilberto T. Garcia Jr. has a Bachelor's degree in Philosophy from USP, and has been working with Internet-related technologies since 1999. He had worked with different programming languages in several projects with different sizes and complexities.

As a person who enjoys learning new things, he started to study and work with Scala and Lift in 2010.

Books From Packt


Learning Play! Framework 2
Learning Play! Framework 2

Play Framework Cookbook
Play Framework Cookbook

Instant Play Framework Starter [Instant]
Instant Play Framework Starter [Instant]

Vaadin 7 Cookbook
Vaadin 7 Cookbook

Learning Vaadin
Learning Vaadin

Instant Lift Web Applications How-to [Instant]
Instant Lift Web Applications How-to [Instant]

PlayStation®Mobile Development Cookbook
PlayStation®Mobile Development Cookbook

Learning Vaadin 7: Second Edition
Learning Vaadin 7: Second Edition


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software