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 社区所有即将举行的活动。

查看所有