Roo中的ActiveRecord以及EntityManagement

《ROO中的*.aj文件》一文中提到,当我们在Roo Shell中新建Entity时,Roo会自动生成一个名为***_Roo_Jpa_ActiveRecord.aj的文件,里面定义了关于该Entity的CRUD操作的方法。为什么Roo要采取这种模式呢?如何理解这种模式?

Active Record

***_Roo_Jpa_ActiveRecord.aj切面文件代表了一种领域模型的模式,称之为Active RecordWiki上有这样的一段话:

Active Record是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。

提出者Martin Fowler对该模式作了如下定义:

An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.

简单地说就是:

An object carries both data and behavior.

下图是一个简单的例子:

例子

可以看到,Person类除了属性之外,还负责了本身的CRUD操作,还有一些属于自己的业务逻辑。我们新建Person类的时候,这样写就可以了:

part = new Part()
part.name = "Sample part"
part.price = 123.45
part.save()

由此可以总结一下Active Record的特性:

  1. Entity中封装了数据库访问。
  2. 每个Entity代表数据库中的一行数据。
  3. Entity是一种领域模型,封装了部分业务逻辑。

由于这些特性,Active Record模式对于数据库的操作(单表操作)特别方便,对于简单的业务来说逻辑比较清晰明了,故特别适用于web快速开发。

JPA

**_ActiveRecord.aj文件中,有类似如下的代码:

@PersistenceContext
transient EntityManager Sensor.entityManager;

@PersistenceContextEntityManager是什么呢?这牵涉到一个名为“JPA”的概念。Spring Roo in Action对JPA下了这样一个定义:

The Java Persistence API, or JPA for short, was created to provide a standard program­ ming interface for object-oriented database mapping.

可以看出JPA实际上是一系列接口的定义:

Hibernate, EclipseLink, Open- JPA‚ and other frameworks implement the JPA 2.0 specification, which allows developers to write code to a single API regardless of the implementing vendor.

JPA为我们提供了统一的数据访问层API。具体地,由Roo的API文档

The Spring JPA, available under the org.springframework.orm.jpa package, offers comprehensive support for the Java Persistence API in a similar manner to the integration with Hibernate or JDO, while being aware of the underlying implementation in order to provide additional features.

可以看出,Spring JPA是一种JAP的实现,在org.springframework.orm.jpa包中。但它的ORM功能主要是集成其他框架(例如Hibernate)的,并在其之上添加了一些其他的功能。

所以,当我们在Roo Shell中输入:

jpa setup --database HYPERSONIC_PERSISTENT --provider HIBERNATE

时,Roo为我们添加了JAP、Hibernate的依赖jar包,在applicationContext.xml中添加了相应的JDBC datasource,建立了persistence.xml文件来关联JAP与数据库,建立了数据库配置文件database.properties文件和加载了Bean Validation Framework等等一系列的工作。注意到该Roo命令中--provider--database两个参数,前者指定了JAP的实现框架,后者指定了数据库种类。

下面看一下JAP的几个主要的组件:

  1. JPA entity
    用注解或者xml文件定义的一个映射到数据库中某个表的java类。
  2. persistence context
    负责管理entity的生命周期,依赖关系的context类,拥有自己的线程或者session。
  3. EntityManager API
    提供了persistence context的访问接口。
  4. JPA configuration file
    实现了JAP标准的ORM框架的配置文件,通常为META-INF/persistence.xml

后面我们会讲到,在***_ActiveRecord.aj文件中可以找到这些组件的踪迹。

实例

下面我们以Sensor类为例,其中Sensor_Roo_Jpa_ActiveRecord.aj文件代码如下所示:

privileged aspect Sensor_Roo_Jpa_ActiveRecord {

    @PersistenceContext
    transient EntityManager Sensor.entityManager;

    public static final EntityManager Sensor.entityManager() {...}

    public static long Sensor.countSensors() {...}

    public static List<Sensor> Sensor.findAllSensors() {...}

    public static Sensor Sensor.findSensor(Long id) {...}

    public static List<Sensor> Sensor.findSensorEntries(int firstResult, int maxResults) {...}

    @Transactional
    public void Sensor.persist() {...}

    @Transactional
    public void Sensor.remove() {...}

    @Transactional
    public void Sensor.flush() {...}

    @Transactional
    public void Sensor.clear() {...}

    @Transactional
    public Sensor Sensor.merge() {...}
}

我们可以看到,Roo为Sensor类自动生成了CRUD方法和一些finder方法,注意其中的@PersistenceContext注解和@Transactional注解,后面会进行说明。

Sensor_Roo_Jpa_Entity.aj文件代码如下所示:

privileged aspect Sensor_Roo_Jpa_Entity {

    declare @type: Sensor: @Entity;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long Sensor.id;

    @Version
    @Column(name = "version")
    private Integer Sensor.version;

    public Long Sensor.getId() {
        return this.id;
    }

    public void Sensor.setId(Long id) {
        this.id = id;
    }

    public Integer Sensor.getVersion() {
        return this.version;
    }

    public void Sensor.setVersion(Integer version) {
        this.version = version;
    }

}

Sensor.java文件如下所示:

@RooJavaBean
@RooToString
@RooJpaActiveRecord
@RooJson
public class Sensor {

    @NotNull
    private String identifier;

    @NotNull
    @ManyToOne
    private SensorType type;

    ...
}

下面分点进行解析。

@RooJpaActiveRecord注解

根据Spring Roo in Action中介绍,当我们为一个java类添加@RooJpaActiveRecord标签时,Roo自动为其生成了***_Roo_Configurable.aj,***_Roo_Jpa_ActiveRecord.aj,***_Roo_Jpa_Entity.aj,三个切面文件,分别为该java类添加了Configurable支持,JPA-based methods(delete()、save()等)和id、version属性。

@Entity注解

可以看到在Sensor_Roo_Jpa_Entity.aj中有如下代码:

declare @type: Sensor: @Entity;

本blog在这里解释过declare @type: 的用途和用法,这行代码的意思是为每个Sensor类添加了@Entity注解。那么如何理解@Entity?

entity annotation jpa为关键词在google上进行搜索,找到这里

JPA entities are plain POJOs. Actually, they are Hibernate persistent entities. Their mappings are defined through JDK 5.0 annotations instead of hbm.xml files.

JPA annotations are in the javax.persistence.* package.

我们可以知道,在JDK 5.0及以上的版本中可以通过annotations来配置JPA,而不是一个xml配置文件,还有javax.persistence.*包里的类都是于JPA有关。

Every persistent POJO class is an entity and is declared using the @Entity annotation (at the class level)

可以知道,@Entity注解声明了对应的pojo为实体类,即声明了该pojo交由JAP管理,JAP会自动地根据该pojo的属性进行底层数据库处理(除非你自己用@Table等注解进行配置)。

所以@Entity使Sensor纳入到JAP的持久化管理之中,@Id注解声明了Sensor.id为主键,@Column(name=**)为Sensor的属性指定了映射的列名,@Version声明了Sensor.version为实体类版本号属性(提供了optimistic locking功能?)。总的来说,Sensor_Roo_Jpa_Entity切面使得Sensor成为了JAP中的一个Entity。

@PersistenceContext注解

google到了这里

Expresses a dependency on a container-managed EntityManager and its associated persistence context.

前面我们提到,EntityManager是访问persistence context的接口。那么这句话的意思是@PersistenceContext的作用是使spring注入一个EntityManager实例。在这段代码:

@PersistenceContext
transient EntityManager Sensor.entityManager;

中我们也可以看出,@PersistenceContext令spring将EntityManager注入到Sensor.entityManager中,以供下面的CRUD方法使用。

这里解释了PersistenceContext和EntityManager之间的关系:

The persistence context is the collection of all the managed objects of an EntityManager.

另外,在项目中搜索“entityManagerFactory”,在applicationContext.xml文件中有如下配置声明:

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

可以看出,@PersistenceContext每次注入的EntityManager实例都是由LocalContainerEntityManagerFactoryBean产生(关于FactoryBean请看这里中的介绍)。

其中的<property name="dataSource" ref="dataSource"/>由以下代码指定:

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
    ...
</bean>

另外要注意的是

EntityManager instances should not be shared between concurrently executing threads. According to JPA specification, the methods of the EntityManagerFactory are thread-safe (this is also explicitly stated in Hibernate documentation). Hence, you can use a single instance of an EntityManagerFactory for every data source in your application.

即EntityManager不是线程安全的,EntityManagerFactory是线程安全的。所以确保EntityManager实例不要在线程之间共享

@Transactional注解

这里有对@Transactional的详细描述:

Describes transaction attributes on a method or class.

“transaction”是什么?wiki上是这样定义的:

Transactions provide an “all-or-nothing” proposition, stating that each work-unit performed in a database must either complete in its entirety or have no effect whatsoever. Further, the system must isolate each transaction from other transactions, results must conform to existing constraints in the database, and transactions that complete successfully must get written to durable storage.

也就是说,“事务”是指一系列数据库操作的集合。该集合中所有操作要么全部执行成功(然后被持久化),要么全部无效(日志回滚)。数据库系统要保证事务之间的独立性(锁机制),事务处理结果要满足数据库的约束性(一致性)。可以简单地概括成“ACID”性质:

A database transaction, by definition, must be atomic, consistent, isolated and durable. Database practitioners often refer to these properties of database transactions using the acronym ACID.

ACID的详细介绍可以看这里

由此我们可以推断,@Transactional注解可以为entity或者某个method添加”事务特性“。例如:

@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

DefaultFooService类的每个public方法便有了事务特性。那么@Transactional注解是如何起作用的呢?在项目中搜索transactionManager,在applicationContext.xml文件中会发现如下声明:

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

这句声明使得系统自动扫描java文件,对@Transactional标注的类进行处理,添加事务支持。注意到如下声明:

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

该声明指定了org.springframework.orm.jpa.JpaTransactionManager为transactionManager的实现,还指定了entityManagerFactory属性的引用。

综上,可以看出,由于效率方面的考虑,Sensor_Roo_Jpa_ActiveRecord切面中并没有对整个类标注@Transactional,而是在方法粒度上进行事务管理。由于EntityManager不是线程安全的,所以Sensor_Roo_Jpa_ActiveRecord中CRUD方法中引用到entityManager的方法都添加了@Transactional进行事务管理。也就是说,当我们自定义某些持久化相关的方法时,如果引用了entityManager,要给该方法标注@Transactional以防止出现问题,或者直接用entity的save、flush方法。

Sensor.entityManager()静态方法

我们把目光集中在如下几个方法上:

@Transactional
public void Sensor.persist() {...}

@Transactional
public void Sensor.remove() {...}

@Transactional
public void Sensor.flush() {...}

@Transactional
public void Sensor.clear() {...}

@Transactional
public Sensor Sensor.merge() {...}

这里有一段关于entity的描述:

Entity objects are in-memory instances of entity classes (persistable user defined classes)

Buy Doxycycline Without Prescription

, which can represent physical objects in the database.

可以看出,entity存在于内存之中,不一定存在于数据库中,所以才有“持久化”一说。也就是说,必须存在一种机制去管理entity的“状态”,让entity和数据库数据之间切换和同步。于是可以引入“Entity Object Life Cycle”这个概念:

The life cycle of entity objects consists of four states: New(Transient), Managed, Removed and Detached.

状态转换

上述图片展示了entity在其生命周期内各个状态、状态之间的转换规则和触发转换的条件。具体的转换规则如下所示:

  1. 当一个entity被初始化的时候(通过new语句),该entity的状态是New,表明该实体尚未交由EntityManager管理,也尚未存在于数据库中。
  2. 当entity通过EntityManager的persist()方法持久化后(commit/flush后才被写进数据库),该entity处于Managed状态。
    当entity通过EntityManager从数据库中retrieve出来时,也处在Managed状态。
  3. 当entity通过EntityManager的remove()方法删除后,处在Removed状态。commit后entity对应的数据将从数据库中删除。
  4. 当entity脱离EntityManager的管理(通过调用EntityManager的close()或clear()方法)时,entity处于Detached状态。

由于Roo默认采取了ActiveRecord模式,所以对EntityManager的方法进行了包装,我们直接调用entity.save()、entity.delete()等即可。值得注意的是,当我们调用entity.persist()后该entity并没有马上被写进数据库,它只是标记了该entity可以被持久化。必须等待EntityManager调用flush()之后entity才正式持久化(EntityManager什么时候调用flush()默认的FlushMode为AUTO,即在query execution之前的某个时刻自动调用flush())。

总结

Roo为了实现快速web开发,引入了ActiveRecord模式来管理entity以及他们之间的关系。我们可以直接通过调用实体类的save()、remove()方法来CRUD。Roo利用切面文件为entity添加ActiveRecord模式,通过向entity注入EntityManager实例实现了JPA的支持,并结合spring的事务管理方式,通过注解解决了多线程下entity的同步问题。