如何使用泛型实现 Hibernate DAO

Posted

技术标签:

【中文标题】如何使用泛型实现 Hibernate DAO【英文标题】:How to implement Hibernate DAO with generics 【发布时间】:2016-10-28 21:23:17 【问题描述】:

我找到了一些关于如何使用泛型构建 Hibernate DAO 的教程,但它们都使用 EntityManager 而不是 SessionFactory。我的问题是如何使用SessionFactory 使用泛型构建DAO。到目前为止,我有以下内容:

界面:

public interface GenericDao<T> 

    public void save(T obj);
    public void update(T obj);
    public void delete(T obj);
    public T findById(long id);

类:

@Repository
public class GenericDaoImpl<T> implements GenericDao<T> 

    @Autowired
    private SessionFactory sessionFactory;

    public void save(T obj) 
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try 
            tx = session.beginTransaction();
            session.save(obj);
            tx.commit();
         catch (HibernateException e) 
            if(tx != null)
                tx.rollback();
            e.printStackTrace();
         finally 
            session.close();
        

    

    public void update(T obj) 
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try 
            tx = session.beginTransaction();
            session.update(obj);
            tx.commit();
         catch (HibernateException e) 
            if(tx != null)
                tx.rollback();
            e.printStackTrace();
         finally 
            session.close();
        

    

    public void delete(T obj) 
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try 
            tx = session.beginTransaction();
            session.delete(obj);
            tx.commit();
         catch (HibernateException e) 
            if(tx != null)
                tx.rollback();
            e.printStackTrace();
         finally 
            session.close();
        

    

    public T findById(long id) 
        // ??
        return null;
    

我不确定如何使用泛型来处理findById。我相信其他方法是对的,但如果我错了,请纠正我。

附带问题:使用EntityManager 是否比使用SessionFactory 更有益?我看到了一些关于这个主题的帖子,但想要更多的意见。

【问题讨论】:

在 Java 中,泛型是通过擦除实现的,并且您的形式类型参数 T 在运行时变为 Object。换句话说,在运行时类型T 不存在。因此,任何返回新创建的T 实例的通用方法都需要一个运行时类型标记,该方法可以使用该标记来反射性地确定它需要创建的实例的类型。因此,findById(...) 的更实用的签名是 public T findById(Class&lt;T&gt; class, long id) @scottb 所以 Class 标记将用于确定该方法需要返回哪种类型的对象?我该怎么做呢?在EntityManager 示例中,我看到了entityManager.find(type.class, id);,但我不确定如何使用SessionFactory 如果您需要返回的类型有一个无参数构造函数,那么动态创建任意类型T 的新实例的最简单方法是使用newInstance() 的方法Class&lt;T&gt;,例如。 T myObj = class.newInstance();。否则,您可能需要通过 Class&lt;T&gt; 对象使用反射来调用带有参数的适当构造函数。在这种方法中,Class&lt;T&gt; class 中的class 扮演了运行时类型标记 的角色。在 Java 中,这些有时是必需的,正是因为泛型类型在运行时不存在。 【参考方案1】:

您需要从该方法中访问Class&lt;T&gt;。您有两个选择,您可以将Class&lt;T&gt; 传递给方法:

public T findById(long id, Class<T> clazz) 
    // method implementation

或者您可以将Class&lt;T&gt; 传递到类的构造函数中,以便在方法中使用:

@Repository
public class GenericDaoImpl<T> implements GenericDao<T> 

    private Class<T> clazz;

    protected GenericDaoImpl(Class<T> clazz) 
        this.clazz = clazz;
    

    // other methods omitted

    public T findById(long id) 
        // method implementation
    

子类会将它们的类传递给超类:

public class UserDao extends GenericDaoImpl<User> 
    public UserDao() 
        super(User.class);
    

然后,使用您的clazz 实例,您可以使用Session#get 方法在您的通用方法中获取实体:

T entity = session.get(clazz, id);

有关更多信息,请参阅以下问题:

How to get a class instance of generics type T Get object by ID in Hibernate

就附带问题而言,EntityManager 是 JPA(Java Persistence API)的一部分。使用 Java API 规范而不是 Hibernate API 开发应用程序可以让您的应用程序不依赖于 Hibernate。这允许您在 Hibernate、OpenJPA 或 TopLink 等流行的 JPA 实现之间切换,而无需对代码进行更改。

This question 有更多关于区别的信息。

【讨论】:

谢谢!另一个问题——是否有理由使用EntityManager 而不是SessionFactory,反之亦然?还是他们俩主要做同样的事情? 无需添加super(User.class); 行。抽象泛型类可以找出其子类的类型参数,如下所示:github.com/acdcjunior/acdcjunior-github-io-example-projects/… @JakeMiller EntityManager 是 JPA,SessionFactory 是 Hibernate(JPA 实现)。它们不是同一件事。 EntityManager (JPA) 对应于 Session (Hibernate),就像 EntityManagerFactory (JPA) 对应于 SessionFactory (Hibernate)。通常,我们更喜欢 JPA。但是,如果您不打算将实现从 Hibernate 更改为其他(例如 TopLink),那应该没关系(我的意思是,在这种情况下,使用任何您喜欢的)。 @JakeMiller,我已经编辑了答案并对此进行了解释。 @acdcjunior 非常好!我从没想过那样做。

以上是关于如何使用泛型实现 Hibernate DAO的主要内容,如果未能解决你的问题,请参考以下文章

Spring + Hibernate DAO 作为泛型类

Generic Hibernate DAO

为 Hibernate 创建一个通用的 DAO 类

JPA的泛型DAO设计及使用

如何使用 JPA/hibernate EntityManager 和 EJB3.0 实现泛型?

泛型 Dao、服务层和多个匹配的 bean