0

I have a relationship between entities that throws a stack overflow error if the @Data annotation from Lombok is used instead of the individual @Getter and @Setter annotations. This is fixed now, but I would like to write a unit test for it within my repository tests. However, I'm not sure how to achieve that and haven't been able to find samples for it.

Here are my entity classes:

@Entity
@Table(name = "users")
@Builder
//@Getter
//@Setter
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @Column(name = "name")
    private String name;

    @ManyToMany
    @JoinTable(
            name = "users_hobbies",
            joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "hobby_and_interest_id", referencedColumnName = "id"))
    private Set<HobbyAndInterestEntity> hobbyAndInterestEntities;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
    @JoinColumn(name = "hometown_id", referencedColumnName = "id")
    private HometownEntity hometownEntity;


@Entity
@Table(name = "hometown")
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HometownEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @Column(name = "city")
    private String city;

    @Column(name = "country")
    private String country;

    @OneToMany(mappedBy = "hometownEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = false)
    private Set<UserEntity> userEntitySet;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class HobbyAndInterestEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @Column(name = "title")
    private String title;

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "hobbyAndInterestEntities")
    private Set<UserEntity> userEntities;

And here is my test for a case without the exception, which I was aiming to modify to test for the exception scenario:

    @Test
    void testGetUser() {

        UserEntity userEntity = saveUserEntity();

        assertTrue(userRepository.findAll().size() > 0);
        userEntity = userRepository.findById(userEntity.getId()).orElse(null);
        assertNotNull(userEntity);

        UserEntity finalUserEntity = userEntity;
        assertAll(
                () -> assertEquals("anyName", finalUserEntity.getName()),
                () -> assertEquals("anyCountry", finalUserEntity.getHometownEntity().getCountry()),
                () -> assertTrue(finalUserEntity.getHobbyAndInterestEntities().size() > 0));

        finalUserEntity.getHobbyAndInterestEntities().forEach(h -> assertEquals("anyInterest", h.getTitle()));
    }


    @NotNull
    private UserEntity saveUserEntity() {

        HometownEntity hometownEntity = HometownEntity.builder().city("anyCity").country("anyCountry").build();
        hometownEntity = hometownRepository.save(hometownEntity);

        HobbyAndInterestEntity hobbyAndInterestEntity = HobbyAndInterestEntity.builder()
                .title("anyInterest")
                .build();
        hobbyAndInterestEntity = hobbyAndInterestRepository.save(hobbyAndInterestEntity);
        Set<HobbyAndInterestEntity> hobbyAndInterestEntities = new HashSet<>();
        hobbyAndInterestEntities.add(hobbyAndInterestEntity);

        UserEntity userEntity = UserEntity.builder()
                .name("anyName")
                .hometownEntity(hometownEntity)
                .hobbyAndInterestEntities(hobbyAndInterestEntities)
                .build();

        return userRepository.save(userEntity);
    }

So in summary, I know the application is throwing the stack overflow when I have the @Data annotation and so I would like to write a test that would fail for it and pass again when I modify the entity class to use @Getter and @Setter, but not sure what is needed here and would appreciate some guidance, please.

Thank you very much.

Francislainy Campos
  • 3,462
  • 4
  • 33
  • 81
  • 1
    your initial diagnosis of root cause seems to be too naive, that is not about [`@Data` annotation](https://stackoverflow.com/a/73078671/3426309), but about how you define `#equals` and `#hashCode` for entities. So, if we are talking about unit tests, you actually need to enforce "correct" definition of `#equals` and `#hashCode` method for all entities. – Andrey B. Panfilov Dec 28 '22 at 11:09
  • Hi, sorry, not sure I understand what you mean by enforcing correct definition for the entities? But yes, you're correct, it's those methods included in the Data annotation that cause the Stack Overflow issue. – Francislainy Campos Dec 28 '22 at 12:03
  • Not sure that I understand your question but are you not already there? Fixing the stackoverflow by adding `@EqualsAndHashCode( exclude = { "hobbyAndInterestEntities", "hometownEntity"}` in your `UserEntity`. The Unitt-Test is then simply using `.equals()` or `.hashCode()`. The Stackoverflow will always be thrown as long as the problem is present. – GJohannes Dec 28 '22 at 16:08
  • Hi, my point is that I had the basic repository tests for my saving and retrieving and they all are passing while I had the @Data annotation but running the application would still throw the StackOverflow exception, so I'm trying to prevent this from happening again with some more robust test. – Francislainy Campos Dec 29 '22 at 05:40
  • I understand your point about explicit tests for the equals and hashCode piece and I'll have a look at this, but as that's not explicitly called inside my application code I was wondering if there's some other way to test for this? – Francislainy Campos Dec 29 '22 at 05:42

1 Answers1

-1

Could you check @Data annotation here. @Data is a shortcut for @ToString, @EqualsAndHashCode, @Getter on all fields, @Setter on all non-final fields, and @RequiredArgsConstructor! When you call toString or equals or hashCode method, the relationship entities will query in the database. You can try to review generated source, the relationship entities is used in those methods. I think it can throw a stack overflow error.

  • Hi, thank you for your answer, but the question is not really about the application code, but how to test for it. – Francislainy Campos Dec 28 '22 at 10:58
  • I know the application is throwing the stack overflow when I have the @Data annotation and so I want to write a test that would fail for it and pass when I modify the entity class to use Getter and Setter. – Francislainy Campos Dec 28 '22 at 11:00