06.域对象模型

来源:互联网 发布:linux cp参数个数 编辑:程序博客网 时间:2024/05/16 04:47

模型是play应用程序中核心。是应用程序操作的信息的特定领域呈现。

Martin Fowler将之定义为:

模型层主要负责表现商业内容、商业状态和商业规则的信息。在这里主要进行商业状态控制和作用,相应技术细节则委托给基础设施。这个层是商业软件的最重要部分。

通用的java反映模式用许多简单的java Bean来映射模型以保存数据,应用程序逻辑被放入“service”层,用于操作模型对象。

Martin fowler把这种反映模式命名为贫血对象模型

贫血对象模型的基本特征就是外表看起来就是一个真实的事物,但在模型里几乎没有行为,只有getter和setter,不能在模型对象里放入逻辑。模型的行为通过许多包括有域逻辑的service对象来实现。

这样的模型是和OO相反的,OO对象既有数据也有对象的方法。

属性模仿

在play里,类的变量都是public的。这引起java开发界的一些质疑。在java的标准教程里,为了对数据进行封闭,要求属性都是private的。这导致了一些批评。

java并没有真正的内建属性定义系统。只是一个Java Bean的约定:在java对象里的属性都要有getter和setter方法,如果属性是只读的,那么只能有getter。

虽然系统可以很好地工作,但编码十分乏味。每个属性都必须声明为私有变量并为此书写getter和setter。因此,许多时候,getter和setter实现都是相同的:

private String name;

 

public String getName() {

    return name;

}

 

public void setName(String value) {

    name = value;

}

在play里,play会为模型自动生成这些内容,以保证代码清晰。事实上,所有public变量都是实例属性。在play里约定为:类的任何public,non-static,no-final域都将以属性对待。

比如:

public class Product {

 

    public Stringname;

    public Integerprice;

}

类在加载时,就变成了如下内容:

public class Product {

 

    public Stringname;

    public Integerprice;

 

    public StringgetName() {

        returnname;

    }

 

    public voidsetName(String name) {

        this.name =name;

    }

 

    public IntegergetPrice() {

        returnprice;

    }

 

    public voidsetPrice(Integer price) {

        this.price= price;

    }

}

要访问一个属性里,只需要以下代码:

product.name = "My product";

product.price = 58;

在加载时会自动翻译为:

product.setName("My product");

product.setPrice(58);

注意!

在自动生成方式下,不能直接作用getter和setter方法来访问属性。这些方法仅存在于运行时状态,因此,如果在编写代码时调用这些方法,将导致不能找到方法的编译错误。

当然也可自行定义getter和setter方法。如果自行定义了这两个方法,play会自动使用这两个手工编写的方法。

为了保护Product类的price属性值,可以使用以下方法:

public class Product {

 

    public Stringname;

    public Integerprice;

 

    public voidsetPrice(Integer price) {

        if (price< 0) {

            thrownew IllegalArgumentException("Price can’t be negative!");

        }

        this.price= price;

    }

}

然后试着给price赋值一个负数时,就会抛出参数异常:

product.price = -10: // Oops! IllegalArgumentException

Play总是会使用已经手工定义好的getter或setter,如下:

@Entity

public class Data extends Model {

 

   @Required

   public Stringvalue;

   public IntegeranotherValue;

 

   public IntegergetAnotherValue() {

      if(anotherValue == null) {

           return0;

       }

       returnanotherValue;

   }

 

   public voidsetAnotherValue(Integer value) {

       if(value ==null) {

          this.anotherValue = null;

       } else {

          this.anotherValue = value * 2;

       }

   }

 

   public StringtoString() {

       return value + " - " +anotherValue;

   }

 

}

在另外一个类里对上面的类进行断言:

Data data = new Data();

data.anotherValue = null;

assert data.anotherValue == 0;

data.anotherValue = 4

assert data.anotherValue == 8;

正常工作,这是因为增加了getter和setter的类遵循JavaBean约定。

设置数据库来持久化模型对象

很多时候都需要把模型对象数据永久保存,最自然的方式就是把数据存入数据库。

在开发期间,可以直接使用内建的数据库来临时保存数据到内存或一个子目录里。

play发布包里包含了H2和Mysql的JDBC驱动($PLAY_HOME/framework/lib/)。如果打算使用PostgreSQL 或Oracle数据库,那么就需要把对应的jdbc驱动放入库目录里,或放入应用程序的lib目录。

连接到任何JDBC规范的数据,只需要设置jdbc的db.url,db.driver,db.userdb.pass属性:

 

db.url=jdbc:mysql://localhost/test

db.driver=com.mysql.jdbc.Driver

db.user=root

db.pass=

使用jpa.dialect属性可以为jpa定义方言。

在代码里,就可以从play.db.DB获取一个java.sql.Connection。

Connection conn = DB.getConnection();

conn.createStatement().execute("select * fromproducts");

用hibernate持久化对象模型

使用hibernate来自动持久化java对象到数据库里。

在任何java对象增加 @javax.persistence.Entity注释就可以定义一个jpa实体。play会自动启动一个jpa实体管理器。

@Entity

public class Product {

 

    public Stringname;

    public Integerprice;

}

注意!
一个经常发生的错误是使用hibernate的@Entity注释来代替JPA注释。请记住,play是通过hibernate来使用的jpa的api。也就是说要引入:javax.persistence.Entity包,而不是hibernate的包。

然后就可以从play.db.jpa.JPA获取一个EntityManager:

EntityManager em = JPA.em();

em.persist(product);

em.createQuery("from Product where price >50").getResultList();

 

play提供了一个非常漂亮的支持类来处理jpa,只需要实体类继承play.db.jpa.Model即可。

@Entity

public class Product extends Model {

 

    public Stringname;

    public Integerprice;

}

之后使用Product实例的简单方法就可以操作Product对象:

Product.find("price > ?", 50).fetch();

Product product = Product.findById(2L);

product.save();

product.delete();

保持模型stateless

play被设计成“什么都不共享”的机制。这个机制用于保持应用是完全无状态的。这样就允许程序可以同时运行于多个服务器节点。

Play 框架的设计架构就是无状态的。它没有提供服务器端的机制用来维护跨多个请求的数据。如果确实需要保存这样的数据的话,可以考虑下面几种方案:

  1. 如果数据很少很简单,可以存储在flash或session使用域里。但这些域只能存储小于4kb的数据,并且只能是字符串类型的数据。
  2. 使用数据库,比如需要创建一个横跨多个请求的wizard对象:
    • 在第一次请求时初始化并持久化对象到数据库中。
    • 把新创建的对象的ID存储到flash或session域中。
    • 在接下来的请求中,使用对象id找回数据、更新数据、再次存储等。
  3. 暂时存储到Cache中,比如需要创建一个横跨多个请求的wizard对象:
    • 在第一次请求时初始化并持久化对象到Cache中。
    • 把新创建的对象的key存储到flash或session域中。
    • 在接下来的请求中,使用正确的key找回数据、更新数据、再次存储等。
    • 在结束最后一次请求后,把对象永久存储到数据库中。

Cache不是一个可靠的存储位置,在系统未出现故障时应该可以正确操作数据。但Cache是比Java Servlet session更好的选择。

0 0
原创粉丝点击