Overview

Active record is a pattern originally named by Martin Fowler and is not specific to Ebean but instead a a pattern common in many persistence frameworks.

In general terms it means the entity beans have the capability to save() and delete() by themselves. You can contrast this to a traditional DAO approach where the entity beans are instead passed to a DAO for saving and deleting.

Play framework

For Ebean the active record pattern has been inherited from the Play framework. The original Model and Finder implementations found in Ebean to implement the active record pattern originated from Play framework and that code has migrated over into Ebean.

Criticism

The two main criticisms to the active record pattern are:

  • Combining concerns - The 'model' and 'how it is persisted' are tightly combined
  • Testability - Active record is in theory harder to test

It should be noted that Ebean provides a testing mechanism via ebean-mocker such that the active record patterns for both saving and finding are fully testable.

Rob's view: JPA and active record

My opinion is that JPA is not well suited to the active record pattern. This is based on my personal experience with Grails/GORM/Hibernate. Specifically, GORM is effectively hiding the existence of Hibernate Session from the developer. My experience suggests that this works well with simple cases but in more complex case developers find a situation where they need to know about and deal with the Session/EntityManager object explicitly.

The save() delete() semantics of active record don't match the underlying attach/detach/flush semantics of JPA and in the more complex cases that mismatch comes out (abstraction leaks). This typically results in a change to both coding style and thinking and a code base containing both styles.

Rob's view: Ebean and active record

EbeanServer is a "session-less" ORM and specifically unlike JPA there are no attach/detach semantics. This makes Ebean well suited to the active record pattern as there is no mismatch between the active record API/abstraction and that of the underlying EbeanServer.

The complex persistence cases with Ebean means you want/need full control over JDBC batch mechanism. In these more complex cases the active record save() and delete() still work as expected and desired but instead you additionally adjust JDBC batch control via the transaction (programmatically or via @Transactional). That is, there is no need to change style in complex cases.

Testing

Ebean provides the ebean-mocker project and that supports the testing requirements when using active record pattern. It supports using test doubles for EbeanServer in both persisting and finding and provides DelegateEbeanServer as a good out of the box test double for persisting and finding.

DelegateEbeanServer provides

  • Capture persisting calls, assert saved beans etc
  • findById stub/mock responses
  • static Finder stub/mock test doubles
  • SQL capture

Rob's view: Active record benefits

For some people active record combines concerns and that ends the conversation and that is fine.

Not missing out on testability

Personally when I compare what I call the traditional DAO/Inject approach with active record I note firstly that I don't lose out on testability - Active record with Ebean can be just as testable as the DAO/Inject style.

Simple things kept simple

Secondly the major plus to active record is that "simple things" are kept simple. What I mean by that is save(), delete(), get reference, find by id and find by unique key are all very simple and very clean in code. In DAO/Inject code what I have seen is that for each type of entity bean you inject a DAO - for a service that deals with 4 different entity bean types you then end up injecting 4 DAOs. For constructor injection in particular injecting DAO's can lead to relatively ugly constructors and 'workarounds' very quickly.

With active record, the simple things are kept simple

In balance I'd choose active record

If someone asks me whether to choose between active record style or traditional DAO/Inject style I would suggest the active record style due to the very nice clean simple code that results. We don't miss out on testability (due to ebean-mocker) and I'm ok that the entity beans have save() delete() methods (mixed concerns).

Model

When your entity beans extend the io.ebean.Model object they inherit some convenience methods save(), delete() etc. When these methods are called internally the default EbeanServer is used to perform the save() and delete().

Ebean Model provides the following convenience methods:

  • save() - Saves the entity
  • update() - Explicitly update the entity
  • insert() - Explicitly insert the entity
  • delete() - Delete the entity
  • refresh() - Refreshes the entity from the database
  • markAsDirty() - this is used so that when a bean that is otherwise unmodified is updated the version property is updated
  • update(String server) - Update the entity using the specified Ebean server
  • insert(String server)- Insert the entity using the specified server
  • delete(String server)- Delete the entity using the specified server

Example: extend Model

/**
* Extend Model to get the save(), delete() etc
* methods on the bean itself (active record style).
*/
@Entity
@Table(name="customer")
public class Customer extends Model {

@Id
Long id;

String name;

Date registered;

String comments;

...

Example: save()

Customer customer = new Customer();
customer.setName("Rob");

customer.save();

Finder

Finder provides some convenience query methods but also it suggests a way to organise 'finder' code.

Example: find by id

Customer customer = Customer.find.byId(42);

Example: get reference

Customer customer = Customer.find.ref(42);

Example: delete by id

Customer.find.deleteById(42);

Example: find where

List<Customer> customers =
  Customer.find
  .where().ilike("name", "rob%")
  .findList();