Подтвердить что ты не робот

Spring Данные JPA и NamedEntityGraphs

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

@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);
}
4b9b3361

Ответ 1

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

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 магия.

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, потому что это самый простой в реализации, но это означает, что когда мы используем наши репозитории, нам приходится искать методы извлечения, чтобы убедиться, что мы используем тот, у которого есть правильный граф сущности. Удачи.

Источники:

У меня недостаточно репутации, чтобы публиковать все мои источники. Извините: (

Ответ 2

Используйте @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();

   ...

}

Ответ 3

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

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

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

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

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

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

Ответ 4

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

@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.