3

quick question:

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

Consider class Wrapper:

@Service
public class Wrapper {
    protected static EntityManager entityManager;

    @PersistenceContext
    private void injectEntityManager(EntityManager entityManager) {
        Wrapper.entityManager = entityManager;
    }

as you see I have now EntityManager injected statically.

Now consider simple entity DogEntity

@Entity
public class DogEntity {
   String name;
}

And for that entity we create wrapper 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);
  }
}

Now in my webapplication (in my controller) I can do something like this:

saveButton = new Button("save") {

public void onSubmit() {
   Dog dog = Dog.create(name);
   // other code 
}

From code point of view this architecture looks perfect. We have wrappers representing business objects. They all have persistent state, there are no stupid services in application called DogSaver with method save(DogEntity) which only call persist on entity manager. Code really gets a lot of readability and there some other advantages, but I dont wont go into details.

What really is my concern is this static EntityManager. I do not have enough knowledge about internals of Spring to know whether this approach is proper and safe. Are there situations where things mihgt get ugly? I know that EntityManare is stateless (according to JPA spec), it always takes persistence context from transaction, thus making it static does not seem a bad idea. But I fear that I might be messing something.

Any thoughts?

Pawel Szulc
  • 1,041
  • 2
  • 12
  • 19

2 Answers2

5

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:

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"> 
  -->
Daniel Serodio
  • 4,229
  • 5
  • 37
  • 33
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • I know @Configurable but it requires weaving and aspectJ. I don't have expreinece with those, but people who did try it, said it is not easy as it sounds from tutorials. – Pawel Szulc Aug 10 '11 at 08:05
  • Paul Szulc: I always use AspectJ for spring intead of this Proy AOP Stuff because it makes everything more predictable - (have a look at the thousends of Stackoverflow question which causes from the fact that proxy aop works only if you call the method from an other bean). On the other hand I have done several not trivial applications based on Spring Roo (the example above, is exactly what Roo does), and I never have had problems with the @Configurable stuff, even if I am not have so much experiance in AspectJ -- So i recommend: If you have time then give this idea a try. – Ralph Aug 10 '11 at 08:14
  • Raplph: do you know easiest way to make @Configurable work on a non-Roo project. Most importantly, how to make weaving happen automatically for entities when project is typical Maven project? I've seen people used surefire-plugin for Spring 2.5.6 no good example for Spring 3.0. (and there were changes in 3.0 that made examples for 2.5.6 impossible to reproduce). – Pawel Szulc Aug 10 '11 at 09:01
  • @Paul Szulc: I have extended the answer -- I use this feature since 3.0 so I don't know the differences to 2.5.6, and I do not know what surefire has to do with aspectJ -- anway that is a part of my current configuration – Ralph Aug 10 '11 at 09:49
2

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.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • I'm not exactly trying to achieve DDD, but this is not the issue right now. You said that EntityManager is stateful, but can you explain in what way it is stateful? Because from what I know EntityManager is 100% stateless. It always takes persistence context from transaction, each time you act upon it. What do you mean that EntityManager is stateful? – Pawel Szulc Aug 10 '11 at 08:01
  • the entity manager (and the underlying session) store all entities in a first-level cache. That's its state. And it is not thread-safe – Bozho Aug 10 '11 at 08:14
  • well, it is not true. 1) persistence context is the one who is storing all entities (that is the first-level cache). Entity manger acts upon persistence context, but EM is not holding reference to persistence context - it always gets it from transaction. Secondly, yes, it is not thread-safe, but when you annotate @PersistencContext you get proxy to entity manager, not entity manager itself. – Pawel Szulc Aug 10 '11 at 08:35
  • What do refer to as "persistence context"? The `EntityManagerImpl` that hibernate provides simply wraps the hibernate Session, which holds a map of entities. You get the transaction from the manager, not the other way around. – Bozho Aug 10 '11 at 08:42
  • in addition - hibernate provides a `StatelessSession` class, to contrast the standard session, which is stateful. – Bozho Aug 10 '11 at 08:43
  • in that case this would be incorrect according to JPA specification. I must then look to Hibernate implementation, but according to JPA spec, Entity Manager is stateless, not thread-safe object, that never holds reference to persistence context (set of attached entity objects), but always get them from transaction. According to specification persistence context can be attached to one (and only one) transaction. Of course when we talk about TransactionScoped configuration (not Extended). – Pawel Szulc Aug 10 '11 at 08:49
  • can you give me the piece of the spec that you think this is contradicting? (I'd doubt that, since initially JPA was based on Hibernate, and hibernate was the reference implementation) – Bozho Aug 10 '11 at 08:50
  • sure: http://download.oracle.com/otndocs/jcp/persistence-2.0-fr-eval-oth-JSpec/ section 7.6.3 Persistence Context Propagation or even whole 7.6 chapter – Pawel Szulc Aug 10 '11 at 08:57
  • I don't see how that contradicts my explanation, or how it means that the context is stateless. The entity managers holds a reference to the persistence context (the hibernate session in this case), otherwise how can it operate on it? – Bozho Aug 10 '11 at 09:04
  • According to specification, entity manager does NOT hold reference to persistence context. "How can it operate on it?" Each time it asks transaction for actual persistence context. This is called "persistence context propagation" which was described in section 7.6.3 of the specification. In other words, "persistence context propagation" means that entity manager never holds refenrence to persistene context. In order to operate on it, it musk ask transaction for it. Each transaction can be bind to one and only one persistence context. – Pawel Szulc Aug 10 '11 at 09:57
  • I think it's explained more clearly here: http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/architecture.html . In short: Session=Persistence Context. The thing is, multiple injected EntityManager instances have the same underlying Session instance. And the propagation affects mostly stateful session beans, which you don't have. I don't see where it says that the persistence context is stored in a transaction. – Bozho Aug 10 '11 at 14:14
  • I will add a clarification to the answer – Bozho Aug 10 '11 at 14:17