Использование дженериков Java для запроса JPA findAll() с предложением WHERE

Итак, после более чем 10-летнего перерыва я возвращаюсь к Java и пробую вещи с JPA и дженериками Java. Я создал запрос findAll(other) JPA на основе дженериков, который в основном делает

SELECT * FROM source WHERE other_id = other.id;

Это где я до. Это работает, но мне интересно, есть ли лучший и более чистый способ сделать это. Использование ManagedType было сложным, и не так много полной документации или простых примеров.

Я решил сохранить свой код как можно более общим (без каламбура), поэтому я использую JPA2.

Это корень всех классов сущностей. Мне, вероятно, это не нужно, но это убережет меня от основных ошибок.

import java.io.Serializable;

public abstract class DomainObject implements Serializable {

    private static final long serialVersionUID = 1L;

    public abstract void setId(Long id);
    public abstract Long getId();

}

Это абстрактный класс DAO. Я расширяю это для классов реализации, так как мне нужно быть более конкретным, выполняя другие действия - в основном проверяя загрузку ленивых наборов.

public abstract class GenericDAOImpl<T extends DomainObject, T2 extends DomainObject> implements GenericDAO<T, T2> {

private Class<T> type;

@PersistenceContext
protected EntityManager entityManager;

public GenericDAOImpl(Class<T> type) {
    super();
    this.type = type;
}

... save and delete classes go here

@Override
public List<T> findAll(T2 where) {

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(type);
    Root<T> rootQuery = criteriaQuery.from(type);
    if (where != null) {

        EntityType<T> entity = entityManager.getMetamodel().entity(type);

        SingularAttribute<? super T, ?> attribute = null;
        for (SingularAttribute<? super T, ?> singleAttribute: entity.getSingularAttributes()) {
            // loop through all attributes that match this class
            if (singleAttribute.getJavaType().equals(where.getClass())) {
                // winner!
                attribute = singleAttribute;
                break;
            }
        }
        // where t.object = object.getID()
        criteriaQuery.where(criteriaBuilder.equal(rootQuery.get(attribute), where));
    }
    criteriaQuery.select(rootQuery);
    TypedQuery<T> query = entityManager.createQuery(criteriaQuery);

    // need this to make sure we have a clean list?
    // entityManager.clear();
    return query.getResultList();
}

Какие-либо предложения? Во всяком случае, я хочу, чтобы это было там, чтобы другие люди могли его использовать.


person Jim Richards    schedule 04.07.2014    source источник
comment
Зачем изобретать велосипед? Spring Data JPA уже делает все это и многое другое. А для ненавистников Spring Spring Data JPA отлично работает в простой среде JEE и не требует Spring (кроме внутреннего использования Spring Data JPA).   -  person M. Deinum    schedule 04.07.2014
comment
Я использую Spring для своего уровня представления, но я не готов читать более 10 000 страниц документации только для того, чтобы разработать Spring DATA JPA (и я уже много прочитал за последние несколько месяцев). Я уверен, что Spring DATA JPA может это сделать, и я был бы рад, если бы вы опубликовали примеры/ссылки. Но это работает, это не стандарт Java бесплатно, и, учитывая, что 2 месяца назад я понятия не имел об этом ... в любом случае, если я хочу знать, как работает колесо, я создам его, а не куплю.   -  person Jim Richards    schedule 04.07.2014
comment
Это должно быть опубликовано на codereview.stackexchange.com. Это сказало. Что, если у Дитя есть отец и мать, оба типа Человека?   -  person JB Nizet    schedule 04.07.2014
comment
Да, это не работает, когда есть более одного атрибута одного типа. Я обнаружил это, когда у моего Subject Entity были String username' и String password;.   -  person Jim Richards    schedule 05.07.2014


Ответы (4)


Совет Адаму Биену, если вы не хотите использовать createQuery с String и хотите обеспечить безопасность типов:

 @PersistenceContext
 EntityManager em;

 public List<ConfigurationEntry> allEntries() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<ConfigurationEntry> cq = cb.createQuery(ConfigurationEntry.class);
        Root<ConfigurationEntry> rootEntry = cq.from(ConfigurationEntry.class);
        CriteriaQuery<ConfigurationEntry> all = cq.select(rootEntry);
        TypedQuery<ConfigurationEntry> allQuery = em.createQuery(all);
        return allQuery.getResultList();
 }

http://www.adam-bien.com/roller/abien/entry/selecting_all_jpa_entities_as

person Adam    schedule 11.03.2016

Я нашел эту страницу очень полезной

https://code.google.com/p/spring-finance-manager/source/browse/trunk/src/main/java/net/stsmedia/financemanager/dao/GenericDAOWithJPA.java?r =2

public abstract class GenericDAOWithJPA<T, ID extends Serializable> {

    private Class<T> persistentClass;

    //This you might want to get injected by the container
    protected EntityManager entityManager;

    @SuppressWarnings("unchecked")
    public GenericDAOWithJPA() {
            this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    @SuppressWarnings("unchecked")
    public List<T> findAll() {
            return entityManager.createQuery("Select t from " + persistentClass.getSimpleName() + " t").getResultList();
    }
}
person R. Gulbrandsen    schedule 31.01.2015
comment
Я стараюсь вообще не использовать SQL в запросе. Это просто мое предпочтение, иначе я бы просто использовал чистый SQL в транзакциях, а затем вы написали бы свою собственную версию JPA. - person Jim Richards; 02.02.2015
comment
На самом деле это JPQL (Java Persistance Query Language), это ядро ​​JPA. Пока у Java нет запросов Linq, таких как .NET, вам нужно иметь некоторый JPQL, где основной JPA не делает то, что вы хотите. Если вы посмотрите, что на самом деле происходит, когда вы выполняете поиск (MyClass.class, id), то он переводит его в SQL и диалект, который вы используете в своем peristance.xml. Когда вы используете JPQL, он делает то же самое с вашим запросом. Вы не будете создавать свою собственную версию JPA, вы будете добавлять к основной функциональности, например, MyClass расширяет Object - person R. Gulbrandsen; 02.02.2015

вы также можете использовать namedQuery с именем findAll для всех ваших объектов и вызывать его в своем общем FindAll с помощью

entityManager.createNamedQuery(persistentClass.getSimpleName()+"findAll").getResultList();
person Vivien SA'A    schedule 19.12.2016

Это будет работать, и если вам нужен оператор where, вы можете добавить его в качестве параметра.

class GenericDAOWithJPA<T, ID extends Serializable> {

.......

public List<T> findAll() {
            return entityManager.createQuery("Select t from " + persistentClass.getSimpleName() + " t").getResultList();
    }
}
person Eduard    schedule 09.01.2015