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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4886587/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 08:28:45  来源:igfitidea点击:

Abstracting named queries in an abstract JPA DAO

javahibernatejpajpa-2.0

提问by yoav.str

I have an abstract DAO class which uses parameterized types E(Entity) and K(Primary Key). In every entity I have a @NamedQuery. I want to dynamically invoke this named query without knowing its exact name and parameter name.

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

As an example, imagine the following entity City

例如,想象以下实体 City

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

and this CityDao

还有这个 CityDao

public class CityDao extends AbstractDao<City, Long> {
    public CityDao() {
        super(City.class);
    }   
}

How should I implement the findByName()method in AbstractDaoso that I don't need to know the exact name and parameter name?

我应该如何实现该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;
        }
    }

    // ...
}

回答by J?rn Horstmann

The naming convention for named queries is usually <Entity Name>.findBy<PropertyAndAnotherProperty>, "City.findByName" in your example, so I would try to change the named queries to follow this pattern. The parameter to this query should then also have the same name, or you could use positional parameters. Your find method would then turn into

<Entity Name>.findBy<PropertyAndAnotherProperty>在您的示例中,命名查询的命名约定通常是“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;
    }
}

回答by Russ Hayward

The simplest method is to pass the name of the query to the constructor of the abstract DAO:

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

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

Then define a public static final String in City to hold the name:

然后在 City 中定义一个 public static final String 来保存名称:

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

Alternatively you could declare DaoAbstreact as abstract and then have a method like this in it:

或者,您可以将 DaoAbstreact 声明为抽象的,然后在其中包含这样的方法:

public abstract String getFindByNameQueryName();

And implement that in ConcreteCityDao.

并在 ConcreteCityDao 中实现它。

Finally you could also introduce an enumeration:

最后,您还可以引入枚举:

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;
    }
}

Then your DAO can determine the type from the class passed in. To ensure you don't forget to add an entity to the enumeration you can make each entity implement an interface with a getNamedEntityType() method. Then you can specify that your abstract generic DAO will only accept entities that implement that interface.

然后您的 DAO 可以从传入的类中确定类型。为确保您不会忘记向枚举添加实体,您可以使用 getNamedEntityType() 方法使每个实体实现一个接口。然后您可以指定您的抽象通用 DAO 将只接受实现该接口的实体。

回答by axtavt

The obvious way would be to pass values from concrete classes to the abstract superclass using abstractmethod

显而易见的方法是使用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";
    }
}

or as a constructor argument:

或作为构造函数参数:

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");
    }
}

Though this requires consistent naming of query parameters for different entities.

尽管这需要对不同实体的查询参数进行一致的命名。

Also note the minor improvements in these snippets.

还要注意这些片段中的小改进。

回答by Arjan Tijms

What you basically seem to want is to annotate the annotations that define the named queries, in such a way that you can programmatically discover what the "findByName" query is (and possible other queries).

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

Since this is not possible in Java, you could use the fact that @NamedQuery supports query hints, that are defined as being vendor specific. Unknown hints are ignored. You could add your own data here, that the generic DAO can read back from entityClass:

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

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