Play provides a set of very useful helpers to simplify the management of your JPA entities.

Note that you can still go back to the plain JPA API whenever you want.

Starting the JPA entity manager

Play will automatically start the Hibernate entity manager when it finds one or more classes annotated with the @javax.persistence.Entity annotation. However, make sure that you have correctly configured a JDBC datasource or it will fail.

Obtaining the JPA entity manager

When the JPA entity manager is started you can get it from the application code, using the JPA helper. For example:

public static index() {
Query query = JPA.em().createQuery("select * from Article");
List<Article> articles = query.getResultList();
render(articles);
}

Transaction management

Play will automatically manage transactions for you. It will start a transaction for each HTTP request and commit it when the HTTP response is sent. If your code throws an exception, the transaction will automatically rollback.

If you need to force transaction rollback from the application code, you can use theJPA.setRollbackOnly() method.

You can also use annotations to specify how transactions should be handled.

If you annotated the method in the controller with @play.db.jpa.Transactional(readOnly=true), then the transaction will be read-only.

If you want to prevent Play from starting a transaction at all, you can annotated the method with@play.db.jpa.NoTransaction.

To prevent transactions for all methods, you can annotate the Controller-class with@play.db.jpa.NoTransaction.

When using @play.db.jpa.NoTransaction, Play does not get a connection from the connection pool at all – which improves speed.

The play.db.jpa.Model support class

This is the main helper class for JPA. If you make one of your JPA entities extend theplay.db.jpa.Model class, it will give you a lot of helper methods to simplify the JPA access.

For example, look at this Post model object:

@Entity
public class Post extends Model {

public String title; public String content; public Date postDate; @ManyToOne public Author author; @OneToMany public List comments; }

The play.db.jpa.Model class automatically provides an autogenerated Long id field. We think that it’s generally a good idea to keep an auto-generated Long id as primary key for JPA models (the technical primary key) and manage your functional primary key using another field.

Note that we have used the fact that Play automatically considers the Post class’ public members asproperties. So we don’t need to write all setter/getter methods for this object.

Custom id mapping with GenericModel

Nothing forces you to base your entities on play.db.jpa.Model. Your JPA entities can also extend theplay.db.jpa.GenericModel class. This is required if you do not want to use a Long id as the primary key for your entity.

For example, here is a mapping for a very simple User entity. The id is a UUID, the name and mailproperties are required, and we use Play Validation to enforce simple business rules.

@Entity
public class User extends GenericModel {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
public String id;

@Required public String name; @Required @MaxSize(value=255, message = “email.maxsize”) @play.data.validation.Email public String mail; }

Finding objects

The play.db.jpa.Model gives you several ways to find data. For example:

Find by ID

The simplest way to find an object.

Post aPost = Post.findById(5L);

Find all

List<Post> posts = Post.findAll();

This is the simplest way to retrieve all posts, but you can do the same using:

List<Post> posts = Post.all().fetch();

This allows you to paginate results:

List<Post> posts = Post.all().fetch(100); // 100 max posts

or even,

List<Post> posts = Post.all().from(50).fetch(100); // 100 max posts start at 50

Find using a simplified query

That allows you to create very expressive finders, but will only work for simple queries.

Post.find("byTitle", "My first post").fetch();
Post.find("byTitleLike", "%hello%").fetch();
Post.find("byAuthorIsNull").fetch();
Post.find("byTitleLikeAndAuthor", "%hello%", connectedUser).fetch();

Simple queries follow the following syntax “[Property][Comparator]And?” where Comparator can be the following:

  • LessThan – less than the given value
  • LessThanEquals – less than or equal a give value
  • GreaterThan – greater than a given value
  • GreaterThanEquals – greater than or equal a given value
  • Like – Equivalent to a SQL like expression, except that the property will always convert to lower case.
  • Ilike – Similar to a Like, except case insensitive, meaning that your argument will convert to lower case too.
  • Elike – Equivalent to a SQL like expression, no conversion.
  • NotEqual – Negates equality
  • Between – Between two values (requires two arguments)
  • IsNotNull – Not a null value (doesn’t require an argument)
  • IsNull – Is a null value (doesn’t require an argument)

Find using a JPQL query

You can use a JPQL query:

Post.find(
"select p from Post p, Comment c where c.post = p and c.subject like ?", "%hop%"
);

or even a part of:

Post.find("title", "My first post").fetch();
Post.find("title like ?", "%hello%").fetch();
Post.find("author is null").fetch();
Post.find("title like % and author is null", "%hello%").fetch();
Post.find("title like % and author is null order by postDate", "%hello%").fetch();

You can even specify only the order by statement:

Post.find("order by postDate desc").fetch();

Counting objects

You can easily count objects.

long postCount = Post.count();

Or even count using a query:

long userPostCount = Post.count("author = ?", connectedUser);

Explicit save

Hibernate maintains a cache of Objects that have been queried from the database. These Objects are referred to as persistent Objects as long as the EntityManager that was used to fetch them is still active. That means that any changes to these Objects within the bounds of a transaction are automatically persisted when the transaction is committed. In standard JPA, these updates are implicit within the transaction’s boundary; you don’t have to explicitly call any method to persist the values.

The main downside is that we must manage all of our Objects manually. Instead of telling the EntityManager to update an Object (which would be far more intuitive), we must tell the EntityManager which Objects NOT to update. We do this by calling refresh(), which essentially rolls back a single entity. We do this just prior to calling commit on the transaction or when we realise the Object shouldn’t be updated.

Here is a common use case, when editing a persistent object after a form submit:

public static void save(Long id) {
User user = User.findById(id);
user.edit("user", params.all());
validation.valid(user);
if(validation.hasErrors()) {
// Here we have to explicitly discard the user modifications...
user.refresh();
edit(id);
}
show(id);
}

From what I’ve seen, most developers are not aware of this, and forget to discard the object state in case of errors, assuming that the object will not be saved without an explicit call to save().

So that’s exactly what we’ve changed in Play. All the persistent objects extending the JPASupport/JPAModel will not be saved without an explicit call to the save() method. So you can actually rewrite the previous code as:

public static void save(Long id) {
User user = User.findById(id);
user.edit("user", params.all());
validation.valid(user);
if(validation.hasErrors()) {
edit(id);
} else{
user.save(); // explicit save here
show(id);
}
}

This is far more intuitive. Moreover since it could be tedious to explicitly call save() on a large object graph, the save() call is automatically cascaded to the relationships annotated with thecascade=CascadeType.ALL attribute.

More about generic typing problems

The play.db.jpa.Model defines a set of generic methods. These generic methods use a type parameter to specify the method’s return type. When using those methods, the concrete type to be used as return value is derived from the invocation context using type inference.

For example, the findAll method is defined as:

<T> List<T> findAll();

And you use it as:

List<Post> posts = Post.findAll();

Here the Java compiler resolves the actual type of T using the fact that you assign the method result to a List<Post>. So T is resolved as a Post type.

Unfortunately, this doesn’t work if the generic method’s return value is directly used as a parameter for another method invocation or used in a loop. So the following code fails with a compiler error saying “Type mismatch: cannot convert from element type Object to Post”:

for(Post p : Post.findAll()) {
p.delete();
}

Of course you can resolve this issue using a temporary local variable, as:

List<Post> posts = Post.findAll(); // type inference works here!
for(Post p : posts) {
p.delete();
}

But wait, there is a better way. You can use a practical but somewhat unknown Java language feature, which makes the code shorter while more readable at the same time:

for(Post p : Post.<Post>findAll()) {
p.delete();
}

Continuing the discussion

Now we’ll check some Play libraries.

JPA persistence的更多相关文章

  1. IntellJ 13.x JPA Persistence Sample

    跟上一篇差不多,一些基本的东西. 这次是JPA + Spring MVC 3.0 1.建立Project 2.Add JPA Support 3.我们以Hibernate为例,设置JPA的Provid ...

  2. Spring data jpa persistence .xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://j ...

  3. jpa注解

    http://www.oracle.com/technetwork/cn/middleware/ias/toplink-jpa-annotations-100895-zhs.html#ManyToOn ...

  4. JPA 批注参考

    body, p, th, td, li, ul, ol, h1, h2, h3, h4, h5, h6, pre { font-family: simsun; line-height: 1.4; } ...

  5. 摘抄JPA官方文档原文 防呆

    Spring Data JPA - Reference Documentation Oliver GierkeThomas DarimontChristoph StroblMark PaluchVer ...

  6. Spring Boot:整合Spring Data JPA

    综合概述 JPA是Java Persistence API的简称,是一套Sun官方提出的Java持久化规范.其设计目标主要是为了简化现有的持久化开发工作和整合ORM技术,它为Java开发人员提供了一种 ...

  7. [转] JPA 2.0 with EclipseLink - 教程

    原文: http://www.vogella.com/articles/JavaPersistenceAPI/article.html Lars Vogel Version 2.2 Copyright ...

  8. 领域对象模型(domain object model)

    在Play程序中,模型(model)占据了核心地位.它是程序操作的信息的特定领域的表现方式. Martin Fowler这样定义模型: 负责表达业务概念,业务状态信息以及业务规则.尽管保存业务状态的技 ...

  9. 【原】Kryo序列化篇

    Kryo是一个快速有效的对象图序列化Java库.它的目标是快速.高效.易使用.该项目适用于对象持久化到文件或数据库中或通过网络传输.Kryo还可以自动实现深浅的拷贝/克隆. 就是直接复制一个对象对象到 ...

随机推荐

  1. 【博客美化】08.添加"扩大/缩小浏览区域大小" 按钮

    博客园美化相关文章目录: [博客美化]01.推荐和反对炫酷样式 [博客美化]02.公告栏显示个性化时间 [博客美化]03.分享按钮 [博客美化]04.自定义地址栏logo [博客美化]05.添加Git ...

  2. javascript类型系统——包装对象

    × 目录 [1]定义 [2]生存期 [3]显式创建[4]转型函数[5]比较运算 前面的话 javascript对象是一种复合值,它是属性或已命名值的集合.通过'.'符号来引用属性值.当属性值是一个函数 ...

  3. poj 2777(线段树的节点更新策略)

    /* 之前的思想是用回溯的方式进行颜色的更新的!如果用回溯的方法的话,就是将每一个节点的颜色都要更新 通过子节点的颜色情况来判断父节点的颜色情况 !这就是TLE的原因! 后来想一想没有必要 !加入[a ...

  4. Redis在win7上的可视化应用

    Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...

  5. [转载]TFS测试管理

    微软2010年发布的Visual Studio 2010或Visual Studio Test Professional 2010包含一个称为 Microsoft 测试管理器的新应用程序,用于帮助您使 ...

  6. impdp导入报错ORA-14460: only one COMPRESS or NOCOMPRESS clause may be specified

    迁移环境 源:Solaris 10 + Oracle 11.2.0.3 目标:Solaris 10 + Oracle 11.2.0.1 导出命令: expdp user/pwd directory=j ...

  7. 【小型系统】抽奖系统-使用Java Swing完成

    一.需求分析 1. 显示候选人照片和姓名. 2. 可以使用多种模式进行抽奖,包括一人单独抽奖.两人同时抽奖.三人同时抽奖. 3. 一个人可以在不同的批次的抽奖中获取一.二.三等奖,但是不能在同一批次抽 ...

  8. 【知识积累】爬虫之网页乱码解决方法(gb2312 -> utf-8)

    前言 今天在测试爬虫项目时,发现了一个很严肃的问题,当爬取的网页编码格式为gb2312时,按照一般的办法转化为utf-8编码时总是乱码,PS:爬取的所有网页无论何种编码格式,都转化为utf-8格式进行 ...

  9. ansible入门

    前言 最近看了一下ansible,挺火的一个配置管理工具,对比老大哥puppet,使用起来要简单一些,并且可以批量执行命令,对比同是python语言编写的saltstack,不需要安装客户端(基于pa ...

  10. 实现jquery.ajax及原生的XMLHttpRequest调用WCF服务的方法

    废话不多说,直接讲解实现步骤 一.首先我们需定义支持WEB HTTP方法调用的WCF服务契约及实现服务契约类(重点关注各attribute),代码如下: //IAddService.cs namesp ...