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

时间:2011-02-03 13:17:01

标签: java hibernate jpa jpa-2.0

我有一个抽象的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;
        }
    }

    // ...
}

4 个答案:

答案 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")
)