Абстрагирование именованных запросов в реферате JPA DAO - PullRequest
9 голосов
/ 03 февраля 2011

У меня есть абстрактный класс DAO, который использует параметризованные типы E (Entity) и K (Primary Key). В каждой сущности у меня есть @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 ]

11 голосов
/ 09 февраля 2011

Соглашение об именовании для именованных запросов обычно равно <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 голос
/ 03 февраля 2011

Самый простой способ - передать имя запроса конструктору абстрактного DAO:

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

Затем определите общедоступную статическую итоговую строку в городе для имени:

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 будет принимать только объекты, реализующие этот интерфейс.

0 голосов
/ 13 февраля 2011

То, что вы в основном хотите, - это аннотировать аннотации, которые определяют именованные запросы, таким образом, чтобы вы могли программно обнаружить, что такое запрос "findByName" (и, возможно, другие запросы).

Поскольку в Java это невозможно, вы можете использовать тот факт, что @NamedQuery поддерживает подсказки запросов, которые определяются как специфичные для поставщика. Неизвестные подсказки игнорируются. Здесь вы можете добавить свои собственные данные, которые DAO может читать из entityClass:

@NamedQuery(
    name="findCityByname",
    query="FROM CITY c WHERE name = :CityName",
    hints=@QueryHint(name="genericDAO.type", value="findByName")
)
0 голосов
/ 03 февраля 2011

Очевидным способом было бы передать значения из конкретных классов в абстрактный суперкласс, используя 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");
    }
}

Хотя для этого требуетсясогласованное именование параметров запроса для различных объектов.

Также обратите внимание на незначительные улучшения в этих фрагментах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...