在抽象的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; } } // ... }
命名查询的命名约定通常是
,“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") )
- 如何根据用户的选择动态生成SQL查询?
- hibernate envers:merge&saveOrUpdate
- exception:NumberFormatException:对于JSP列表页面中的输入字符串
- Sprint引导数据JPA:没有类型为’java.util.Set ‘的限定bean
- Hibernate – Custom Dialect的BigDecimal列映射
- 在Hibernate 5中替换org.hibernate.Transactions.isActive()
- org.hibernate.TransientObjectException:object引用未保存的瞬态实例 – 在刷新之前保存瞬态实例
- Hibernate UnknownServiceException:在事务完成时请求未知服务
- 你如何使用注释在hibernate中映射“地图”?