06.域對象模型

模型是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.user db.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對象:
  3. 暫時存儲到Cache中,比如需要創建一個橫跨多個請求的wizard對象:
    • 在第一次請求時初始化並持久化對象到Cache中。
    • 把新創建的對象的key存儲到flash或session域中。
    • 在接下來的請求中,使用正確的key找回數據、更新數據、再次存儲等。
    • 在結束最後一次請求後,把對象永久存儲到數據庫中。

Cache不是一個可靠的存儲位置,在系統未出現故障時應該可以正確操作數據。但Cache是比Java Servlet session更好的選擇。

發佈了8 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章