如何在Java EE 8中实现具有相同实体管理器的通用DAO。

huangapple go评论72阅读模式
英文:

How to implement a generic DAO with the same Entity Manager in java EE 8

问题

以下是您提供的代码部分的翻译:

通用DAO

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public abstract class GenericDAO<T> {

    protected final Class<T> persistentClass;
    protected final EntityManager em;

    public GenericDAO(EntityManager em, Class<T> persistencClass) {
        this.persistentClass = persistencClass;
        this.em = em;
    }

    public List<T> find(int id) {
        List<T> list;
        Query query = em.createQuery("select e from " + persistentClass.getSimpleName() + " e" + " where e.id = :id")
                        .setParameter("id", id);
        list = query.getResultList();
        return list;
    }

    // 其他方法的翻译省略...
}

通用DAO实现

import java.util.List;
import entities.User;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class UserDAOImpl extends GenericDAO<User> {
    public UserDAOImpl(EntityManager em) {
        super(em, User.class);
    } 
}

import entities.Tag;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class TagDAOImpl extends GenericDAO<Tag> {
    public TagDAOImpl(EntityManager em) {
        super(em, Tag.class);
    }
}

Java EE中的通用DAO

import java.lang.reflect.ParameterizedType;
import java.util.List;
import javax.persistence.EntityManager;

public abstract class BaseDAO<T extends AbstractEntity> {

    private Class<T> entityClass() {
        @SuppressWarnings("unchecked")
        ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
        return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }

    protected abstract EntityManager entityManager();

    // 其他方法的翻译省略...
}

Java EE中的通用DAO实现

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;

@Stateless
public class UserDAO extends BaseDAO<User> {

    @Inject
    protected EntityManager entityManger;

    @Override
    protected EntityManager entityManager() {
        return this.entityManger;
    }
}

@Stateless
public class TagDAO extends BaseDAO<Tag> {

    @Inject
    protected EntityManager entityManger;

    @Override
    protected EntityManager entityManager() {
        return this.entityManger;
    }
}

如果我在UserDAO和TagDAO中注入不同的EntityManager实例,那么在持久化用户和更新标签时会出现问题吗?我可以提供一个包含优缺点的示例吗?

英文:

I'm a begginer in Java EE and in JPA. In the past I have been creating a simple web-project, and I have implemented a generci DAO pattern without CDI, handling the Enitiry Manager.I know that diffrent EntityManager can use the same persistent context but because persistent context is a black box for me, I inject in the DAO constractor one EntityManager passing it as argument.I use this aproach for two reasons:

  1. Seperate the operation that has to do with diffrent
    entites.
  2. Deal with concurrency problems because all
    operation will be handeld in the same EntityManager and
    Trasanction

Here is the code of generic DAO:

GenericDAO

import java.util.List ;
import javax.persistence.EntityManager ;
import javax.persistence.Query ;

public abstract class GenericDAO&lt;T&gt; {

    protected final Class&lt;T&gt; persistentClass;
    protected final EntityManager em;

    public GenericDAO(EntityManager em, Class&lt;T&gt; persistencClass) {
        this.persistentClass = persistencClass;
        this.em = em;
    }

    public List&lt;T&gt; find(int id) {
        List&lt;T&gt; list;
        Query query = em.createQuery(&quot;select e from &quot; + persistentClass.getSimpleName() + &quot; e&quot; + &quot; where e.id =:id&quot;).setParameter(&quot;id&quot;, id);
        list = query.getResultList();
        return list;
    }

    public List&lt;T&gt; findAll() {
        List&lt;T&gt; list;
        Query query = em.createQuery(&quot;select e from &quot; + persistentClass.getSimpleName() + &quot; e&quot;);
        list = query.getResultList();
        return list;
    }

    public void create(T entity) {
        em.persist(entity);
    }

    public void edit(T entity) {
        em.merge(entity);
    }

    public void remove(T entity) {
        em.remove(em.merge(entity));
    }
}

Actual implementation of generic DAO, code below:

UserDAOImpl

import java.util.List;
import entities.User;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class UserDAOImpl extends GenericDAO&lt;User&gt; {
     public UserDAOImpl(EntityManager em) {
           super(em, User.class);
     } 
}

TagDAOImpl

import entities.Tag ;
import java.util.List ;
import javax.persistence.EntityManager ;
import javax.persistence.Query ;

public class TagDAOImpl extends GenericDAO&lt;Tag&gt; {

    public TagDAOImpl(EntityManager em) {
        super(em, Tag.class);
    }
}

Now I want to implement the same pattern using Java EE injection of EntityManager. After a good search I found out the same solution like the below:

GenericDAO In Java ee

    public abstract class BaseDAO&lt;T extends AbstractEntity&gt; {

    private Class&lt;T&gt; entityClass() {
        @SuppressWarnings(&quot;unchecked&quot;)
        ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
        return (Class&lt;T&gt;) parameterizedType.getActualTypeArguments()[0];
    }

    protected abstract EntityManager entityManager();

    private final String SELECT_ALL = &quot;SELECT e FROM &quot;;
    private final String SELECT_COUNT = &quot;SELECT COUNT(e) FROM &quot;;
    private final String INSTANCE = &quot; e&quot;;

    public List&lt;T&gt; findAll() {
        return entityManager()
                .createQuery(SELECT_ALL + entityClass().getSimpleName() + INSTANCE)
                .getResultList();
    }

    public Long count() {
        Object resulet = entityManager()
                .createQuery(SELECT_COUNT + entityClass().getSimpleName() + INSTANCE)
                .getSingleResult();
        return (Long) resulet;
    }
}

Actual implementation of generic DAO in Java ee, code below:

UserDAO

@Stateless
public class UserDAO extends BaseDAO&lt;User&gt; {

    @Inject
    protected EntityManager entityManger;

    @Override
    protected EntityManager entityManager() {
        return this.entityManger;
    }
}

TagDAO

@Stateless
public class TagDAO extends BaseDAO&lt;Tag&gt; {

    @Inject
    protected EntityManager entityManger;

    @Override
    protected EntityManager entityManager() {
        return this.entityManger;
    }

}

My problem here is that if I inject EntityManager in UserDAO and TagDAO, I have two diffrent instances of EntityManager. Will this be a problem if I want to persist a user and update a tag? Can I have such an example? Including the advantages and disadvantages.

答案1

得分: 1

首先,您可以将@Inject private EntityManager添加到您的GenericDAO类中,应用DRY原则。对于EntityManager的使用及其独特性,主要取决于您在生产方法中选择的范围。如果您确定所有请求都将在@RequestScoped环境中处理,您可以使用此范围生成EntityManager,并且只要它们在同一个请求中,所有的DAO都将共享相同的bean。

要生成具有请求范围的EntityManager,您可以使用CDI bean和生产者字段或方法,如下所示:

@ApplicationScoped
public class EntityManagerProducer implements Serializable {

    @Produces
    @RequestScoped
    // @MyBusinessDatabase // 一个限定符,用于选择不同的数据库
    @PersistenceContext
    private EntityManager entityManager;

}

您还可以使用@Dependent伪范围。在这种情况下,每个被注入的entityManager将对应一个唯一的注入bean。请查看thisthis。由于EntityManager是一个轻量级实体(与EntityManagerFactory相对),在使用这种范围时几乎没有开销,并且与@RequestScoped bean相比,我们有一个优势,即可以在非请求操作(如异步或自动化的服务器端调用)中使用相同的bean。

我已经使用@Dependent范围的实体管理器有一段时间了,使用了不同的JPA实现(Eclipselink和OpenJPA),没有任何问题,只有在同一时间由太多线程更新给定实体实例时才会出现普通的乐观锁异常。在情况紧急时,您可以采用悲观的方法来处理这些情况。

英文:

First of all, you may add the @Inject private EntityManager into your GenericDAO class, applying DRY concepts.
The usage of EntityManager and it's uniqueness will depend mainly of the scope chosen on you production methods. If you are sure all requests will be handled in a @RequestScoped environment, you may produce the EntityManager with this scope and all your DAOs will share the same bean as long they are at the same request.

To produce a EntityManager with a request scope, you may use a CDI bean and a producer field or method, such as:

@ApplicationScoped
public class EntityManagerProducer implements Serializable {

    @Produces
    @RequestScoped
    // @MyBusinessDatabase // A qualifier so you can select different databases
    @PersistenceContext
    private EntityManager entityManager;

}

You may also use a @Dependent pseudo-scope. In this case, each entityManager injected will be unique to the injected bean. Please check this and this. As EntityManager is a lightweight entity (when opposed to an EntityManagerFactory), there is little overhead in using such scope, and we have, as an advantage when compared to @RequestScoped beans, the possibility to use the same bean in non-request operations, such as asynchronous or automated server-side calls.

I've been using @Depended scoped entity managers for quite some time now, with different JPA implementations (Eclipselink and OpenJPA) without any issues but the ordinary optimistic lock exception when one given entity instance is updated by too many threads at the same time. When the case is critical, you may use a pessimistic approach to handle these cases.

huangapple
  • 本文由 发表于 2020年5月31日 00:30:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/62105487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定