在spring和不寻常的体系结构中对实体管理器的静态访问



快速提问:

我有webapplication (wicket+spring+jpa),并且正在考虑相当不寻常的架构设计。请查看并给出您的评论。

考虑类包装器:

@Service
public class Wrapper {
    protected static EntityManager entityManager;
    @PersistenceContext
    private void injectEntityManager(EntityManager entityManager) {
        Wrapper.entityManager = entityManager;
    }

如你所见,我现在已经静态注入了EntityManager。

现在考虑简单实体DogEntity

@Entity
public class DogEntity {
   String name;
}

我们为这个实体创建包装器Dog

public class Dog extends Wrapper {
  private DogEntity entity;
  private Dog(DogEntity entity) {
     this.entity = entity;
  }
  public static Dog create(String name) {
    entity = new DogEntity();
    entity.name = name;
    entityManager.persist(entity); // for a moment forget that this code is not in transaction
    return new Dog(entity);
  }
}

现在在我的web应用程序(在我的控制器中)我可以这样做:

saveButton = new Button("save") {
public void onSubmit() {
   Dog dog = Dog.create(name);
   // other code 
}

从代码的角度来看,这个架构看起来很完美。我们有表示业务对象的包装器。它们都有持久状态,在应用程序中没有愚蠢的服务叫做DogSaver,它的方法是save(DogEntity),它只在实体管理器上调用persist。代码确实有很多可读性和其他优点,但我不想详细说明。

我真正关心的是这个静态EntityManager。我对Spring的内部没有足够的了解,无法知道这种方法是否正确和安全。有没有情况会让事情变得很糟糕?我知道EntityManare是无状态的(根据JPA规范),它总是从事务中获取持久性上下文,因此使其静态似乎不是一个坏主意。但是我担心我可能搞砸了什么。

任何想法吗?

你应该去看看Spring Roo。它们有类似的东西(没有dao或服务),但是EntityManager不是静态的。

他们在实体中使用@Configurable注释:

@Entiy
@Configurable
class MyEntity() {
  @PersistenceContext
  transient EntityManager Car.entityManager;
  ...
  public static MyEntity findMyEntityById(Long id) {
    if (id == null) return null;
    return entityManager().find(MyEntity.class, id);
  }
  public static EntityManager entityManager() {
    EntityManager em = new MyEntity().entityManager;
    if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
    return em;
  }    
}

无论如何,它有两三个缺点:

    你需要AspectJ
  • 这行EntityManager em = new MyEntity().entityManager;很丑
  • 如果您想要模拟持久的"层",那么
  • 测试就会变得有点困难。但幸运的是,Spring提供了一个特殊的AOP拦截器(@参见JavaDoc of org.springframework.mock.staticmock.AnnotationDrivenStaticEntityMockingControl)

但是它也有一些很好的效果:例如,持久化和删除方法变得非常自然,它们只是实体的成员:

@Transactional
public void persist() {
    if (this.entityManager == null) this.entityManager = entityManager();
    this.entityManager.persist(this);
}

要使一个无Roo项目可用于@Configurable,您至少需要这样做:

扩展pom.xml:

<properties>
    <spring.version>3.0.5.RELEASE</spring.version>
    <aspectj.version>1.6.11</aspectj.version>
    <aspectj-maven-plugin.version>1.2</aspectj-maven-plugin.version>
    <maven-compiler-plugin.version>2.3.2</maven-compiler-plugin.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
    ...
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>${aspectj-maven-plugin.version}</version>
            <!-- NB: do use 1.3 or 1.3.x due to MASPECTJ-90 - wait for 1.4 -->
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjrt</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjtools</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
    <!--
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-aspects</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    -->
            </dependencies>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <outxml>true</outxml>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                    <!--
                    <aspectLibrary>
                        <groupId>org.springframework.security</groupId>
                        <artifactId>spring-security-aspects</artifactId>
                    </aspectLibrary>
                    -->
                </aspectLibraries>
                <source>1.6</source>
                <target>1.6</target>
                <encoding>utf-8</encoding>
            </configuration>
        </plugin>

Spring配置:

<!-- Turn on AspectJ @Configurable support. As a result, any time you instantiate an object,
Spring will attempt to perform dependency injection on that object.
This occurs for instantiation via the "new" keyword, as well as via reflection.
This is possible because AspectJ is used to "weave" Roo-based applications at compile time.
 In effect this feature allows dependency injection of any object at all in your system,
 which is a very useful feature (without @Configurable you'd only be able to
 dependency inject objects acquired from Spring or subsequently presented to
 a specific Spring dependency injection method). -->
 <context:spring-configured />
 <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />
  <!--
  Spring Security:
  requires version 3.0.4 of Spring Security XSD: spring-security-3.0.4.xsd
  <global-method-security ... mode="aspectj"> 
  -->

EntityManager在某种程度上是有状态的。工厂是无状态的。拥有静态实体管理器是一种称为"每个应用程序会话"(或每个应用程序实体管理器)的反模式。

注意这些是实现细节——@PersistenceContext EntityManager em是如何处理的。Hibernate提供了EntityManagerImpl,如果还没有创建Hibernate会话,它会创建一个新的Hibernate会话。但是如果它已经被创建,它会保存对它的引用(session=persistence上下文)。

拥有静态实体管理器意味着它是而不是容器管理的。这反过来意味着您要负责管理持久性上下文。

你要做的是领域驱动的设计。这是我关于DDD和JPA的一篇文章。

我个人倾向于不这样做——对象不应该能够在数据库中持久化自己——这是基础结构逻辑。为了避免代码重复,您可以简单地使用BaseDao来包装persist方法。或者甚至直接在服务层中使用EntityManager。(假设你有清晰的图层边界)

如果您真的确定要使用DDD路径,请查看本文中的示例。Spring允许您通过aspectJ在任何对象中注入实体管理器。因此,您的实体管理器将被妥善处理,以及您的事务。

最新更新