Lombok은 java 객체에서 getter, setter, toString, constructor 등 field별로 선언해줘야하는 메소드를 빌드과정에서 자동으로 생성해주는 라이브러리이다.
특히 DTO, VO 를 생성할 때 Lombok은 개발생산성과 번거로운 단순 반복 작업을 수월하게 해주는 고마운 존재이다.
JPA에서 Entity를 생성할 때도 Lombok을 많이들 사용할 것이다. 하지만 JPA에서 Lombok을 사용할 때는 주의사항이 몇 가지 있다고 한다. 아래 사이트는 JPA와 Lombok에 주의사항을 정리한 사이트다.
Lombok and JPA: What Could Go Wrong?
우리는 JPA Entity에서 종종 @EqualsAndHashCode, @Data annotation을 사용한다. JPA 엔티티에서 Equals()/hashCode() 구현은 민감한 주제다.
For example, let’s create a test entity:
@Entity
@EqualsAndHashCode
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long id;
}
And execute the following code:
TestEntity testEntity = new TestEntity();
Set<TestEntity> set = new HashSet<>();
set.add(testEntity);
testEntityRepository.save(testEntity);
Assert.isTrue(set.contains(testEntity), "Entity not found in the set");
위 코드에서 마지막은 실패를 한다. @EqualsAndHashCode 디롬복을 해보면 아래와 같은 결과를 얻는다.
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $id = this.getId();
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
return result;
}
jpa를 통해 저장을 하면 Id가 생성되면서 hashcode가 변경된다. 그래서 HashSet에 저장된 객체는 별도의 것으로 인지하게 된다.
위에서 언급했듯이 @EqualsAndHashCode는 기본적으로 모든 객체 필드를 포함한다. 이는 @ToString도 마찬가지.
equals()/hashCode()/toString() 메소드는 객체의 모든 필드에서 호출한다. 이로 인해 JPA 엔티티에 원치 않는 부작용이 발생할 수 있다. 그것은 Lazy Attributes 로드하는 것.
예를 들면, hashCode() 메소드를 호출하면 @OneToMany에 lazy로 설정한 모두 엔티티들을 가져오게 된다. 만약 외부 트랙잭션에서 발생한다면 LazyInitializationException이 발생할 수 있다.
@ToString도 사용하려면 lazy로 지정한 필드는 제외시켜야 한다. lazy 필드에는 @ToString.Exclude 선언한다. 또는 클래스에 @ToString(onlyExplicitlyIncluded = true) 사용해거나 lazy가 아닌 필드에는 @ToString.Include를 사용한다.
JPA 가이드에 따르면, 모든 엔티티 클래스는 public 또는 protected no-argument constructor가 요구된다. @AllArgsConstructor와 @Builder는 컴파일러가 기본 Constructor를 생성하지 않는다.
따라서 항상 @NoArgsConstructor 또는 인수가 없는 명시적 생성자와 함께 사용해야 한다.
JPA와 Lombok을 작업할 때 다음 규칙을 기억하자: