노솔리(🩸AB) | 박웅빈(🩸A) | 이주원(🩸B) | 홍민영(🩸O) |
---|---|---|---|
@soljjang777 | @Ungbbi | @2oo1s | @HongMinYeong |
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
EMPNO | int | NO | PRIMARY KEY | ||
COMM | int | YES | |||
ENAME | String | YES | |||
JOB | String | YES | |||
HIERDATE | String | YES | |||
SAL | int | YES | |||
MGR | int | YES | |||
DEPTNO | int | YES | FOREIGN KEY |
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
DEPTNO | int | NO | PRIMARY KEY | ||
DNAME | String | YES | |||
LOC | String | YES |
@Entity
public class Dept {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
}
List<Dept> datas = em.createQuery("select d from Dept d", Dept.class).getResultList();
- JPQL 쿼리에서
Dept.class
를 사용하여Dept
타입으로 결과를 가져오는 이유는 무엇인가요? - 위의 JPQL 쿼리를 사용하여
Dept
엔티티의name
필드가 "Sales"인 모든Dept
객체를 조회하려면 쿼리를 어떻게 수정해야 하나요?
- Dept.class를 사용하는 이유| Dept.class를 사용하는 이유는 createQuery 메서드의 두 번째 인자로 결과 타입을 지정하여 반환된 결과가 Dept 타입으로 캐스팅되도록 하기 위함입니다 -> 이는 타입 안전성을 보장합니다.
- name 필드가 "Sales"인 Dept 객체를 조회하는 JPQL 쿼리 수정:
List<Dept> datas = em.createQuery("select d from Dept d where d.name = :name", Dept.class)
.setParameter("name", "Sales")
.getResultList();
package model.domain.entity;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Getter
@Setter
//@ToString
@Table(name = "emp")
@Entity
public class Emp {
@Id
@Column(name = "empno")
private long empno;
@NonNull
private String ename;
@NonNull
private String job;
@NonNull
private int mgr;
@NonNull
private Date hiredate;
@NonNull
private int sal;
private int comm;
@OneToOne
@JoinColumn(name="deptno")
private Dept deptno;
}
현재 Dept객체로 Select시 위와 같은 에러가 발생하는데 왜 나는것일까요??
emp 테이블의 mgr와 sal 컬럼에 null 값이 포함된 것을 확인할 수 있습니다. 현재 Emp 엔티티를 생성할 때 mgr과 sal의 데이터 타입을 int로 설정하였는데, int 타입은 null 값을 허용하지 않습니다.
따라서, 만약 데이터베이스에서 mgr이나 sal이 null인 경우, 엔티티에서 이를 처리할 수 없게 됩니다.
이를 해결하기 위해 mgr과 sal의 데이터 타입을 Integer로 변경해야 합니다. Integer는 null 값을 허용하는 래퍼 클래스이므로,
데이터베이스에서 null 값을 포함할 수 있는 컬럼을 적절히 처리할 수 있습니다. 이 변경을 통해 null 값이 있을 경우에도 Emp 엔티티가 정상적으로 동작할 수 있게 됩니다.
- Import 문은 제외하였습니다.
package model.domain.entity;
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Getter
@Setter
@ToString
@Table(name = "dept")
@Entity
public class Dept {
@Id
@Column(name = "deptno")
private long deptno;
@NonNull
private String dname;
@NonNull
private String loc;
@OneToMany(mappedBy = "deptno")
private List<Emp> emps = new ArrayList<>();
}
package model.domain.entity;
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Getter
@Setter
@ToString
@Table(name = "emp")
@Entity
public class Emp {
@Id
@Column(name = "empno")
private long empno;
@NonNull
private String ename;
@NonNull
private String job;
@NonNull
private int mgr;
@NonNull
private Date hiredate;
@NonNull
private int sal;
private int comm;
@OneToOne
@JoinColumn(name="deptno")
private Dept deptno;
}
public class RunningTest {
@Test
public void stpe01Test() {
EntityManager em = DBUtil.getEntityManager();
Emp emp = em.find(Emp.class, 7782L);
System.out.println("사원 아이디가 7782 사람 : " + emp.getEname());
System.out.println("사원 아이디가 7782 사람 : " + emp.getEname() + " / 부서명 : " + emp.getDeptno().getDeptno());
System.out.println("사원 아이디가 7782 사람 : " + emp.getDeptno());
Dept dept = em.find(Dept.class, 10L);
System.out.println("부서 아이디가 10인 부사 : " + dept.getDname());
List<Emp> emps = dept.getEmps();
emps.forEach(System.out::println);
em = null;
}
}
RunningTest.java 에서 System.out.println 으로 Deptno를 출력하고자 하면 Stackoverflow error가 발생합니다. 왜일까요?
순환 참조
원인 : ToString으로 인해 발생하게 되는 에러입니다.
- Emp 객체와 Dept 객체가 있습니다.
- Emp 객체는 Dept 객체를 참조합니다 (emp.getDeptno()).
- Dept 객체도 여러 Emp 객체들을 리스트로 참조합니다 (dept.getEmps()).
즉, Emp 객체가 Dept 객체를 포함하고, Dept 객체가 다시 여러 Emp 객체들을 포함하는 구조입니다.
emp.toString()이 호출되면 Dept 객체의 toString()이 호출되고, 이 Dept 객체의 toString()은 다시 그 안에 포함된 여러 Emp 객체들의 toString()을 호출하게 됩니다.
쉽게 표현하자면
emp.toString() -> dept.toString() -> emps.toString() -> 다시 emp.toString()으로 무한히 순환하면서 호출됩니다.
이로 인해 StackOverflowError와 같은 에러가 발생하게 되는 것입니다.
두 클래스파일 중 한 곳에 ToString(exclude = )로 순환참조가 발생하게되는 멤버변수를 제외시켜줍시다. 예를 들자면 Dept 클래스에서 해결해주고자 한다면 @ToString(exclude = "emps")
위와 같이 순환 참조가 발생하게 되는 emps를 제외시켜주면 됩니다.