playframework1.2.x 入门(三)编写Model类

本篇为playframework入门引导的第三篇,主要介绍如何使用JPA,本篇将以博客网门为例说明。

JPA介绍

model层是Play应用程序的核心部份,它划定了我们应用中所使用的特定领域domain-specific。比如我们创建一个博客应用,model层就会包括用户User,文章Post,评论Comment等类。

因为大部份model对象都需要在应用重启后保留下来,我们必须要把他们保存在一个持久化的数据存储中。大众的选择是使用关系型数据库,但是由于Java是一个面向对象的编程语言,我们可以使用对象关系映射object-relational mapper来减少麻烦。

Java Persistence API,即JPA是Java特定用于处理对象关系映射的标准API。Hibernate框架是众所周知的JPA实现。使用JPA的一个好处是,Hibernate API的映射关系是直接声明在Java对象中。

如果你用过Hibernate或者JPA,你会感到惊喜,Play将两者简单的结合在一起,不需要任何配置,JPA开箱即用。如果你未接触过JPA,可以阅读 some of these simple presentations

Model类—User


package models;

import play.db.jpa.Model;

import javax.persistence.Entity;

@Entity
public class User extends Model {
    public String email;
    public String password;
    public String fullName;
    public boolean isAdmin;

    public User(String email, String password, String fullName, boolean isAdmin) {
        this.email = email;
        this.password = password;
        this.fullName = fullName;
        this.isAdmin = isAdmin;
    }
}

@Entity 注解标识这个类由JPA实体管理,User的父类Model提供了很多有用的JPA帮助方法。类中所有的成员都可以自动持久化至数据库中。

  • 表名默认是User,user在某些数据中是预留的关键字,可以通过使用@Table注解来更改表名。@Table(name="blog_user")

  • 实体类并不是必须要继承play.db.jpa.Model,也可以使用普通的JPA方式,但是继承Model类相对要简单很多。

如果你使用过JPA,你会知道每个JPA实体都必须要提供一个@Id属性。play的Model类提供了自动生成主键方式,在绝大多数情况下都是够用的。

  • 不要把id属性即用作功能的标识又用作技术性的标识,较好的作法是将功能性和技术性的标识分开。自动生成的ID通常作为技术情标识。

现在,如果你是一个有经验的Java开发者,你会认为所有属性都是公共的很疯狂。Java,作为一种纯面像对象的语言,最佳实践会告诉你把所有的属性都使用private声明和提供公共的访问方式。事实上,Play会为你自动生成getter,setter保留封装。在下面的教程中,我会提及到。

测试

使用和play run类似的命令行play test可以在浏览器中测试应用。

访问地址可以看到所有的测试


http://localhost:9000/@tests

对于model 测试,我们还可以使用JUnit测试,在生成的项目中默认已经生成一个测试用例。BasicTests.java


import org.junit.*;
import java.util.*;
import play.test.*;
import models.*;

public class BasicTest extends UnitTest {

    @Test
    public void aVeryImportantThingToTest() {
        assertEquals(2, 1 + 1);
    }

}

现在我们来创建一个User类的测试。在BaseTest中添加如下代码


@Test
public void createUser() {
    new User("cgz@gmail.com", "124", "gzPAPA", true).save();
    User cgz = User.find("byEmail", "cgz@gmail.com").first();
    assertNotNull(cgz);
    assertEquals("gzPAPA", cgz.fullName);

}
  • 这里的save(),find()都是Model提供的方法

实体中的自定义方法:

User.java中添加较验帐号密码的方法check()


public static User check(String email, String password) {
    return find("byEmailAndPassword", email, password).first();
}

Unit Test中


@Test
public void testCheckUser() {
    new User("cgz@gmail.com", "124", "gzPAPA", true).save();
    assertNotNull(User.check("cgz@gmail.com", "124"));
}

model类—Post文章类


package models;

import play.db.jpa.Model;

import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.util.Date;

@Entity
@Table(name = "tb_post")
public class Post extends Model {
    public String title;
    public Date postedAt;

    @Lob
    public String content;

    @ManyToOne
    public User author;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
    public List<Comment> comments;

    public Post(String title, Date postedAt, String content, User author) {
        this.comments = new ArrayList<String>();
        this.title = title;
        this.postedAt = postedAt;
        this.content = content;
        this.author = author;
    }
    public Post previous() {
        return Post.find("postedAt < ? order by postedAt desc", postedAt).first();
    }

    public Post next() {
        return Post.find("postedAT > ? order by postedAt asc", postedAt).first();
    }

    public Post addComment(String author, String content) {
        Comment comment = new Comment(this, author, content).save();
        this.comments.add(comment);
        this.save();
        return this;
    }
}
  • 这里的@Lob注解告诉JPA使用大文本类型存储文章的内容。使用@ManyToOne意味着每加载一篇文章都会加载一个用户实体。创建新的用例去测试Post类,但是在此之前,数据库的内容并不会被删掉,所以新加入的model,需要先执行deteted database

@Before

public void setup() {

    Fixtures.deleteDatabase();

}
  • @Before注解使在执行所有用例之前先执行此标注的方法。Fixtures类是一个处理测试数据库的工具。

测试代码:


@Test
public void testCreatePost() {
    User cgz = new User("cgz@gmail.com", "124", "gzPAPA", true);
    Logger.info("before save id:" + cgz.id);
    cgz.save();
    new Post(cgz, "my first post", "hello world", new Date()).save();
    Logger.info("after save id:" + cgz.getId());
    new Post(cgz, "my twice post", "hello world 2", new Date()).save();

    List<Post> posts = Post.find("byAuthor", cgz).fetch();

    assertEquals(posts.size(), 2);
    Post post = posts.get(0);
    assertNotNull(post.content);
}
  • 这里要将一个对象持久化,其所有的属性都必须先持久化,例如调用Post.save(),传进去的user实例必须是数据库对象

model—Comments 评论类


package models;

import play.db.jpa.Model;

import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.util.Date;

@Entity
@Table(name = "tb_comments")
public class Comment extends Model {
    public String author;
    public Date postedAt;

    @Lob
    public String content;

    @ManyToOne
    public Post post;

    public Comment(String author, String content, Post post) {
        this.author = author;
        this.postedAt = new Date();
        this.content = content;
        this.post = post;
    }
}

编写测试:


@Test
public void testPostComments() {
    User cgz = new User("cgz@gmail.com", "124", "gzPAPA", true).save();
    Post post = new Post(cgz, "my first post", "hello world", new Date()).save();
    new Comment(post, "Jerry", "good job").save();
    new Comment(post, "Tom", "labshi job").save();
    List<Comment> comments = Comment.find("byPost", post).fetch();
    assertNotNull(comments);
    assertEquals(comments.size(), 2);
}

使用Fixtures编写更复杂的测试

当你开始写复杂用例测试时,你可能需要事先存在一些数据。Fixtures可以使用YAML文件描述你的model

创建/app_1/test/data.yml文件,加入以下内容


User(cgz):

    email:bob@gmail.com

    password:secret

    fullName:gzPAPA

如果你需要更多的测试数据,可以点击这里下载

现在我们创建新的用例:


@Test
public void fullTest() {
    Fixtures.loadModels("data.yml");
    assertEquals(2, User.count());
    assertEquals(3, Post.count());

    Post frontPost = Post.find("order by postedAt desc").first();
    User user = frontPost.author;
    List<Comment> comments = Comment.find("byPost", frontPost).fetch();
    assertEquals(2, comments.size());
    long postsNum = Post.count("byAuthor", user);
    assertEquals(2, postsNum);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值