본문 바로가기

삽질

Spring Data JDBC with..메소드와 final에 관한 의문

공식문서에 Spring Data JDBC에서 객체를 생성하는 알고리즘을 다음과 같이 설명하고있다.

  1. If the property is immutable but exposes a with… method (see below), we use the with… method to create a new entity instance with the new property value.
  2. If property access (i.e. access through getters and setters) is defined, we’re invoking the setter method.
  3. If the property is mutable we set the field directly.
  4. If the property is immutable we’re using the constructor to be used by persistence operations (see Object creation) to create a copy of the instance.
  5. By default, we set the field value directly.

4번에 따르면 wither가 없어도 all-arguments 생성자를 통해서 create 되야 하는 게 아닌가?

General recommendations에도

For identifiers to be generated, still use a final field in combination with an all-arguments persistence constructor (preferred) or a with… method —

또는 with... 메소드인데 왜 all-arguments counstructor 만으로 안될까?

몇 시간에 걸쳐 이런저런 실험을 해봤지만 답을 찾을 수 없었다.🤔

---2020.10.03

다섯 달이 지났는데 아직도 안될까해서 다시 해보니까 된다. 저 글을 쓸 당시에는 정말 안됐었는데 그때 실험했던 코드를 올려놨었으면 같은 코드로 실험을 했을텐데 아쉽다.

저 때도 꽤 오랜시간 삽질을 했으니 안됐었던 건 확실하다. 무튼 지금은 공식 문서 내용대로 wither 메서드 없이 all argument constructor 만으로 불변 엔티티를 잘 사용할 수 있다!

public class Article {
    @Id
    private final Long id;
    private final String title;
    private final String content;
    private final Set<Comment> comments;

    public static Article of(String title, String content, Set<Comment> comments) {
        return new Article(null, title, content, comments);
    }

    Article(Long id, String title, String content, Set<Comment> comments) {
        this.id = id;
        this.title = title;
        this.content = content;
        this.comments = comments;
    }

    public void addComment(Comment comment) {
        this.comments.add(comment);
    }

    public Long getId() {
        return id;
    }
}
public class Comment {
    @Id
    private final Long id;
    private final String content;

    public static Comment of(String content) {
        return new Comment(null, content);
    }

    Comment(Long id, String content) {
        this.id = id;
        this.content = content;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Comment comment = (Comment) o;
        return Objects.equals(id, comment.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}
public interface ArticleRepository extends CrudRepository<Article, Long> {

    @Override
    List<Article> findAll();
}
@DataJdbcTest
class ArticleRepositoryTest {
    @Autowired
    ArticleRepository articleRepository;

    @DisplayName("create")
    @Test
    void create() {
        Article article = Article.of("title", "content", new HashSet<>());
        article.addComment(Comment.of("1"));
        Article save = articleRepository.save(article);

        Article find = articleRepository.findById(save.getId()).get();

        assertThat(find).isEqualToComparingFieldByField(save);
    }
}