06.域对象模型

    xiaoxiao2021-04-18  69

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

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

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

    转载请注明原文地址: https://ju.6miu.com/read-675264.html

    最新回复(0)