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 活动记录实体。但是,您可以通过添加更多服务或存储库层轻松更改现有应用程序(详情如下)。如果您添加新层,Roo 将自动更改其消费者层或服务层中的 ITD。例如,Roo 将自动更改您的应用程序,以便为给定的域类型注入和调用现有 MVC 控制器、GWT 定位器、集成测试或按需数据中的新服务层。

持久化层

随着 Roo 1.2.0.M1 的发布,Roo 核心现在有三个选项可用于支持数据持久化:JPA 实体(活动记录样式)、JPA 存储库和 MongoDB 存储库。Spring Data Neo4J 的支持目前正在进行中,并应很快作为Roo 附加组件提供。

JPA 实体(活动记录样式)

自 Spring Roo 首次发布以来,活动记录样式的 JPA 实体一直是默认设置,并将继续如此。为了将您的项目配置为 JPA 持久性,您可以运行 jpa setup 命令

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

这会将您的项目配置为使用 Hibernate 对象关系映射器以及内存数据库 (HSQLDB)。Roo 支持的活动记录样式 JPA 实体使用@RooEntity进行注释,该注释负责提供持久性标识符字段及其访问器和更改器。此外,此注释还创建了支持数据访问的典型 CRUD 方法。

roo> entity --class ~.domain.Pizza

此命令将创建一个 Pizza 域类型以及用于持久化、更新、读取和删除实体的活动记录样式方法。以下示例还包含一些可以直接添加到 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“活动记录”基于持久化方法的开发人员可以通过为给定的 JPA 域类型创建Spring Data JPA 支持的存储库来实现此目的。通过jpa setup命令将项目配置为 JPA 持久性后,此功能会通过使用 Roo 的@RooJpaEntity注释对域类型进行注释来自动提供。

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

通过定义--activeRecord false,您可以选择退出否则为默认的“活动记录”样式。以下示例还包含一些可以通过 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 将 extends 语句添加到 PizzaRepository 接口,从而产生与以下接口定义等效的结果

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

查看全部