Рубрики
Без рубрики

Создайте запрос с подусловием для дочернего списка с помощью Spring Data JPA

Вступление Недавно я столкнулся с такой ситуацией, когда мне нужно было запросить объект с не-del… С тегами java, jpa, spring.

Вступление

Недавно я столкнулся с такой ситуацией, когда мне нужно было запросить объект с не удаленными дочерними элементами через Spring Data JPA и Hibernate.

Давайте посмотрим на модель предметной области моего тестового приложения:

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List items;

    ... getters, setters, equals, and hashcode.
}

@Entity
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user")
    private User user;

    private Boolean deleted;

    ... getters, setters, equals, and hashcode.
}

Вопрос был в следующем: как я могу запросить всех пользователей с не удаленными элементами?

Первая идея состояла в том, чтобы использовать JPQL и @Query аннотацию внутри данных Spring CrudRepository . Я написал следующий запрос:

public interface UserRepository extends CrudRepository {

    @Query("from User u left join u.items i where i.deleted = false or i.deleted is null")
    List findUserWithNonDeletedItems();

}

Тестирование

Тестовый код представляет собой:

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        userService.createUser();
        userService.makeQuery();
    }
}

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional
    public void makeQuery() {
        var result = userRepository.findUsersWithNonDeletedItems();
        assert result.get(0).getItems().size() == 1;
    }

    @Transactional
    public void createUser() {
        User userWithItems = new User();

        var items = List.of(
                new Item(userWithItems, false),
                new Item(userWithItems, true)
        );

        userWithItems.setItems(items);
        userRepository.save(userWithItems);

        User userWithoutItems = new User();
        userRepository.save(userWithoutItems);
    }
}

Когда я запустил этот код, я получил Ошибка утверждения , потому что поиск пользователей С Не удаленными элементами() вернул 2 элемента для/| пользователь С элементами , включая удаленный элемент. Причиной такого появления является отсутствие ключевого слова fetch .

Давайте объясним разницу между левым соединением и выборка левого соединения запросы.

Левое соединение

Если мы сделаем следующий запрос JPQL:

from User u
left join u.items i
where i.deleted = false or i.deleted is null

Hibernate собирается сгенерировать следующую инструкцию SQL:

SELECT u.*
FROM user u
LEFT OUTER JOIN item i ON i.user_id = u.id
WHERE i.deleted = false OR i.deleted is null

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

Выборка левого соединения

Если мы сделаем следующий запрос JPQL:

from User u
left join fetch u.items i
where i.deleted = false or i.deleted is null

Hibernate собирается сгенерировать следующую инструкцию SQL:

SELECT u.*, i.*
FROM user u
LEFT OUTER JOIN item i ON i.user_id = u.id
WHERE i.deleted = false OR i.deleted is null

В этом запросе hibernate загружает пользователей с их элементами и фильтрует элементы по удаленному столбцу. В результате мы получаем пользователей с удаленными элементами.

Решение

Результирующий запрос выглядит следующим образом:

public interface UserRepository extends CrudRepository {

    @Query("from User u left join fetch u.items i where i.deleted = false or i.deleted is null")
    List findUsersWithNonDeletedItems();

}

Он запрашивает у пользователей только не удаленные элементы.

Оригинал: “https://dev.to/golovpavel/make-a-request-with-sub-condition-for-child-list-via-spring-data-jpa-4inn”