程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> ActiveJPA:針對JPA的活動記錄模式

ActiveJPA:針對JPA的活動記錄模式

編輯:關於JAVA

ActiveRecord是Ruby on Rails的ORM層,大體上類似於Java中的Hibernate。ActiveRecord基於約定優於配置的原則,所以它使用起來比Hibernate更容易。在簡化基本的數據操作方面,如創建、讀取、更新和刪除,它確實是非常棒的。

借助於ActiveRecord,你的模型類也會作為數據訪問對象(Data Access Object,DAO)來執行CRUD操作。在初步探究之後,我對ActiveRecord產生了濃厚的興趣,因此開始尋找一種解決方案來簡化基於Java持久化API(Java Persistence API,JPA)的ORM框架的使用。

大多數JPA應用都會有某種類型的數據訪問層(Data Access Layer,DAL)來與數據庫進行交互。通常DAL會包含數據訪問對象或符合Repository設計模式的類。

DAO的實現與實體對象通常是一對一的關系,而Repository則是針對每個聚合根(aggregate root)實現一個。不管是哪種場景,應用最後都會創建多個類與數據庫進行交互。盡管適當的抽象能夠有效限制所創建類的數量,但是它終究還是會在應用中引入一個額外的層,這都是需要維護和測試的。

ActiveJPA基於JPA,提供了Martin Fowler所提出的活動記錄模式(Active Record pattern)的Java實現。借助於ActiveJPA,模型本身會作為DAO並與數據庫交互,這樣就不需要額外的代碼作為數據訪問層了。

ActiveJPA使用到了JPA規范,因此所有JPA的ORM實現(Hibernate、EclipseLink、OpenJPA等)都可以與ActiveJPA協同使用。

將已有的JPA模型轉換為ActiveJPA

要將已有的模型轉換為ActiveJPA,只需讓你的模型實現擴展org.activejpa.entity. Model即可:

@java.persistence.Entity   
public class YourModel extends org.activejpa.entity.Model {   
}

執行CRUD操作

你的模型將會從ActiveJPA的模型類中繼承得到很多的CRUD功能。

//根據id獲得訂單    
Order order = Order.findById(12345L);

// 根據customer獲得其已發貨的訂單   
List orders = Order.where("customerEmail", "[email protected]",
 "status", "shipped");

// 得到匹配過濾條件的第一條訂單記錄 
Long count = Order.first("customerEmail", "[email protected]", 
"status", "shipped");

// 得到匹配過濾條件的唯一一條訂單記錄
Long count = Order.one("customerEmail", "[email protected]", 
"status", "shipped");

// 得到所有的記錄   
List orders = Order.all();

// 檢查指定標識符的訂單是否存在   
boolean exists = Order.exists(1234L);

// 保存訂單   
order.persist();

// 刪除訂單   
order.delete();

// 刷新訂單
order.refresh();

// 與持久化上下文中已有的訂單進行合並
order.merge();

過濾與分頁

對記錄進行過濾時,你並不需要創建JPQL或criteria查詢。ActiveJPA提供了一個復雜的過濾器,用於在不同的操作間進行連接(conjunction):

// 獲取匹配指定Email地址且賬單額大於1000的所有訂單,並且要進行分頁 
Filter filter = new Filter();    
filter.setPageNo(1);    
filter.setPerPage(25);    
filter.addCondition(new Condition("customerEmail", Operator.eq,
 "[email protected]");

filter.addCondition(new Condition("billingAmount", Operator.gt, 
1000.00);   
List orders = Order.where(filter);

// 對滿足過濾條件的訂單進行計數   
Long count = Order.count(filter);

// 刪除匹配這個過濾器的訂單   
Long count = Order.deleteAll(filter);

嵌套查詢

ActiveJPA允許嵌套過濾參數。這樣的話就能更容易地在運行時創建動態查詢。例如,你獲取的訂單中,至少要有一個訂單項是基於“book”類別的產品創建的。

// 得到至少包含一個book項的所有訂單 
Filter filter = new Filter();    
filter.setPageNo(1);    
filter.setPerPage(25);    
filter.addCondition(new Condition("orderItems.product.category",
 Operator.eq, "books");    
List orders = Order.where(filter);

使用集合

上面討論的所有CRUD操作同時可以在集合級別執行。將查詢范圍設置到集合類的用法如下所示:

// 在訂單中,根據id查找訂單項    
order.collections("orderItems").findById(123L);

// 得到第一個已發貨的訂單項
order.collections(“orderItems”).first(“status”, “shipped”);

// 得到所有取消的訂單項   
order.collections(“orderItems”).where(“status”, “cancelled”);

// 得到集合中的所有項   
order.collections(“orderItems”).all();

// 往集合中添加一項   
order.collections(“orderItems”).add(orderItem);

// 移除集合中的一項   
order.collections(“orderItems”).remove(orderItem);

過濾和分頁也可以使用到集合之中

// 對一個訂單中的訂單項基於過濾器進行查詢並進行分頁
Filter filter = new Filter();   
filter.setPageNo(1);    
filter.setPerPage(25);

filter.addCondition(new Condition(“status”, “shipped”);   
order.collections("orderItems").where(filter);

// 在訂單中,得到匹配過濾器的訂單項的數量

order.collections(“orderItems”).count(filter);

動態更新

ActiveJPA支持對模型的動態更新。在有些場景下這是很有用的,例如用戶通過浏覽器更新一個表單。你可以傳遞一個包含屬性的map來進行更新,而不是調用每個屬性的setter方法:

// 更新屬性    

Map attributes = new HashMap();    
attributes.put("billingAmount", 1000.0);  
order.updateAttributes(attributes);

你也可以更新非原始/非包裝類型的域,只需給這些對象傳遞map即可。以下的樣例展現了更新一個訂單的收獲地址和賬單金額。

// 更新一個訂單的收獲地址和賬單金額

Map attributes = new HashMap();

Map address = new HashMap();

address.put(“city”, “Bangalore”);

address.put(“state”, “Karnataka”);

attributes.put(“shippingAddress”, address);

attributes.put("billingAmount", 1000.0);

order.updateAttributes(attributes);

注意:目前尚不支持更新list/set/array域,未來的版本將會提供支持。

事務處理

默認情況下,如果沒有事務,ActiveJPA將會為所有的更新操作啟動一個事務,不過你也可以將整個工作單元包裝到一個事務之中:

JPAContext context = JPA.instance.getDefaultConfig().getContext();    
context.beginTxn();    
boolean failed = true;    
try {    
 // 你的工作單元置於此處    
failed = false;    
} finally {    
 // 提交或回滾事務    
context.closeTxn(failed);    
}

如果已經存在了外部的事務,那麼ActiveJPA將會使用這個事務,但是不會進行提交或回滾。應該由應用來負責關閉該事務。

測試你的模型

ActiveJPA為TestNG提供了一個基本的測試類,它會將ActiveJPA以掛鉤(hook)的方式添加到測試運行時之中。只需讓你的測試類擴展自org.activejpa.entity.testng.BaseModelTest類即可。以下為一個樣例代碼:

public class OrderTest extends BaseModelTest {
     @Test
     public void testCreateOrder() {
       Order order = new Order();
       order.setCustomerEmail("[email protected]");
       ...
       ...
       order.persist();
       Assert.assertEquals(Order.where("customerEmail",
 "[email protected]").get(0), order);
    }
  }
}

使用起步

搭建Maven

ActiveJPA可以以Maven artifact的方式來獲取,能夠非常容易地集成到你的應用之中。只需在你的pom.xml文件中添加如下的maven依賴:

<dependencies>    
<dependency>    
<groupId>org.activejpa</groupId>    
<artifactId>activejpa-core</artifactId>    
<version>0.1.5</version>    
</dependency>    
</dependencies>

<repositories>   
<repository>    
<id>activejpa-repo</id>    
<url>https://raw.github.com/ActiveJpa/activejpa/mvn-repo/releases</url>    
<snapshots>    
<enabled>true</enabled>    
<updatePolicy>always</updatePolicy>    
</snapshots>    
</repository>    
</repositories>

嵌入到你的應用之中

ActiveJPA需要在實體類加載前就嵌入到你的應用之中。如果你使用Tomcat的話,那麼ServletContextListener就是做這件事的一個很好的地方。你可以將以下的代碼編寫到上下文監聽器的contextInitialized()方法之中。

// 動態加載Java代理   
ActiveJpaAgentLoader.instance().loadAgent();

// 添加定義在persistence.xml中的持久化單元,以“order”名進行標識。
persistence.xml應該位於類路徑下   
JPA.addPersistenceUnit("order");

// 如果你已經創建了實體管理工廠的話,可以將其關聯到ActiveJpa上  
// JPA.addPersistenceUnit("order", entityManagerFactory);

與Spring框架集成

將ActiveJPA與Spring這樣的框架進行集成是很容易的。大多數的應用都會使用Spring的注解來配置JPA和管理事務。ActiveJPA能夠以兩種方式來進行配置——你可以讓它來創建實體管理工廠也可以傳入一個已存在的對象。在Spring配置JPA的情況下,我們可以使用Spring所創建的實體管理工廠。這樣就能保證ActiveJPA使用Spring所創建的相同的連接和事務,從而提供無縫的集成。

以下的代碼展現了如何將ActiveJPA集成到Spring應用之中,這個應用是部署在servlet容器裡面的。它使用了一個自定義的上下文加載監聽器,從而將ActiveJPA嵌入到應用之中。需要注意的是,這很類似與上面的servlet樣例,區別在於這裡使用了Spring框架的ContextLoaderListener:

public class CustomContextListener extends ContextLoaderListener {
  @Override
  public void contextInitialized(ServletContextEvent event) {
          try {
                    // 在這裡動態加載Java代理
                    ActiveJpaAgentLoader.instance().loadAgent();
          } catch (Exception e) {
                    throw new RuntimeException(e);
          }
          super.contextInitialized(event);
          JPA.instance.addPersistenceUnit("default", 
getCurrentWebApplicationContext().getBean(EntityManagerFactory.
class), true);
   }
}

樣例應用

在GitHub的ActiveJPA工程頁面上有一個示例應用,包含了很多更具體的樣例,展現了Spring-ActiveJPA的集成。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved