Spring data JPA: еще одна проблема LazyInitializationException - PullRequest
0 голосов
/ 13 февраля 2019

Я хочу просто получить однонаправленно связанные @OneToMany данные из базы данных и предоставить эти данные с реактивной конечной точкой Spring WebFlux.

Однако я не могу избавиться от исключения LazyInitializationException в производительном коде,В моих тестовых методах я использую @Transactional, и все работает хорошо.Но даже добавление @Transactional к методам контроллера совсем не помогло.

Простой пример:

Entity2:

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@Entity
public class Entity2 {

    @Id
    @GeneratedValue
    private Integer id;
    private String string;

}

Entity1:

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@Entity
public class Entity1 {

    @Id
    @GeneratedValue
    private Integer id;

    @JsonIgnore
    @OneToMany
    @JoinTable(
            name = "entity1_to_entity2",
            joinColumns = {@JoinColumn(name = "entity1_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "entity2_id", referencedColumnName = "id")}
    )
    private List<Entity2> entity2List;

}

SampleService:

@Service
public class SampleService {

    private final Entity1Repository entity1Repository;

    @Autowired
    public SampleService(Entity1Repository entity1Repository) {
        this.entity1Repository = entity1Repository;
    }

    public Optional<Entity1> findEntity1(Integer id) {
        return entity1Repository.findById(id);
    }

}

SampleComponent:

@Component
public class SampleComponent {

    private final SampleService sampleService;

    @Autowired
    public SampleComponent(SampleService sampleService) {
        this.sampleService = sampleService;
    }

    public List<Entity2> getList(Integer entity1_id) {
        return sampleService
                .findEntity1(entity1_id)
                .map(Entity1::getEntity2List)
                .orElse(Collections.emptyList());
    }

}

SampleController:

@RestController
public class SampleController {

    private final SampleComponent sampleComponent;

    @Autowired
    public SampleController(SampleComponent sampleComponent) {
        this.sampleComponent = sampleComponent;
    }

    @GetMapping(value = "/endpoint")
    @Transactional
    public Mono<List<Entity2>> findList(@RequestParam Integer id) {
        return Mono.just(sampleComponent.getList(id));
    }

}

Метод тестирования:

@Test
@Transactional
public void simpleTest() {
    List<Entity2> entity2List = new ArrayList<>();
    entity2List.add(Entity2.builder().string("foo").build());
    entity2List.add(Entity2.builder().string("bar").build());
    entity2Repository.saveAll(entity2List);

    Entity1 entity1 = entity1Repository.save(Entity1.builder().entity2List(entity2List).build());

    sampleController.findList(entity1.getId())
            .subscribe(
                    list -> list.forEach(System.out::println)
            );
}

Как ясказал, удалив @Transactrional из метода test, давайте провалим тест, то же самое для вызова конечной точки при запуске программы.

Я знаю, что не хочу устанавливать fetch.EAGER для отношений, поскольку для всего остального нашего кода имеет смысл лениво извлекать List<Entity2>.

Редактировать: Вв настоящее время я использую аннотированный метод @Query, где я join fetch список вручную, но это, на мой взгляд, не является общим решением.

1 Ответ

0 голосов
/ 13 февраля 2019

Проблема в том, что, поскольку getEntity2List() не вызывается в вашем коде, он сначала вызывается после завершения метода @Transactional, когда отложенная загрузка больше невозможна.

Вы можете решить эту проблемуразличными способами:

  • вызовите getEntity2List() в методе @Transactional, чтобы запустить отложенную загрузку, пока это еще возможно, или
  • получить объект с запросом, который присоединяется к@OneToMany Relationship или
  • говорит вашей библиотеке JSON, как писать ленивые загрузочные прокси.Для Джексона вы можете сделать это с помощью модуля jackson-datatype-hibernate .
...