본문 바로가기
spring/spring jpa

[Spring] JPA 연관 관계 매핑 및 제거

by moonsiri 2021. 6. 18.
728x90
반응형

user와 dept 테이블을 맵핑하는 user_dept 테이블이 존재할 때, Entity의 객체 연관관계를 맵핑하여 영속 상태의 entity를 만들어 보겠습니다.

 

@Entity
@Table(name = "user")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserEntity {

    @Id
    @Column(name = "user_id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @Column(length = 50, nullable = false)
    private String name;

    @Column(length = 50, nullable = false)
    private String lastname;

    private int age;

    @Column(name = "email_address", length = 200)
    private String emailAddress;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "userEntity", cascade = CascadeType.ALL)
    private List<UserDeptEntity> userDeptList = new ArrayList<>();
}
@Entity
@Table(name = "dept")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class DeptEntity {

	@Id
	@Column(name = "dept_cd", nullable = false)
	private String deptCd;

	@Column(name = "dept_nm", nullable = false)
	private String deptNm;

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "deptEntity", cascade = CascadeType.ALL)
	private List<UserDeptEntity> userDeptList = new ArrayList<>();

}
@Entity
@Table(name = "user_dept", uniqueConstraints = {
	@UniqueConstraint(columnNames = {"user_id", "dept_cd"})
})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserDeptEntity {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@ManyToOne
	@JoinColumn(name = "user_id")
	private UserEntity userEntity;

	@ManyToOne
	@JoinColumn(name = "dept_cd")
	private DeptEntity deptEntity;

	@Builder
	public UserDeptEntity(UserEntity userEntity, DeptEntity deptEntity) {
		this.userEntity = userEntity;
		this.deptEntity = deptEntity;
	}
}

 

userEntity를 조회하면 연관되는 데이터가 모두 조회됩니다.

원하는 데이터를 조회하기 위해선 아래 코드처럼 Projection을 사용하여 원하는 값을 조회할 수 있습니다.

@RequiredArgsConstructor
public class UserRepositoryCustomImpl implements UserRepositoryCustom {

	private final JPAQueryFactory jpaQueryFactory;

	private QUserEntity userEntity = QUserEntity.userEntity;
	private QUserDeptEntity userDeptEntity = QUserDeptEntity.userDeptEntity;

	@Override
	public List<UserDeptDTO.DetailRes> findAllByDeptCd(UserDeptDTO.GetListReq param) {
		return jpaQueryFactory.select(Projections.constructor(UserDeptDTO.DetailRes.class,
			userEntity.userId, userEntity.name, userEntity.age, userDeptEntity.deptEntity.deptNm))
		                      .from(userEntity)
		                      .leftJoin(userDeptEntity)
		                      .on(userEntity.userId.eq(userDeptEntity.deptEntity.userId)
		                                           .and(userDeptEntity.deptEntity.deptCd.eq(param.getDeptCd())))
		                      .orderBy(userEntity.userId.asc())
		                      .offset(param.getLimitOffSet())
		                      .limit(param.getRecordCountPerPage())
		                      .fetch();
	}
}
public class UserDeptDTO {

	@Getter @Setter
	public static class GetListReq extends PagingCommonVO {
		private String deptCd;
	}

	@Getter @Setter
	@NoArgsConstructor
	public static class DetailRes {
		private long userId;
		private String name;
		private int age;
		private String deptNm;

		public DetailRes(long userId, String name, int age, String deptNm) {
			this.userId = userId;
			this.name = name;
			this.age = age;
			this.deptNm = deptNm;
		}
	}
}

 

 

Entity의 영속성 상태가 변경되었을 때 이를 연관된 Entity에도 전파시킬 때는 cascade를 사용합니다.

(예. dept 데이터 삭제를 하면 user_dept 테이블의 데이터도 자동으로 삭제시킬 때 cascade = CascadeType.REMOVE)

 

 

Cascade 옵션

  • PERSIST
    • 엔티티를 영속화할 때 연관된 엔티티도 함께 유지 (* PERSIST의 예상치 못한 동작)
    • 연관 엔티티가 DB에 이미 저장이 되어있어도 다시 persist 하기 때문에 detached entity passed to persist Exception이 발생함(이경우에는 CascadeType.MERGE를 사용)
  • MERGE
    • 엔티티 상태를 병합할 때, 연관된 엔티티도 함께 병합
    • 트랜잭션이 종료되고 detach 상태에서 엔티티가 merge()를 수행하게 되면 연관 엔티티의 추가 및 수정사항도 함께 적용됨
  • REFRESH
    • 엔티티를 새로 고칠 때, 연관된 엔티티도 새로고침
  • REMOVE
    • 엔티티를 삭제할 때, 연관된 엔티도 함께 삭제
  • DETACH
    • 부모 엔티티가 detach()를 수행하게 되면, 연관된 엔티티도 detach() 상태가 되어 변경사항이 반영되지 않음
  • ALL
    • 모든 Cascade 적용

 

 

 

[Reference]

https://www.baeldung.com/jpa-cascade-types

728x90
반응형

댓글