Абстрагирование именованных запросов в абстрактном JPA DAO
Автор вопроса: yoav.strОтветов: 4
У меня есть абстрактный класс 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, чтобы мне не нужно было знать точное имя и имя параметра?
Соглашение об именах для именованных запросов обычно <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;
}
}
Ответ 2 · рейтинг 1 · Russ Hayward
Самый простой способ - передать имя запроса конструктору абстрактного DAO:
Затем определите общедоступную статическую конечную строку в City для хранения имени:
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 будет принимать только объекты, реализующие этот интерфейс.
Ответ 3 · axtavt
Очевидным способом было бы передать значения из конкретных классов в абстрактный суперкласс, используя метод 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");
}
}
Хотя это требует согласованного именования параметров запроса для разных сущностей.
Также обратите внимание на незначительные улучшения в этих фрагментах.
Ответ 4 · Arjan Tijms
В основном вы хотите аннотировать аннотации, которые определяют именованные запросы, таким образом, чтобы вы могли программно обнаружить, что такое запрос "findByName" (и возможные другие запросы).
Поскольку это невозможно в Java, вы можете использовать тот факт, что @NamedQuery поддерживает подсказки запросов, которые определены как зависящие от поставщика. Неизвестные подсказки игнорируются. Вы можете добавить сюда свои собственные данные, которые общий DAO может считывать из entityClass:
@NamedQuery(
name="findCityByname",
query="FROM CITY c WHERE name = :CityName",
hints=@QueryHint(name="genericDAO.type", value="findByName")
)