【问题标题】:Abstracting named queries in an abstract JPA DAO在抽象 JPA DAO 中抽象命名查询
【发布时间】:2011-06-20 16:06:42
【问题描述】:

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

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

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

    // ...
}

【问题讨论】:

    标签: java hibernate jpa jpa-2.0


    【解决方案1】:

    您基本上想要的是对定义命名查询的注释进行注释,以便您可以通过编程方式发现“findByName”查询是什么(以及可能的其他查询)。

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

        然后在 City 中定义一个 public static 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 将只接受实现该接口的实体。

        【讨论】:

          【解决方案4】:

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

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

          还要注意这些 sn-ps 的细微改进。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-10-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多