Play框架拾遗之四:域模型与JPA支持
Play框架拾遗之四:域模型与JPA支持
刀狂剑痴 发表于2年前
Play框架拾遗之四:域模型与JPA支持
  • 发表于 2年前
  • 阅读 930
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 十分钟定制你的第一个小程序>>>   

1、属性模拟

对于model,Play中字段都是public修饰符,但框架会自动生成setter与getter如:

@Entity
@Table(name="product ")
public class Product extends BaseModel{
  @Id
  public Long id;
  public String name;
}

运行play 得到class文件,用javap命令可以看到:

E:\>javap PrintTemplateType
Compiled from "Product .java"
public class models.Product extends models.BaseModel{
public java.lang.Long id;
public java.lang.String name;
public models.Product ();
public java.lang.Long getId();
public void setId(java.lang.Long);
public java.lang.String getName();
public void setName(java.lang.String);
... //后面省略一些继承父类的方法
}

另外其他地方有引用这样字段也会替换成setter与getter。 如果有已经有自定义方法,那么会框架会优先选择手动编写的方法。

2、数据持久化

Play 1.x持久层采用的是Hibernate,在使用注解实体时,应该使用JPA包中的@Entity而不是Hibernate中的。 可以直接从play.db.jpa.JPA对象中得到实体管理器,通过实体管理器可以将Model持久化到数据库或者执行HQL语句,例如:

EntityManager em = JPA.em();
em.persist(product);
em.createQuery("from Product where price > 50").getResultList();

Play实体采用ActiveRecord模式,将Struts中的dao与pojo合并,ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。Play也提倡使用ActiveRecord模式进行快速开发,其主要思想是:

  • 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field;

  • ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;

  • ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑。

3、无状态模型

Play被设计成为“无共享”的架构,目的就是为了保持应用的完全无状态化。这样做的好处在于可以让一个应用同一时刻在多个服务器节点上运行。lay为了保持模型无状态化,需要避免一些常见的陷阱,最重要的就是不要因为多请求而将对象保存到Java堆中。使用缓存可以是一种非常好的选择,也是Java Servlet Session的良好的替代方案。

4、JPA支持

JPA(Java Persistence API)是Java持久化的规范,但JPA本身并不是持久化的具体实现。目前JPA的实现主要有:Hibernate,OpenJPA, Toplink,JDO等。Play默认使用了Hibernate的实现,JPA作为一种规范,因此也可以使用其他任意的JPA实现来替代Hibernate。 JPA实体管理器启动后,程序代码中就可以通过JPA辅助类来取得被管理的实体。例如:

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

事务(Transaction)是访问并可能更新数据库中各种数据项的程序执行单元。Play会自动进行事务管理,在每次发送HTTP请求时自动开启事务,发送HTTP响应完毕后提交事务。如果程序在request/response过程中抛出了异常,事务会自动回滚。当然也可以显式调用JPA.setRollbackOnly()方法通知JPA不要提交当前事务,而在代码中对事务进行强制回滚。 play.db.jpa.Model 类提供多种方法用于数据查找, 采用以下写法可以对查询结果进行分页:

// 最多匹配100篇 post
List<Post> posts = Post.all().fetch(100);

//从第50篇post开始查找,并且最多匹配到第100篇post
List<Post> posts = Post.all().from(50).fetch(100);

Play提供的count()方法具有统计查询对象结果的功能:
long postCount = Post.count();
long userPostCount = Post.count("author = ?", connectedUser);

在Play中也可以书写完整的JPQL语句进行查询:

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

也可以在JPQL语句中只写条件部分的查询:

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();
Post.find("order by postDate desc").fetch();

Hibernate会将数据库中查询到的结果以对象缓存的形式维护起来。当实体管理器在进行查询匹配等操作的过程中,数据一直作为持久对象存在。这意味着如果事务没有提交,对象的任何改变都将自动持久化到数据库中。JPA的规范是这样定义的:对象的修改默认与事务过程一致,所以无需显式地调用任何方法就可以持久化修改后的数据。 这种全自动的持久化管理,也有不足的地方,因为我们并不总是希望对象一旦修改就被持久化。所以与其通知实体管理器更新一个对象,还不如告诉它哪些对象不需要更新。refresh()方法可以回滚单个实体,在事务提交之前调用,对象将不被持久化。如:

public static void save(Long id) {
  User user = User.findById(id);
  user.edit("user", params.all()); //修改持久化的对象
  validation.valid(user);
  if(validation.hasErrors()) {
    // 需要显式抛弃user对象
    user.refresh();
    edit(id);
  }
  show(id);
}
标签: JPA
共有 人打赏支持
粉丝 18
博文 111
码字总数 82582
×
刀狂剑痴
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: