如何使用泛型实现 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<T> class, long id)
。
@scottb 所以 ClassEntityManager
示例中,我看到了entityManager.find(type.class, id);
,但我不确定如何使用SessionFactory
。T
的新实例的最简单方法是使用newInstance()
的方法Class<T>
,例如。 T myObj = class.newInstance();
。否则,您可能需要通过 Class<T>
对象使用反射来调用带有参数的适当构造函数。在这种方法中,Class<T> class
中的class
扮演了运行时类型标记 的角色。在 Java 中,这些有时是必需的,正是因为泛型类型在运行时不存在。
【参考方案1】:
您需要从该方法中访问Class<T>
。您有两个选择,您可以将Class<T>
传递给方法:
public T findById(long id, Class<T> clazz)
// method implementation
或者您可以将Class<T>
传递到类的构造函数中,以便在方法中使用:
@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的主要内容,如果未能解决你的问题,请参考以下文章