Je souhaite traiter une erreur courante, mais je ne trouve pas la bonne réponse. l'erreur est:
@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); } }
Comment éviter l'utilisation de @Transactional
?
J'essaie de faire les actions suivantes dans mon SpringBootTest:
@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); } }
L'erreur est dans cette partie:
public void addTag(Tag tag) { tags.add(tag); tag.getPosts().add(this); }
Je suis ouvert à toute question supplémentaire liée à ce problème p>
Je ne souhaite pas utiliser les réponses suivantes: - Fetch.EAGER (ou quelque chose lié au refus de Fetch.LAZY) - hibernate.enable_lazy_load_no_trans = True
Le code:
@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); } }
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: newsProject.post.entity.Tag.posts, could not initialize proxy - no Session
3 Réponses :
La solution rapide serait soit:
Annotez votre méthode de test avec @Transactional
(qui annulera toutes les modifications et aura peut-être des effets secondaires indésirables. voir ce lien pour plus d'informations)
Annotez votre méthode de test avec @Rollback (false)
, qui couvrira une transaction autour de votre test et pas l'annulation.
J'ai essayé votre cas et cela fonctionne pour moi. Veuillez essayer:
@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); }
Nous bénéficions de l'enregistrement en cascade ici. N'oubliez pas d'ajouter @Transactional à votre méthode.
La réponse est simple. Nous pouvons éviter @Transactional
à 100%.
@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); }
pendant que nous testons le travail de JPA.
Utilisation dans l'interface du référentiel étend JpaRepository <... ...>
au lieu de CrudRepository <... ...>
p>
dans tagRepository
ajoutez la ligne:
List<Tag> findByNameIn(List<String> names);
@DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @Rollback(false) @ActiveProfiles("test") class PostRepositoryTest {
Après toutes ces actions, vous pouvez éviter l'utilisation de @Transactional