Hibernate @ManyToMany org.hibernate.LazyInitializationException - PullRequest
1 голос
/ 04 апреля 2020

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

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: newsProject.post.entity.Tag.posts, could not initialize proxy - no Session

Как избежать использования @Transactional?

Я пытаюсь выполнить следующие действия в моем SpringBootTest:

@SpringBootTest
@ActiveProfiles("test")
class PostRepositoryTest {

    @Autowired
    private PostRepository postRepository;

    @Autowired
    private TagRepository tagRepository;

    @AfterEach
    void deleteFromDB(){
        postRepository.deleteAll();
        tagRepository.deleteAll();
    }

    @Test
    void deletePostWithMultipleTags(){

        Post post = new Post();

        Tag tag = new Tag(1L,"tag1", null);
        Tag tag2 = new Tag(2L,"tag2", null);
        Tag tag3 = new Tag(3L,"tag3", null);
        tagRepository.save(tag);
        tagRepository.save(tag2);
        tagRepository.save(tag3);

        post.setTitle("testTitle");

        Tag tagNew = tagRepository.findByTagName(tag.getTagName());

        post.addTag(tagNew);
        post.addTag(tagRepository.findByTagName(tag2.getTagName()));
        post.addTag(tagRepository.findByTagName(tag3.getTagName()));
        System.out.println(post);

        postRepository.save(post);

    }
}

ошибка в этой части:

    public void addTag(Tag tag) {
        tags.add(tag);
        tag.getPosts().add(this);
    }

Я открыт для любых дополнительных вопросов, связанных с этой проблемой

Я не хочу использовать следующие ответы: - Fetch.EAGER (или что-то связанное с с выключением Fetch.LAZY) - hibernate.enable_lazy_load_no_trans = True

Код:

Почта. java

@Entity
@Data
@Table
@NoArgsConstructor
@AllArgsConstructor
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_generator")
    @SequenceGenerator(name="post_generator", sequenceName = "seq_post", allocationSize = 1)
    private Long id;

    @Enumerated(EnumType.STRING)
    private Status status;

    //Some code

    @ManyToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    })
    @JoinTable(
            name = "post_tag",
            joinColumns = {@JoinColumn(name = "post_id")},
            inverseJoinColumns = {@JoinColumn(name = "tag_id")})
    private List<Tag> tags = new ArrayList<>();

    public void addTag(Tag tag) {
        tags.add(tag);
        tag.getPosts().add(this);
    }

    public void removeTag(Tag tag) {
        tags.remove(tag);
        tag.getPosts().remove(this);
    }
}

Tag. java

@Entity
@Data
@Table
@NoArgsConstructor
@AllArgsConstructor
@ToString(exclude = "posts")
public class Tag {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_generator")
    @SequenceGenerator(name="id_generator", sequenceName = "seq_tag", allocationSize = 1)
    private Long id;

    private String tagName;

    @ManyToMany(mappedBy="tags")
    List<Post> posts = new ArrayList<>();

    public void addPost(Post post) {
        posts.add(post);
        post.getTags().add(this);
    }

    public void removePost(Post post) {
        posts.remove(post);
        post.getTags().remove(this);
    }
}

Ответы [ 3 ]

1 голос
/ 05 апреля 2020

Я попробовал ваше дело, и это работает для меня. Пожалуйста, попробуйте:

@Test
@Transactional
public void test() {
    Post post = new Post();
    post.setTitle("post");

    Tag tag1 = new Tag();
    tag1.setTagName("tag1");
    tag1.getPosts().add(post);

    Tag tag2 = new Tag();
    tag2.setTagName("tag2");
    tag2.getPosts().add(post);

    post.getTags().add(tag1);
    post.getTags().add(tag2);

    postRepository.save(post1);
}

У нас есть преимущества каскадного сохранения здесь. Не забудьте добавить @Transactional в ваш метод.

0 голосов
/ 06 мая 2020

Ответ прост. Мы можем избежать @Transactional 100%.

  1. Добавьте следующие строки в класс тестирования:
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Rollback(false)
@ActiveProfiles("test")
class PostRepositoryTest {

, поскольку мы тестируем работу JPA.

Использовать в интерфейсе хранилища extends JpaRepository<..., ...> вместо CrudRepository<..., ...>

в tagRepository добавить строку:

List<Tag> findByNameIn(List<String> names);

см. Документацию.

Теперь мы можем изменить или проверить это:
@Test
    void deletePostWithMultipleTags(){

        Tag tag = new Tag(1L,"tag1", null);
        Tag tag2 = new Tag(2L,"tag2", null);
        Tag tag3 = new Tag(3L,"tag3", null);
        tagRepository.saveAll(Arrays.asList(tag, tag2, tag3));
        tagRepository.flush();

        Post post = new Post();
        post.setTitle("testTitle");

        List<Tag> tags =  tagRepository.findByNameIn(Arrays.asList(tag.getName(), tag2.getName(), tag3.getName(),));
        for (Tag tagItem : tags){
          post.addTag(tahItem);
        }

        postRepository.saveAndFlush(post);

    }

После всех этих действий вы можете избежать использования @Transactional

0 голосов
/ 04 апреля 2020

Быстрое исправление может быть следующим:

  1. Аннотируйте ваш метод теста с помощью @Transactional (который откатит все изменения и может иметь нежелательные побочные эффекты. См. эту ссылку для получения дополнительной информации)

  2. Аннотируйте ваш метод теста с помощью @Rollback(false), который будет охватывать транзакцию вокруг вашего теста и не откат.

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