Наследование в HQL с использованием Spring Data JPA - PullRequest
2 голосов
/ 04 февраля 2012

У меня есть набор классов, которые наследуются от одного суперкласса:

   Super
     |
     +------+-------+
     Aaaa   Bbbb    Cccc

Каждый из Aaaa, Bbbb, Cccc должен содержать метод findByTag.Проблема в том, что я не могу определить это вообще.В следующем примере определяется конкретный findByTag для Aaaa.

public interface AaaaRepository extends SuperRepository<Aaaa> {
    @Query("select distinct a from Aaaa a " +
            "join a.tags t " +
            "join fetch a.locale where t = ?1")
    public List<Event> findByTag(Tag t);
}

Обратите внимание, что Суперкласс - @MappedSuperclass и не имеет своей собственной таблицы в базе данных.

Я хотел бы использовать какой-то "Супер""в запросе, который будет заменен в каждом классе его именем.

Моя вторая проблема заключается в том, что я не знаю, как принудительно вызвать @ElementCollection.Я должен всегда явно говорить "присоединиться к выборке" в запросе.Если он не получен, после завершения транзакции я не могу получить доступ к тем объектам, которые я явно не получил.(LazyFetch Исключения ...)

Спасибо

Ответы [ 3 ]

2 голосов
/ 09 февраля 2012

Глядя на документацию , раздел пользовательских реализаций, что насчет этого подхода:

  1. Создайте интерфейс, расширяющий репозиторий и имеющий метод findByTag без аннотаций.
  2. Создайте реализацию этого класса, и в реализации метода вы используете критерии JPA.Вам также необходимо поле класса для хранения фактического класса для объекта предметной области, потому что обобщенные элементы стираются во время компиляции.Затем вы используете это поле для построения критериев.
  3. Прочтите документацию, чтобы использовать эту реализацию в качестве базового класса для фабрики хранилищ, затем Spring Data создаст реализации для других хранилищ на основе этого настраиваемого хранилища..

    public interface MyRepository<T, ID> extends JpaRepository<T, ID> {        
        public List<Event> findByTag(Tag t);
    }
    
    public class MyRepositoryImpl<T, ID> implements MyRepository<T, ID> {
    
        private Class<T> actualClass; // initialized in the constructor
    
        public List<Event> findByTag(Tag t) {
             // here you build the criteria using actualClass field, and execute it.
        }
    }
    
    public interface AaaaRepository extends MyRepository <Aaaa, Integer> {
        // other methods...
    }
    

Посмотрите на «Пример 1.16. Пользовательский репозиторий Factory Bean» документации для создания фабричного компонента.

Когда Spring создает экземпляр реализации AaaaRepository,он будет использовать MyRepositoryImpl в качестве базового класса.

Будет ли это работать для вас?

2 голосов
/ 04 февраля 2012

Вместо того, чтобы писать это таким образом, я бы создал объект доступа к данным, похожий на псевдо-код Java ниже:

class DAO<T> {
     private Class<T> clazz;
     DAO( Class<T> class) { this.clazz = t; }
     @PersistenceContext
     private EntityManager em;

     public List<T> findByTag(Tag t ) {
        Query q = em.createQuery( "select from " + clazz.getSimpleName + "....";
        ...
        return q.getResultList();
     }
}

Надеюсь, это поможет!

0 голосов
/ 16 февраля 2012

В конце концов, я был настолько недоволен поведением и негибкостью JPA Spring Data, что написал себе небольшой инструмент для простого создания запросов.Пример использования здесь:

https://github.com/knyttl/Maite/wiki/Maite-Persistence

Есть два дочерних класса и родительский класс, которые определяют функциональность.Но хитрость заключается в свободном интерфейсе построения запроса.

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

Небольшой пример родительского класса - проверьте ссылку выше для деталей:

@Autowired
EntityManager em;

protected abstract String getName();

protected Clause select() {
    return em
            .select("DISTINCT i")
            .from(this.getName(), "i")
            .joinFetch("i.locale lf")
}

public List<T> findByTag(Tag tag) {
    return (T) this.select()
            .join("i.tags t")
            .where("t = ?", tag)
            .fetchAll();
}
...