我有一个抽象的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<City, Long> {
public CityDao() {
super(City.class);
}
}
如何在findByName()
中实施AbstractDao
方法,以便我不需要知道确切的名称和参数名称?
public abstract class AbstractDao<E, K> implements Dao<E, K> {
@PersistenceContext
protected EntityManager entityManager;
protected Class<E> entityClass;
protected AbstractDao(Class<E> 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;
}
}
// ...
}
答案 0 :(得分:11)
命名查询的命名约定通常是示例中的<Entity Name>.findBy<PropertyAndAnotherProperty>
,“City.findByName”,因此我会尝试更改命名查询以遵循此模式。此查询的参数也应该具有相同的名称,或者您可以使用位置参数。然后,您的查找方法将变为
@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;
}
}
答案 1 :(得分:1)
最简单的方法是将查询名称传递给抽象DAO的构造函数:
public DaoAbstreact(Class myClass, String findByNameQueryName) {
this.myClass = myClass;
this.findByNameQueryName = findByNameQueryName;
}
然后在City中定义一个公共静态final String来保存名称:
public class ConcreteCityDao<City,Long> extends DaoAbstreact {
ConcreteCityDao(){
super(City.class, City.FIND_BY_NAME_QUERY_NAME));
}
}
或者你可以将DaoAbstreact声明为抽象,然后在其中有一个这样的方法:
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仅接受实现该接口的实体。
答案 2 :(得分:0)
显而易见的方法是使用abstract
方法将值从具体类传递给抽象超类
public abstract class AbstractDao<E, K extends Serializable> implements Dao <E, K> {
...
protected abstract String getFindByNameQueryName();
@Override
public E findByName(String EntityStr) {
... entityManager.createNamedQuery(getFindByNameQueryName()) ...
}
}
@Override
public class ConcreteCityDao<City,Long> extends DaoAbstreact{
...
protected String getFindByNameQueryName() {
return "findCityByName";
}
}
或作为构造函数参数:
public abstract class AbstractDao<E, K extends Serializable> implements Dao<E, K> {
public AbstractDao(Class<E> myClass, String findByNameQueryName) { ... }
...
}
@Override
public class ConcreteCityDao<City, Long> extends DaoAbstreact{
public ConcreteCityDao() {
super(City.class, "findCityByName");
}
}
虽然这需要为不同实体一致地命名查询参数。
另请注意这些片段的细微改进。
答案 3 :(得分:0)
您基本上似乎想要的是注释定义命名查询的注释,以便您可以以编程方式发现“findByName”查询的内容(以及可能的其他查询)。
由于这在Java中是不可能的,因此可以使用@NamedQuery支持查询提示的事实,查询提示被定义为特定于供应商。未知提示将被忽略。您可以在此处添加自己的数据,通用DAO可以从entityClass
回读:
@NamedQuery(
name="findCityByname",
query="FROM CITY c WHERE name = :CityName",
hints=@QueryHint(name="genericDAO.type", value="findByName")
)