static access to entity manager in spring and unusual architecture

前端 未结 2 926
情话喂你
情话喂你 2020-12-09 23:53

quick question:

I have webapplication (wicket+spring+jpa) and was thinking about rather unusual architecture design. Please check it out and give your comments.

相关标签:
2条回答
  • 2020-12-10 00:16

    The EntityManager is stateful (in a way). The factory is stateless. And having a static entity manager is an anti-pattern called "session per application" (or entity manager per application).

    Note that these are implementation details - how is @PersistenceContext EntityManager em handled. Hibernate provides EntityManagerImpl which creates a new hibernate session if one isn't already created. But if it is already created, it holds a reference to it (the session=persistence context).

    Having a static entity manager means it is not container-managed. Which in turn means that you are responsible for managing the persistence context.

    What you are trying to do is Domain-driven design. Here is an article of mine about DDD and JPA.

    My personal preference is not to go that way - the object should not be able to persist itself in a database - this is infrastructure logic. In order to avoid code duplication you can simply have a BaseDao which wraps the persist method. Or even directly use the EntityManager in your service layer. (This assumes you have clear layer boundaries)

    If you are really sure you want to go the DDD path, take a look at the examples in the article. Spring allows you to inject the entity manager in any object, via aspectJ. So your entity manager will be properly handled, as well as your transactions.

    0 讨论(0)
  • 2020-12-10 00:32

    You should have a look at Spring Roo. They have something simular (no DAOs or Services), but there EntityManager is not static.

    They do the trick with the @Configurable annotation in Entities:

    @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;
      }    
    }
    

    Anyway it has two or three drawbacks:

    • you need AspectJ
    • this line EntityManager em = new MyEntity().entityManager; is very ugly
    • testing becomes a bit difficult if you want to mock the persistent "layer". But fortunally the Spring provides a special AOP Interceptor (@see JavaDoc of org.springframework.mock.staticmock.AnnotationDrivenStaticEntityMockingControl)

    But it has also some nice effects: for example the persist and delete methods become very natural, they are just member of the entity:

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

    To make a none Roo project aviable for @Configurable you need to at LEAST doing this:

    extend the 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 config:

    <!-- 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"> 
      -->
    
    0 讨论(0)
提交回复
热议问题