Entity отваливается от управления Spring Data во время транзакции

Имеется обычный бин спринга. В нем некий метод, не отмеченный аннотацией @Transactional, дергает метод приведенный ниже:

@Transactional //spring tx, not javax
public void saveSubCategories(int id, ...) {
    Optional<Category> optionalCategory = categoryRepository.findById(id);
    //some code
    Category category = optionalCategory.get();
    List<SubCategory> dbSubCategories = category.getSubCategories();
    int size = dbSubCategories.size();
    ///some code

Любое взаимодействие с dbSubCategories (для примера взял получение размера списка) вызывает LazyInitException.

Jpa entity настроены так:

@Entity
@Table(name = "category")
public class Category {
    //fields and getters/setters

    @OneToMany(mappedBy = "category", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    public List<SubCategory> getSubCategories() { return subCategories; }

    public void setSubCategories(List<SubCategory> subCategories) { this.subCategories = subCategories; }
@Entity
@Table(name = "subcategory")
public class SubCategory {
    //fields and getters/setters

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "category_id")
    public Category getCategory() { return category; }

    public void setCategory(Category category) { this.category = category; }

Также пожалуй важно заметить что в бд таблица subcategory пуста, т.е. должно бы просто вернуть size = 0;

Если поставить в Category fetch = EAGER то работает корректно. В ходе экспериментов также выяснилось что если в том же методе дальше выполнить

SubCategory subCategory = new SubCategory();
subCategory.setCategory(category);
subCategoryRepository.save(subCategory);

То все падает с ошибкой springframework.dao.InvalidDataAccessApiUsageException ... detached entity passed to persist: entity.Category

Как это так? Транзакция ведь не закрыта, все выполняется в одном методе, но объект category отваливается от управления jpa.

И как избежать использования fetch = EAGER? И почему падает LazyInit во время транзакции?


Ответы (1 шт):

Автор решения: skayt15

А ларчик просто открывается:

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object calling another method of the target object) does not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative-annotations

→ Ссылка