https://mjoo1106.tistory.com/entry/JAVA-JPA-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%98%88%EC%A0%9C
앞서 JPA의 기본 개념과 예제를 통해 간략하게 실습하는 시간을 가져보았다. 이번 시간에는 거기에 조금 더 나아가고자 한다.
데이터베이스 설계
국민 예제로 유명한 수강신청 시스템 DB를 간략하게 설계해보자.
진짜 데이터베이스 설계는 아니고 이런 형을 쓸 것이다 그리고 이런 관계가 있다 정도로만 참고하면 될 것이다. 이를 바탕으로 테이블을 jpa를 통해 create해보고 join을 통해 조회까지 해보고자 한다.
코드 작성
1. 프로젝트 생성
2. 초기 세팅
build.gradle
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation 'mysql:mysql-connector-java:8.0.28'
implementation 'org.hibernate:hibernate-entitymanager:5.3.10.Final'
implementation 'javax.xml.bind:jaxb-api:2.3.0'
}
dependencies 파일을 위와 같이 작성한다. 참고로 mysql로 프로젝트를 진행하기 때문에 3 번째 줄은 자신이 사용하는 db 플랫폼에 따라 다르게 설정해야 한다.
src > main > META-INF > persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="hello">
<class>jpaenroll.domain.Enroll</class>
<class>jpaenroll.domain.Student</class>
<class>jpaenroll.domain.Subject</class>
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="08520130"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpaschool?serverTimezone=UTC&characterEncoding=UTF-8"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<!-- 쿼리 보여줘 -->
<property name="hibernate.show_sql" value="true"/>
<!-- 쿼리 예쁘게 포맷해줘 -->
<property name="hibernate.format_sql" value="true"/>
<!-- insert 등으로 왜 쿼리가 발생했는지 -->
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
참고로 META-INF와 persistence.xml은 직접 만들어 주어야 한다. 여기서 마지막 줄을 주목해야 한다. hibernate.hbm2ddl.auto value="create"라고 작성되어 있는 부분은 기존의 데이터베이스가 있다면 날려버리고 새로 하나 만드는 옵션이다. 만약 jpa가 종료되는 시점에 만든 db를 없애고 싶다면 create-drop, 새로 추가된 부분만 업데이트 해주는 update 옵션이 있다. 초기에는 table을 만들기 위해 create로 지정했다.
그리고 password 설정 밑에 jpaschool의 경우 db의 스키마로 mysql에서는 create database jpaschool; 로 생성 후 사용 가능하다.
3. Domain
앞서 설계한 db를 참고하여 각각을 객체로 매핑하려고 한다. 그러므로 위 양식에 맞춰 하나씩 만들어주자!
Student
package jpaenroll.domain;
import javax.persistence.*;
@Entity
public class Student {
@Id
@Column(name="student_id")
private Long id;
private int grade;
// enum은 기본적으로 숫자인데, 이를 string 그대로 사용한다는 뜻임
@Enumerated(EnumType.STRING)
private KindOfMajor major;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public KindOfMajor getMajor() {
return major;
}
public void setMajor(KindOfMajor major) {
this.major = major;
}
}
enum을 정의하기 위해 KindOfMajor enum을 하나 생성해야 한다. (domain 안에 KindOfMajor class를 생성해주세요!)
package jpaenroll.domain;
public enum KindOfMajor {
Math, Computer, Science
}
Subject
package jpaenroll.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Subject {
@Id
@Column(name="subject_id")
private Long id;
private String name;
private int grade;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
Enroll
package jpaenroll.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.LocalDateTime;
@Entity
public class Enroll {
@Id @GeneratedValue
@Column(name="enrollment_id")
private Long id;
@Column(name="student_id")
private Long studentid;
@Column(name="subject_id")
private Long subjectid;
private LocalDateTime enrolldate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getStudentid() {
return studentid;
}
public void setStudentid(Long studentid) {
this.studentid = studentid;
}
public Long getSubjectid() {
return subjectid;
}
public void setSubjectid(Long subjectid) {
this.subjectid = subjectid;
}
public LocalDateTime getEnrolldate() {
return enrolldate;
}
public void setEnrolldate(LocalDateTime enrolldate) {
this.enrolldate = enrolldate;
}
}
lombok이 마렵지만 참아야한다. 기초부터 차근차근 가보자. 이렇게 작성해주면 앞서 작성한 db 설계에 딱맞다. 근데 여기서 @Id, @GeneratedValue, @Column 이런 것을 사용했는데 하나씩 간단하게 설명하고자 한다.
@Id는 PK를 알려주는 것이다!
@GeneratedValue는 값을 자동으로 생성해서 넣어주는 것이다. 위 코드처럼 옵션을 사용하지 않으면 AUTO INCREMENT와 동일한 효과를 얻을 수 있다. 즉, 입력하지 않아도 자동으로 1씩 증가한 숫자가 기본 키에 할당된다.
@Column은 실제 테이블의 column name과 내가 사용하고자하는 객체 이름이 다를 때 사용한다. 만약 student_unique_id 라는 column이 있다고 생각하면 이를 매번 자바 코드에 작성하기 힘들다. 그래서 나는 id라고만 쓰고 위에 @Column을 통해 수동으로 매핑해주는 것이다.
4. 테이블 생성
앞서 create 옵션을 사용했으므로 기본 main 코드만 작성해서 테이블이 생성되는지 테스트 해보자.
package jpaenroll;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args){
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em=emf.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
try {
tx.commit();
} catch (Exception e){
tx.rollback();
} finally{
em.close();
}
emf.close();
}
}
성공적으로 create 된 것을 확인할 수 있다. 이제 임의로 값을 insert 후 join 문을 진행해보고자 한다.
5. INSERT
INSERT 하기 전에 아까 persistence.xml의 마지막 줄에서 create -> none 으로 반드시 변경해야한다.
try {
Subject subject=new Subject();
subject.setId(1101L);
subject.setName("JPA_BASIC");
subject.setGrade(3);
Student student=new Student();
student.setId(160164L);
student.setGrade(4);
student.setMajor(KindOfMajor.Computer);
em.persist(subject);
em.persist(student);
tx.commit();
}
위 코드는 JpaMain의 try 구문 안에 넣어주면 된다. 우선 subject와 student 정보를 하나씩 입력했다.
try {
Enroll enroll = new Enroll();
enroll.setStudentid(em.find(Student.class, 160164L).getId());
enroll.setSubjectid(em.find(Subject.class,1101L).getId());
enroll.setEnrolldate(LocalDateTime.now());
em.persist(enroll);
tx.commit();
}
위 코드 또한 JpaMain의 try 구문 안에 넣어주면 된다. 앞서 삽입된 정보들을 찾아 db에 삽입하도록 설계했다.
이제 조인문을 활용하여 진행해보자.
6. JOIN
Q. 수강신청에 성공한 사람의 전공은 무엇일까?
try {
Enroll enroll = em.find(Enroll.class, 1L);
Student student = em.find(Student.class, enroll.getStudentid());
System.out.println(student.getMajor());
tx.commit();
}
우선 find를 사용하여 enroll의 첫 번째 항목을 불러와 이를 활용하여 한 번 더 find 함수를 써서 전공을 찾는다.
그런데, 이는 JPA의 객체 지향 보다는 기존의 RDB 조회와 다를 것이 없다. 객체 그래프를 활용하고 있다는 느낌이 없다. (객체 그래프 : 이어진 객체들 사이의 관계)
그럼 어떻게 해결해야할까?
간단하게 Enroll Class에 private Student student; 를 하나 추가해준다. (현재는 table을 새롭게 create하지 않았기 때문에 참고만 해주세요!)
그 후 아래와 같은 코드를 작성한다.
try {
Enroll enroll = em.find(Enroll.class, 1L);
Student student=enroll.getStudent();
System.out.println(student.getMajor());
tx.commit();
}
두 코드를 비교해보면 어떤 것이 조금 더 객체 지향이 가까운지 바로 느껴질 것이다.
JPA는 진짜 신기하다. 하면서도 놀라는 경우가 많다. 앞으로 JPA 전문가가 되는 그 날까지 쭉~~ 진행해야겠다.
'SPRING' 카테고리의 다른 글
[Spring] JWT 실습 - 1 (2) | 2022.07.27 |
---|---|
[JAVA] JPA 연관관계 매핑 (0) | 2022.07.25 |
[JAVA] JPA 개념 및 예제 (0) | 2022.07.18 |
[Spring] JPA를 활용한 로그인 구현 (2) | 2022.07.15 |
[Spring] JPA를 활용한 회원가입 구현 (0) | 2022.07.14 |