JPA
는 Java ORM
기술의 표준으로 사용되는 인터페이스 모음으로, 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다.
대표적으로는 Hibernate
가 있다.
그런데, ORM
은 대체 무엇일까?
- ORM (Object Relational Mapping)
: 객체와 관계형 데이터베이스를 연결한다는 의미
JPA
는 자바 객체와 관계형 데이터베이스를 연결시 사용하는 방식을 정의한 인터페이스라고 해석할 수 있다!
SQL 문장이 아닌 메서드를 통해 DB를 조작할 수 있게 해준다. 따라서 DB중심의 설계가 아닌, 객체지향적 설계가 가능해진다.
특정 DB에 종속되지 않는다.
- DB별로 조금씩 다른 문법 등을 고려하지 않아도 된다는 장점이 있다.
- DB 종속적인 기능을 많이 이용하는 경우를 고려해 다양한 방언 클래스를 제공한다.
방언이란?
: DBMS마다 조금씩 문법의 차이가 발생하는데 이런 문법적 차이가 발생하는 부분을 방언이라고 한다.
유지보수 및 구조변경에 용이하다.
생산성 향상이 가능하다.
JPA
를 사용하기 위해서는 먼저 Java Project
를 JPA Project
로 변경해야 한다.
Java Project
우클릭 > Configure
> Convert to JPA Project
설정 창에는 Java
와 JPA
두가지가 선택되어 있을 것이다. 여기서 Java
의 버전을 확인한 뒤 바꿔주자.
Java 16
으로 설정되어 있는데 현재 11버전을 이용중이라 바꿔주었다.Details
옆의 Runtimes
를 클릭한 뒤, jre
를 체크한다.
JPA
와 Java
가 잘 체크되어 있는지, 버전은 현재 사용중인 자바 버전과 동일한지 최종 확인한 후 Next
를 두 번 눌러준다.JPA implementation
의 Type
을 Disable Lirary Configuration
으로 바꿔준 뒤 Finish 한다.
설정이 완료 되었다면 Java Project의 src 하단에 META-INF
라는 폴더가 생기며 해당 폴더 안에 persistence.xml
이라는 설정 파일이 생긴다.
해당 파일을 오픈한 뒤 source
를 클릭하면 설정을 변경할 수 있다.
JPA
를 이용하여 테이블을 생성하고 데이터 CRUD 작업을 수행하려면 데이터베이스의 Table과 대응하는 Entity
클래스를 생성한 뒤 EntityManager
설정, 트랜잭션 관리, 비즈니스 로직을 구성해줘야 한다.
여기서 생소한 단어가 여러가지 등장한다.
먼저, Entity
는 무엇을 의미하는 걸까?
데이터베이스의 테이블과 대응하는 하나의 클래스
영속성을 가진 객체로 DB Table에 보관할 대상(혹은 영속 컨텍스트에 보관할 대상)이라고도 한다.
Oracle에서 학습용으로 제공한 Table인 emp
를 예시로 들어보자.
DB에 emp
Table과 Java에 emp Class
가 존재한다고 하면, emp Table
⟷ emp.java Class
의 대응 관계를 가지게 된다.
@Entity
어노테이션을 붙이면 테이블과 Java 클래스가 연결(매핑)이 된다.
예시로 Java에서 Student
테이블을 만들어보자.
@Entity
public class Student {
private int student_no;
private String student_name;
private String grade;
}
DB의 Student
테이블과 매핑될 Java 클래스가 생성되었다.
만약 DB 테이블과 Java 클래스의 이름이 다르면 @Table
어노테이션을 사용하면 된다.
@Entity
@Table(name="Student") //이름을 "Student"로 설정
public class Test {
private int student_no;
private String student_name;
private String grade;
}
Test Class를 새로 생성한 뒤 EntityManager
를 이용하여 DB에 Student Table을 새로 생성해볼 것이다.
멤버변수 초기화 메서드와 @Getter
, @Setter
외의 설정을 추가하여 코드를 마저 작성했다.
@requiredargsconstructor
:@NonNull
이 붙은 필드의 생성자를 추가
@NonNull
:Null
을 허용하지 않을 경우
@Id
: PK ( Primary Key, 기본 키 ) 지정
@GeneratedValue
: 기본키 설정을 DB에 위임하는 방식,@Id
와 함께 사용해야 하며 DB가 자동으로AUTO_INCREMENT
적용함
(strategy = GenerationType.IDENTITY)
: persistence provider
가 DB의 identity
컬럼을 이용하여 자동 생성하겠다는 의미
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Getter
@Setter
@ToString
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int student_no;
@NonNull
private String student_name;
@NonNull
private String grade;
}
Test라는 이름의 클래스를 새로 생성한 뒤 JPA를 사용해서 Student 테이블을 생성해보자.
먼저 DB 작업을 위해 다음과 같은 코드를 작성했다.
public class RunningTest {
public void Test() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MySQLDB");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
tx.commit();
}
}
EntityManagerFactory
, EntityManager
, EntityTransaction
은 무엇일까?
EntityManager
인스턴스를 관리하는 객체
EntityManagerFactory
는 생성 비용이 아주 크기 때문에, 어플리케이션 실행시 하나만 생성한 뒤 공유하여 사용한다.
사용자가 요청시 EntityManagerFactory
가 EntityManager
를 생성한다.
✔️ 어플리케이션 종료시 EntityManagerFactory
도 Close
로 종료해줘야 한다.
코드를 한번 살펴보자.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MySQLDB");
EntityManagerFactory
객체인 emf
를 생성한다. Persistence
는 META-INF
폴더의 persistence.xml
내부의 설정 정보를 조회한다.persistence.xml
에서 조회한 정보를 바탕으로 EntityManagerFactory
를 생성한다.EntityManagerFactory
가 EntityManager
를 생성한다.Entity에 대한 DB 작업을 제공하는 객체
EntityManager
는 내부적으로 DB Connection을 이용하여 DB에 접근한 뒤, EntityManager
를 통해 CRUD 작업이 가능하게 해준다.
= EntityManager
를 통해 Persistence Context(영속성 컨텍스트)
에 접근하여 DB 작업을 제공한다.
어플리케이션 구동시 EntityManagerFactory
를 생성하고 EntityManagerFactory
는 사용자가 요청시 EntityManager
를 생성한다.
DB에 작업이 가능하려면, DB 작업을 제공하는 객체인 EntityManager
객체를 생성해줘야 한다.
다시 코드를 살펴보자.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MySQLDB");
EntityManager em = emf.createEntityManager();
EntityManager
객체 em
이 EntityManagerFactory
객체인 emf
의 emf.createEntityManager()
를 통해 생성된 것을 확인할 수 있다!
이제 트랜젝션을 시작하여 DB에 작업을 요청할 수 있다.
트랜잭션(Transaction) : 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위
트랜젝션은 DB의 상태를 변화시키기 위해 수행하는 작업, 즉. DB 작업을 뜻한다.
따라서 EntityTransaction
은 DB 작업을 시작하기 위해 DB 작업을 제공하는 객체인 EntityManager
로부터 트랜젝션 API를 가져와야 한다.
코드를 다시 살펴보자.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MySQLDB");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
tx.commit();
EntityTransaction
객체인 tx
가 EntityManager
객체인 em
으로부터 트랜젝션을 가져오는 것을 볼 수 있다.
이 흐름을 도식화하면 이런 그림이 되는 것이다.
이제 코드를 실행하여 Student
Table을 생성해보자.
public class RunningTest {
public void Test() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MySQLDB");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
tx.commit();
}
}
코드 실행 후, cmd에서 MySQL에 접속하여 생성된 테이블 목록을 show tables;
로 확인해보았다.
정상적으로 생성된 것을 확인했다!
desc student;
명령어를 통해 Table의 속성을 확인해보자.
Student
클래스에서 생성한 컬럼들이 전부 있는 것을 볼 수 있다.
student_no
에 적용한 PK 설정과 AUTO_INCREMENT
도 적용되어 있다.
이제 Student
클래스에 새로운 데이터를 추가해보자.
학번은 AUTO_INCREMENT
를 적용하여 입력하지 않아도 되므로 [ Louis, 1학년 ] 을 추가할 것이다.
@Test
public void Test() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("dbinfo");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Student student1 = new Student("Louis", "1학년");
em.persist(student1);
tx.commit();
}
DB에 CURD 작업을 수행하려면 .persist()
를 사용하여 영속성 컨텍스트에 데이터를 넣어준다.
이후 commit
이 실행되면서 DB에 Insert한 데이터가 반영되는 것이다.
데이터 추가 로직을 MYSQL 문장으로 입력하면 다음과 같다.
Insert into Student VALUES ('Louis', '1학년');
DB에 데이터가 추가된 것을 확인할 수 있다.
이번에는 "Louis"의 학년을 2학년으로 Update 해보자.
tx.begin();
Student student1 = new Student("Louis", "1학년");
student1.setGrade("2학년");
em.persist(student1);
tx.commit();
"Louis"의 grade
가 2학년으로 변경된 것을 확인할 수 있다.
쿼리 문장을 실행하려면 createQuery
안에 SQL 문장을 입력해야 한다.
일반 SQL 문장이 아닌 JPQL
쿼리문으로 Select 문을 작성하고 Student
클래스에 있는 모든 데이터를 출력해보자.
em.createQuery(jpql, Member.class)
: 해당 메서드의 Member.class
부분에는 조회하려는 데이터의 자료형 클래스를 입력해주면 된다.
객체지향 쿼리 언어로 Table이 아닌 Entity를 대상으로 한다.
일반 쿼리 문장과 JPQL 쿼리 문장은 거의 똑같지만 일부 차이점이 있다.
JPQL 작성 문법에 대해 알아보자! 예시로 Select 문을 작성한다고 하면,
select 별칭 from 테이블명 별칭
위와 같이 작성할 수 있다.
DB의 Table을 대상으로 찾는 것이 아닌 Entity를 대상으로 하기 때문에 별칭이 필수로 들어가며, @Entity
어노테이션이 붙은 테이블 명과 대소문자를 동일하게 작성해주어야 한다.
Student
클래스로 일반 SQL 쿼리문과 비교를 해보자.
//MySQL
select * from student;
//JPQL
select s from Student;
JPQL 문법을 바탕으로 select 문장을 적용시켜보았다.
List<String> studentList = em.createQuery("select s from Student s").getResultList();
System.out.println(studentList);
Student
클래스에 존재하는 데이터인 Louis의 정보가 출력되었다.
em.remove(student1);
해당 문장 실행 후 cmd에서 Student
클래스를 조회하면 다음과 같이 출력된다.
mysql> select * from student;
Empty set (0.00 sec)