User마다 한개씩만 생성되어야 하는 엔티티들이 있다.
기존에는 기본키를 따로 두고 user를 FK로 두는 방식으로 진행했다.
그렇지만 기본키를 많이 안쓰고 findByUserId 식의 메서드를 많이 사용한다면 컬럼 방비라고 생각하여 전략을 변경해보게 되었다.
기존 방식은 다음과 같다.
@OneToOne + id + user_id
@Entity
@Table(name = "user_notification_settings")
class UserNotificationSetting(
// 자체 PK
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "notification_setting_id")
val id: Long? = null,
// user FK
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false, unique = true)
@OnDelete(action = OnDeleteAction.CASCADE)
val user: User,
// ...
)
@MapsId를 사용해서 개선하고자 한다.
부모 - User
@Entity
@Table(name = "users")
class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
val id: Long? = null,
// ...
)
자식 - UserNotificationSetting
@Id 필드에서 자동으로 채우는 방식이다.
@Entity
@Table(name = "user_notification_settings")
class UserNotificationSetting(
//
@MapsId
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
val user: User,
@Id
@Column(name = "user_id")
val userId: Long? = null,
// ...
)
저장 방법은 다음과 같다.
// UserAccountRegistrar.kt
// user를 먼저 저장
val user = userRepository.save(buildUser())
// MapsId가 user.id를 읽어서 user_notification_settings.user_id에 자동 세팅
notificationSettingRepository.save(
UserNotificationSetting(user = user)
)
// @MapsId가 user.id를 읽어서 user_notification_settings.user_id에
자동 세팅
결론적으로 @MapsId 방식에서는 컬럼이 하나다.
user_id가 PK면서 동시에 FK이기 때문이다.
일대일 매핑을 할 때 무지성 중간테이블 기본키 + 외래키 해서 추가 컬럼을 만들기보다는 이런 식으로 하면 컬럼 낭비를 줄일 수 있을 것 같다.