2015-08-11 7 views
22

В настоящее время я борюсь с возможностью получить только нужные мне данные. Метод findAll() должен извлекать данные, зависящие от того, где он вызван. Я не хочу писать разные методы для каждого графа сущности. Кроме того, я бы избегал вызова сущностей и формирования (повторяющихся) запросов. В основном я хочу использовать метод build в findAll, но с графом сущности, который мне нравится. Любой шанс?Весенние данные JPA и NamedEntityGraphs

@Entity 
@Table(name="complaints") 
@NamedEntityGraphs({ 
    @NamedEntityGraph(name="allJoinsButMessages", attributeNodes = { 
      @NamedAttributeNode("customer"), 
      @NamedAttributeNode("handling_employee"), 
      @NamedAttributeNode("genre") 
    }), 
    @NamedEntityGraph(name="allJoins", attributeNodes = { 
      @NamedAttributeNode("customer"), 
      @NamedAttributeNode("handling_employee"), 
      @NamedAttributeNode("genre"), 
      @NamedAttributeNode("complaintMessages") 
    }), 
    @NamedEntityGraph(name="noJoins", attributeNodes = { 

    }) 
}) 
public class Complaint implements Serializable{ 
    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue 
    private long id; 

    private Timestamp date; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "customer") 
    private User customer; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "handling_employee") 
    private User handling_employee; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name="genre") 
    private Genre genre; 

    private boolean closed; 

    @OneToMany(mappedBy = "complaint", fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
    private List<ComplaintMessage> complaintMessages = new ArrayList<ComplaintMessage>(); 

//getters and setters 
} 

И мой JPARepository

@Repository 
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{ 

    List<Complaint> findByClosed(boolean closed); 

    @EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH) 
    @Override 
    List<Complaint> findAll(Sort sort); 
} 
+0

Я думаю, что решение по-разному названных методов было бы более уместным. –

ответ

24

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

1) Префиксы. Данные jpa предоставляют несколько префиксов (find, get, ...) для имени метода. Одна из возможностей - использовать разные префиксы с разными именованными графами. Это наименьшая работа, но скрывает смысл метода от разработчика и имеет большой потенциал, чтобы вызвать некоторые неочевидные проблемы с загрузкой неправильных объектов.

@Repository 
@Transactional 
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom { 
    @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD) 
    User findByUserID(int id); 

    @EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD) 
    User readByUserId(int id); 
} 

2) CustomRepository. Другими возможными решениями являются создание настраиваемых методов запросов и внедрение EntityManager. Это решение дает вам самый чистый интерфейс для вашего репозитория, потому что вы можете назвать свои методы чем-то значимым, но значительная сложность добавить к вашему коду, чтобы обеспечить решение. И вы вручную захватываете диспетчер сущностей, а не используете Spring magic.

interface UserRepositoryCustom { 
    public User findUserWithMembershipYearsById(int id); 
} 

class UserRepositoryImpl implements UserRepositoryCustom { 
    @PersistenceContext 
    private EntityManager em; 
    @Override 
    public User findUserWithMembershipYearsById(int id) { 
     User result = null; 
     List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class) 
       .setParameter("id", id) 
       .setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears")) 
       .getResultList(); 
     if(users.size() >= 0) { 
      result = users.get(0); 
     } 
     return result; 
    } 
} 

@Repository 
@Transactional 
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom { 
    @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD) 
    User findByUserID(int id); 
} 

3) JPQL. По сути, это просто отказ от названных графиков объектов и использование JPQL для обработки ваших соединений для вас. Не идеальный, на мой взгляд.

@Repository 
@Transactional 
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom { 
    @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD) 
    User findByUserID(int id); 

    @Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????") 
    User findUserWithTags(@Param("id") final int id); 
} 

Мы пошли с вариантом 1, поскольку он является самым простым в реализации, но это не означает, когда мы используем наши хранилища мы должны смотреть на извлекающих методах, чтобы убедиться, что мы используем один с правильным графом объекта , Удачи.

Источники:

Я не хватает репутации размещать все мои источники. К сожалению :(

10

Использование @EntityGraph вместе с @Query

@Repository 
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{ 

    @EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH) 
    @Query("SELECT c FROM Complaint ORDER BY ..") 
    @Override 
    List<Complaint> findAllJoinsButMessages(); 

    @EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH) 
    @Query("SELECT c FROM Complaint ORDER BY ..") 
    @Override 
    List<Complaint> findAllJoin(); 

    ... 

}

+1

Мне нравится ваше решение. Но я столкнулся с проблемой с помощью NamedEntityGraphs - я не могу вернуть пользовательский объект, как этот '@Query (« SELECT new DTO (f) из Entity f »)' –

7

У нас была такая же проблема и построил расширение Spring Data JPA, чтобы решить:

https://github.com/Cosium/spring-data-jpa-entity-graph

Этот расширение позволяет передавать имя или динамически созданный EntityGraph в качестве аргумента любого метода репозитория.

С помощью этого расширения, вы бы этот метод немедленно доступный:

List<Complaint> findAll(Sort sort, EntityGraph entityGraph); 

И быть в состоянии назвать его с EntityGraph выбранным во время выполнения.

+0

Я сделал свой ответ более точным. –

+1

Это очень полезно. У меня есть выдающийся билет Spring Data по этой проблеме https://jira.spring.io/browse/DATAJPA-645?filter=-2. Рассматривали ли вы включение в проект Spring Data? Работает ли он с методами QueryDsl Predicate? –

+0

Здравствуйте, Алан, перед созданием https://github.com/Cosium/spring-data-jpa-entity-graph, я попросил включить динамическое включение объекта в билете https://jira.spring.io/browse/DATAJPA-749 , Но было отказано. Поддержка QueryDsl включена. Для этого есть несколько тестовых примеров. Но я не использую QueryDsl для своих проектов. Если вы найдете ошибки, не стесняйтесь сообщать об этом на трекер;) –

1

Можете ли вы попытаться создать имя EntiyGraph с дочерним элементом, которое вы запросите и укажите имя метода find all. Ex:

@EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD) 
Employee getProfileAddressRecordById(long id); 

В вашем случае: имя

@NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = { 
     @NamedAttributeNode("customer"), 
     @NamedAttributeNode("handling_employee"), 
     @NamedAttributeNode("genre") 
}) 

метода в хранилище

@EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH) 
findAllCustomerHandlingEmployeeGenre 

Таким образом, вы можете следить за различные методы FindAll.

 Смежные вопросы

  • Нет связанных вопросов^_^