Spring Roo 1.2 中新的應用分層和持久化選擇

工程 | Stefan Schmidt | 2011年9月14日 | ...

Java 企業應用程式可以有多種形態和形式。根據其需求,開發人員需要決定應用程式需要哪些特定的架構層。到目前為止,Spring Roo 採取了實用方法,以減少服務門面、倉庫或 DAO 層引入的往往不必要的複雜性。新發布的 Spring Roo 1.2.0.M1(檢視公告)包含了頻繁請求的對可根據應用程式需求量身定製的架構層的支援。本文概述了 Roo 的新服務和倉庫層功能。

Spring Roo Application Layering Support

雖然有許多新的分層和持久化選擇,但Roo預設將繼續支援JPA Active Record實體。但是,您可以透過新增額外的服務或倉庫層(詳情如下)輕鬆更改現有應用程式。如果您新增新的層,Roo將自動更改其在消費層或服務層中的ITD。例如,Roo將自動更改您的應用程式,以在現有MVC控制器、GWT定位器、整合測試或給定域型別的資料點播中注入並呼叫新的服務層。

持久化層

透過Roo 1.2.0.M1版本,Roo核心現在提供三種選項來支援資料持久化:JPA實體(Active Record風格)、JPA倉庫和MongoDB倉庫。對Spring Data Neo4J的支援目前正在開發中,很快將作為Roo外掛提供。

JPA實體(Active Record風格)

Active Record風格的JPA實體自Spring Roo的第一個版本以來一直是預設設定,並將保持不變。要為JPA持久化配置您的專案,您可以執行jpa setup命令

roo> jpa setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT

這會將您的專案配置為使用Hibernate物件關係對映器以及記憶體資料庫(HSQLDB)。Roo支援的Active Record風格的JPA實體使用@RooEntity進行註解,該註解負責提供持久化識別符號欄位及其訪問器和修改器。此外,此註解還會建立典型的CRUD方法以支援資料訪問。

roo> entity --class ~.domain.Pizza

此命令將建立一個Pizza域型別,以及用於持久化、更新、讀取和刪除實體的Active Record風格方法。以下示例還包含一些欄位,可以直接新增到Java原始檔,或透過Roo shell中的field命令新增。

@RooJavaBean
@RooToString
@RooEntity
public class Pizza {

    @NotNull
    @Size(min = 2)
    private String name;

    private BigDecimal price;

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Topping> toppings = new HashSet<Topping>();

    @ManyToOne
    private Base base;
}

JPA倉庫

需要倉庫/DAO層而不是預設Roo JPA“Active Record”持久化方法的開發人員可以透過為給定JPA域型別建立Spring Data JPA支援的倉庫來實現。透過jpa setup命令為JPA持久化配置專案後,此功能透過使用Roo的@RooJpaEntity註解域型別自動提供。

roo> entity --class ~.domain.Pizza --activeRecord false

透過定義--activeRecord false,您可以選擇退出預設的“Active Record”風格。以下示例還包含一些欄位,可以透過Roo shell中的field命令新增。

@RooJavaBean
@RooToString
@RooJpaEntity
public class Pizza {

    @NotNull
    @Size(min = 2)
    private String name;

    private BigDecimal price;

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Topping> toppings = new HashSet<Topping>();

    @ManyToOne
    private Base base;
}

有了域型別之後,您現在可以使用repository jpa命令為此型別建立新的倉庫

roo> repository jpa --interface ~.repository.PizzaRepository --entity ~.domain.Pizza

這將建立一個利用Spring Data JPA的簡單介面定義

@RooRepositoryJpa(domainType = Pizza.class)
public interface PizzaRepository {
}

當然,您可以在任何介面上手動新增@RooRepositoryJpa註解,而無需在Roo shell中發出repository jpa命令。

新增@RooRepositoryJpa註解將觸發一個相當簡單的AspectJ ITD的建立,該ITD會向PizzaRepository介面新增一個extends語句,從而產生與此介面定義等效的結果

public interface PizzaRepository extends JpaRepository<Pizza, Long> {}

JpaRepository介面是Spring Data JPA API的一部分,並提供所有開箱即用的CRUD功能。

MongoDB持久化

作為JPA持久化的替代方案,Spring Roo 1.2現在透過利用Spring Data MongoDB專案提供MongoDB支援。MongoDB受Cloud Foundry支援,這是一個免費開發和託管Spring Roo應用程式的好地方。除了MongoDB,您還可以使用MySQL和PostgreSQL與Cloud Foundry。要為MongoDB持久化配置專案,您可以使用mongo setup命令

roo> mongo setup

這將配置您的Spring應用程式上下文以使用執行在localhost和預設埠上的MongoDB安裝。可選的命令屬性允許您定義主機、埠、資料庫名稱、使用者名稱和密碼。如果您在Cloud Foundry上使用MongoDB,只需在“mongo setup”後新增--cloudFoundry,Roo就會自動為您配置所有內容

一旦應用程式配置了MongoDB支援,entity mongorepository mongo命令就可用了

roo> entity mongo --class ~.domain.Pizza

此命令將建立一個用@RooMongoEntity註解的Pizza域型別。此註解負責觸發ITD的建立,該ITD提供一個Spring Data @Id註解欄位及其訪問器和修改器。以下示例還包含一些欄位,可以透過Roo shell中的field命令新增。

@RooJavaBean
@RooToString
@RooMongoEntity
public class Pizza {

    @NotNull
    @Size(min = 2)
    private String name;

    private BigDecimal price;

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Topping> toppings = new HashSet<Topping>();

    @ManyToOne
    private Base base;
}

有了域型別之後,您現在可以使用repository mongo命令為此型別建立新的倉庫(或透過將@RooRepositoryMongo註解應用於任意介面)

roo> repository mongo --interface ~.repository.PizzaRepository --entity ~.domain.Pizza

這將建立一個利用Spring Data MongoDB的簡單介面定義

@RooRepositoryMongo(domainType = Pizza.class)
public interface PizzaRepository {

    List<Pizza> findAll();
}

與上面看到的Spring Data JPA驅動的倉庫類似,此介面透過一個ITD進行增強,該ITD引入了Spring Data API提供的PagingAndSortingRepository,並負責提供所有必要的CRUD功能。此外,此介面定義了一個“自定義”查詢器,PagingAndSortingRepository實現不提供此查詢器:List findAll();。此方法是Spring Roo的UI腳手架所必需的,並由Spring Data MongoDB提供的查詢構建器機制自動實現。

與使用JPA持久化的應用程式類似(有關使用JPA與Postgres的詳細資訊,請參閱這篇部落格文章),MongoDB應用程式可以輕鬆部署到VMware Cloud Foundry。以下指令碼提供了一個Pizza Shop示例應用程式(參見/sample/pizzashop.roo)的示例,該應用程式已調整為與MongoDB支援的倉庫層一起使用

// Create a new project.
project com.springsource.pizzashop

// Create configuration for MongoDB peristence 
mongo setup --cloudFoundry true

// Define domain model.
entity mongo --class ~.domain.Base --testAutomatically
field string --fieldName name --sizeMin 2 --notNull --class ~.domain.Base
entity mongo --class ~.domain.Topping --testAutomatically
field string --fieldName name --sizeMin 2 --notNull --class ~.domain.Topping
entity mongo --class ~.domain.Pizza --testAutomatically
field string --fieldName name --notNull --sizeMin 2 --class ~.domain.Pizza
field number --fieldName price --type java.lang.Float
field set --fieldName toppings --type ~.domain.Topping
field reference --fieldName base --type ~.domain.Base
entity mongo --class ~.domain.PizzaOrder --testAutomatically
field string --fieldName name --notNull --sizeMin 2 --class ~.domain.PizzaOrder
field string --fieldName address --sizeMax 30
field number --fieldName total --type java.lang.Float
field date --fieldName deliveryDate --type java.util.Date
field set --fieldName pizzas --type ~.domain.Pizza

// Add layer support.
repository mongo --interface ~.repository.ToppingRepository --entity ~.domain.Topping
repository mongo --interface ~.repository.BaseRepository --entity ~.domain.Base
repository mongo --interface ~.repository.PizzaRepository --entity ~.domain.Pizza
repository mongo --interface ~.repository.PizzaOrderRepository --entity ~.domain.PizzaOrder
service --interface ~.service.ToppingService --entity ~.domain.Topping
service --interface ~.service.BaseService --entity ~.domain.Base
service --interface ~.service.PizzaService --entity ~.domain.Pizza
service --interface ~.service.PizzaOrderService --entity ~.domain.PizzaOrder

// Create a Web UI.
web mvc setup
web mvc all --package ~.web

// Package the application into a war file.
perform package

// Deploy and start the application in Cloud Foundry
cloud foundry login 
cloud foundry deploy --appName roo-pizzashop --path /target/pizzashop-0.1.0.BUILD-SNAPSHOT.war --memory 512
cloud foundry create service --serviceName pizzashop-mongo --serviceType mongodb
cloud foundry bind service --serviceName pizzashop-mongo --appName roo-pizzashop
cloud foundry start app --appName roo-pizzashop

“即時”示例應用程式目前部署在Cloud Foundry上:http://roo-pizzashop.cloudfoundry.com/

服務層

開發人員還可以選擇在應用程式中建立服務層。預設情況下,Roo將為一個或多個域實體建立服務介面(和實現)。如果存在提供持久化的層(例如Roo的預設實體層或倉庫層)用於給定域實體,則服務層將透過其介面和實現公開此持久化層提供的CRUD功能。

根據Roo的約定,所有功能都將透過AspectJ ITD引入,從而為開發人員提供了一個乾淨的畫布,用於實現不自然地適合域實體的自定義業務邏輯。服務層的其他常見用例是安全性或遠端整合,例如Web服務。有關更詳細的討論,請參閱Spring Roo參考指南中的架構章節

服務層整合到Roo專案中與倉庫層類似,透過直接使用@RooService註解或Roo shell中的service命令

roo> service --interface ~.service.PizzaService --entity ~.domain.Pizza

此命令將在定義的包中建立PizzaService介面,並在同一包中建立PizzaServiceImplPizzaServiceImpl的名稱和包可以透過可選的--class屬性自定義)。

@RooService(domainTypes = { Pizza.class })
public interface PizzaService {
}

根據Roo約定,預設的CRUD方法定義可以在AspectJ ITD中找到

void savePizza(Pizza pizza);
Pizza findPizza(Long id);    
List<Pizza> findAllPizzas();    
List<Pizza> findPizzaEntries(int firstResult, int maxResults);   
long countAllPizzas();    
Pizza updatePizza(pizza pizza);
void deletePizza(Pizza pizza);

同樣,PizzaServiceImpl也相當簡單

public class PizzaServiceImpl implements PizzaService {}

透過AspectJ ITD,PizzaServiceImpl型別預設用@Service@Transactional註解。此外,ITD將向目標型別引入以下方法和欄位

@Autowired PizzaRepository pizzaRepository;
    
public void savePizza(Pizza pizza) {
    pizzaRepository.save(pizza);
}

public Pizza findPizza(Long id) {
    return pizzaRepository.findOne(id);
}

public List<Pizza> findAllPizzas() {
    return pizzaRepository.findAll();
}

public List<Pizza> findPizzaEntries(int firstResult, int maxResults) {
    return pizzaRepository.findAll(new PageRequest(firstResult / maxResults, maxResults)).getContent();
}

public long countAllPizzas() {
    return pizzaRepository.count();
}

public Pizza updatePizza(Pizza pizza) {
    return pizzaRepository.save(pizza);
}
    
public void deletePizza(Pizza pizza) {
    pizzaRepository.delete(pizza);
}

如您所見,Roo將檢測是否存在給定域型別的持久化提供程式層,並自動注入此元件以將所有服務層呼叫委託給此層。如果不存在持久化(或其他“較低級別”)層,服務層ITD將簡單地提供方法存根。

總結

使用Spring Roo 1.2,向新的或現有的Spring Roo管理應用程式新增架構層或持久化選項變得幾乎微不足道。無需考慮為新的持久化提供程式配置應用程式,或將對新層的引用注入到Spring MVC控制器、GWT UI或整合測試中——Roo將為您完成所有這些工作!

隨著Spring Roo 1.2中可用的分層支援,我們預計未來會有更多的持久化提供程式。對Spring Data Neo4J的倉庫分層整合目前正在開發中,很快將作為Roo外掛提供。

如果您想輕鬆試用這些新功能,為什麼不構建您自己的MongoDB驅動的Pizza Shop應用程式版本並將其部署Cloud Foundry呢?得益於這些新的Roo 1.2.0.M1功能,這隻需幾分鐘。

鑑於Spring Roo 1.2.0.M1是一個里程碑版本,您應該繼續將Roo 1.1.5用於生產專案。但是,我們相信Roo 1.2 M1適合探索新功能或快速專案。

Roo團隊始終歡迎社群的反饋。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有