在抽象的JPA DAO中抽象命名查询

我有一个抽象的DAO类,它使用参数化类型E (实体)和K (主键)。 在每个实体中我都有一个@NamedQuery 。 我想在不知道其确切名称和参数名称的情况下动态调用此命名查询。

举个例子,想象一下下面的实体City

 @Entity(name="CITY") @NamedQuery( name="findCityByname", query="FROM CITY c WHERE name = :CityName" ) public class City { // ... } 

而这个CityDao

 public class CityDao extends AbstractDao { public CityDao() { super(City.class); } } 

我应该如何在AbstractDao实现findByName()方法,以便我不需要知道确切的名称和参数名称?

 public abstract class AbstractDao implements Dao { @PersistenceContext protected EntityManager entityManager; protected Class entityClass; protected AbstractDao(Class entityClass) { this.entityClass = entityClass; } @Override public E findByName(String name) { try { return (E) entityManager .createNamedQuery("findCityByName") .setParameter("CityName", name) .getSingleResult(); } catch(Exception e) { return null; } } // ... } 

命名查询的命名约定通常是.findBy ,“City.findByName”,因此我将尝试更改命名查询以遵循此模式。 此查询的参数也应该具有相同的名称,或者您可以使用位置参数。 然后你的find方法会变成

 @Override public E findByName(String name) { E entity = null; try { return (E)entityManager.createNamedQuery(myClass.getSimpleName() + ".findByName") .setParameter("name", name) .getSingleResult(); } catch (Exception ex) { return null; } } 

最简单的方法是将查询的名称传递给抽象DAO的构造函数:

 public DaoAbstreact(Class myClass, String findByNameQueryName) { this.myClass = myClass; this.findByNameQueryName = findByNameQueryName; } 

然后在City中定义一个公共静态final String来保存名称:

 public class ConcreteCityDao extends DaoAbstreact { ConcreteCityDao(){ super(City.class, City.FIND_BY_NAME_QUERY_NAME)); } } 

或者你可以将DaoAbstreact声明为abstract,然后在其中有一个这样的方法:

 public abstract String getFindByNameQueryName(); 

并在ConcreteCityDao中实现它。

最后你还可以介绍一个枚举:

 public enum NamedEntityType { CITY(City.class, "findCityByname"), PERSON(Person.class, "findPersonByname"); private final Class entityClass; private final String findByNameQueryName; private NamedEntityType(Class entityClass, String findByNameQueryName) { this.entityClass = entityClass; this.findByNameQueryName = findByNameQueryName; } public Class getEntityClass() { return entityClass; } public String getFindByNameQueryName() { return findByNameQueryName; } } 

然后你的DAO可以确定传入的类的类型。为了确保你不要忘记在枚举中添加一个实体,你可以使每个实体实现一个带有getNamedEntityType()方法的接口。 然后,您可以指定您的抽象通用DAO仅接受实现该接口的实体。

显而易见的方法是使用abstract方法将值从具体类传递给抽象超类

 public abstract class AbstractDao implements Dao  { ... protected abstract String getFindByNameQueryName(); @Override public E findByName(String EntityStr) { ... entityManager.createNamedQuery(getFindByNameQueryName()) ... } } @Override public class ConcreteCityDao extends DaoAbstreact{ ... protected String getFindByNameQueryName() { return "findCityByName"; } } 

或者作为构造函数参数:

 public abstract class AbstractDao implements Dao { public AbstractDao(Class myClass, String findByNameQueryName) { ... } ... } @Override public class ConcreteCityDao extends DaoAbstreact{ public ConcreteCityDao() { super(City.class, "findCityByName"); } } 

虽然这需要为不同实体一致地命名查询参数。

另请注意这些片段的细微改进。

您基本上似乎想要的是注释定义命名查询的注释,以这种方式您可以以编程方式发现“findByName”查询是什么(以及可能的其他查询)。

由于这在Java中是不可能的,因此可以使用@NamedQuery支持查询提示的事实,查询提示被定义为特定于供应商。 未知提示将被忽略。 您可以在此处添加自己的数据,通用DAO可以从entityClass读回:

 @NamedQuery( name="findCityByname", query="FROM CITY c WHERE name = :CityName", hints=@QueryHint(name="genericDAO.type", value="findByName") )