Spring/Java

[Spring] @Embedded, @Embeddable

java곰 2024. 6. 27. 23:31
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private LocalDateTime eventDate;

    @Column(nullable = false)
    private Integer amount;

    @Enumerated(EnumType.STRING)
    private TransactionAction action;

    @Column(columnDefinition = "TEXT")
    private String memo;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

다음과 같이 거래 내역에 관련된 entity가 있습니다.

거래와 관련된 api들은 금액들을 통계, 총합하는 등 여러 추가적인 api들이 있을 수 있는데 이것들을 위해 transactionAmount라는 class를 만들어 transaction api에서 책임 분리 및 더욱 객체지향적으로 설계할 수 있습니다.

public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private LocalDateTime eventDate;

    @Embedded
    private TransactionAmount amount;

    @Column(columnDefinition = "TEXT")
    private String memo;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

위와 같이 @Embedded를 사용하여 amount, action을 분리한 모습입니다.

@Getter
@Embeddable
@NoArgsConstructor(force = true)
public class TransactionAmount {
    @Enumerated(EnumType.STRING)
    private final TransactionAction action;
    private final Long amount;

    public TransactionAmount(Long amount, String action) {
        this.amount = amount;
        this.action = TransactionAction.valueOf(action);
    }

    public Long getAmount() {
        return this.amount != null ? amount : 0;
    }
}

분리된 transactionAmount class의 모습입니다. 해당 클래스에서는 @Embeddable을 명시해줘야합니다.

서비스 레이어에서도 활동하는 객체이기 때문에 final을 적용하여 필드의 추가적인 수정이 금지되어있으며 생성자에서도 강제성을 부여하여 작동하고있습니다.

  @Query("""
           SELECT new com.damo.server.domain.transaction.TransactionTotalAmount(
                 new com.damo.server.domain.transaction.entity.TransactionAmount(
                     SUM(CASE WHEN t.amount.action = 'GIVING' THEN t.amount.amount ELSE 0 END), 'GIVING'
                 ),
                 new com.damo.server.domain.transaction.entity.TransactionAmount(
                     SUM(CASE WHEN t.amount.action = 'RECEIVING' THEN t.amount.amount ELSE 0 END), 'RECEIVING'
                 )
           )
           FROM Transaction t
                LEFT JOIN FETCH  Person p ON t.person.id = p.id
           WHERE t.user.id = :userId
           """)
    TransactionTotalAmount findTotalAmount(@Param("userId") final Long userId);

분리로 인해 쿼리가 더러워졌는데 이건 이제 방법을 찾아야할 거 같습니다....