Skip to content

B1ABOA/jpa_study

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jpa 공부해보자!!

👩‍💻 팀원 소개

노솔리(🩸AB) 박웅빈(🩸A) 이주원(🩸B) 홍민영(🩸O)
@soljjang777 @Ungbbi @2oo1s @HongMinYeong

Entity

Emp Table

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

Dept Table

Field Type Null Key Default Extra
DEPTNO int NO PRIMARY KEY
DNAME String YES
LOC String YES

예제1

🎁 Dept 엔티티는 다음과 같이 정의되어 있다고 가정합니다:

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

    private String name;

    // Getters and Setters
}

🎁 아래는 Dept 엔티티 클래스를 사용하여 Dept 엔티티의 모든 레코드를 가져오는 JPQL 쿼리의 예입니다:

List<Dept> datas = em.createQuery("select d from Dept d", Dept.class).getResultList();

🎱 문제입니다!:

  1. JPQL 쿼리에서 Dept.class를 사용하여 Dept 타입으로 결과를 가져오는 이유는 무엇인가요?
  2. 위의 JPQL 쿼리를 사용하여 Dept 엔티티의 name 필드가 "Sales"인 모든 Dept 객체를 조회하려면 쿼리를 어떻게 수정해야 하나요?

답안

  1. Dept.class를 사용하는 이유| Dept.class를 사용하는 이유는 createQuery 메서드의 두 번째 인자로 결과 타입을 지정하여 반환된 결과가 Dept 타입으로 캐스팅되도록 하기 위함입니다 -> 이는 타입 안전성을 보장합니다.
  2. name 필드가 "Sales"인 Dept 객체를 조회하는 JPQL 쿼리 수정:
List<Dept> datas = em.createQuery("select d from Dept d where d.name = :name", Dept.class)
                    .setParameter("name", "Sales")
                    .getResultList();

예제2

🎁 아래는 Emp 엔티티 클래스와 Emp 테이블에 있는 데이터 입니다.

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 엔티티가 정상적으로 동작할 수 있게 됩니다.



💥 Issue

  • Import 문은 제외하였습니다.

Dept.java

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<>();

}

Emp.java

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;
}

RunningTest.java

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으로 인해 발생하게 되는 에러입니다.

  1. Emp 객체와 Dept 객체가 있습니다.
  2. Emp 객체는 Dept 객체를 참조합니다 (emp.getDeptno()).
  3. 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와 같은 에러가 발생하게 되는 것입니다.

Solution

두 클래스파일 중 한 곳에 ToString(exclude = )로 순환참조가 발생하게되는 멤버변수를 제외시켜줍시다. 예를 들자면 Dept 클래스에서 해결해주고자 한다면 @ToString(exclude = "emps")
위와 같이 순환 참조가 발생하게 되는 emps를 제외시켜주면 됩니다.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages