Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바의 정석 기초편 ch3
- 게시글 목록 api
- 2024 정보처리기사 시나공 필기
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch11
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch7
- 스프링 mvc1 - 서블릿
- 스프링 고급 - 스프링 aop
- @Aspect
- 스프링 db2 - 데이터 접근 기술
- 자바의 정석 기초편 ch9
- 자바의 정석 기초편 ch14
- 자바의 정석 기초편 ch5
- 스프링 db1 - 스프링과 문제 해결
- 코드로 시작하는 자바 첫걸음
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch4
- 자바의 정석 기초편 ch6
- 스프링 입문(무료)
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch8
- jpa - 객체지향 쿼리 언어
- 스프링 mvc2 - 타임리프
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch1
- 자바 기본편 - 다형성
- 스프링 mvc2 - 로그인 처리
Archives
- Today
- Total
나구리의 개발공부기록
JPA 소개, SQL 중심적인 개발의 문제점, JPA 소개, JPA 시작하기, 프로젝트 생성, 애플리케이션 개발 본문
인프런 - 스프링부트와 JPA실무 로드맵/자바 ORM 표준 JPA 프로그래밍 - 기본편
JPA 소개, SQL 중심적인 개발의 문제점, JPA 소개, JPA 시작하기, 프로젝트 생성, 애플리케이션 개발
소소한나구리 2024. 10. 4. 13:44출처 : 인프런 - 자바 ORM 표준 JPA 프로그래밍 - 기본편(유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. SQL 중심적인 개발의 문제점 및 소개
- 스프링 DB 2편 - JPA강의 내용과 중복, https://nagul2.tistory.com/316
2. 프로젝트 생성 - HelloJPA
** 참고
- 실무에서는 프로젝트를 띄울 때 스프링과 통합해서 사용하다보니(스프링부트로 프로젝트를 생성) 스프링이 버전관리를 해주지 않는 라이브러리들의 버전은 사용할 스프링부트 버전의 doc에서 Dependency Versions에 들어가서 버전을 맞춰서 설치
- Dependency Versions -> Managed Dependency Coordinates에 들어가면 확인할 수 있음
- https://spring.io/projects/spring-boot#learn
1) Spring 통합 없이 순수하게 JPA만 세팅하기
- 2019년 강의로 이번 실습은 maven으로 실습하나 현재 추세는 gradle로 프로젝트를 생성함
- 의존성이 많아지면 가독성이 떨어지는 xml로 작성하는 maven에 비해 gradle은 그루비라는 프로그래밍 언어로 작성하여 가독성이 좋고 구동 속도도 조금 빠르다고 함
- 실무에서는 maven 프로젝트가 아직 많이 남아있어서 둘다 다룰줄 알면 도움이 됨
(1) 프로젝트 생성 - pom.xml 설정
- 강의에서 제공하는 프로젝트로 진행
- java 17
- maven 1.1.0
- hibernate 6.4.2
- jakarta.xml.binding api 4.0.1 -> 기존 javax 패키지를 jakarta로 변경(오라클과의 자바 라이선스 문제)
- h2database 2.2.224 -> 사이트에서 다운로드 진행
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jpa-basic</groupId>
<artifactId>ex1-hello-jpa</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- JPA 하이버네이트 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.2.Final</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
<!-- logback -->
<!--
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
-->
</dependencies>
</project>
(2) JPA설정 - persistence.xml
- ~/main/resources/META-INF/persistence.xml에 위치해야함
- persistence-unit name 으로 이름을 지정
- jakarta.persistence로 시작하는 옵션
- JPA 표준 속성(driver, user, password, url 정보 입력)
- hibernate로 시작하는 옵션: 하이버네이트 전용 속성
- show_sql: 실행시 JPA가 작성한 SQL 보여주기
- hdm2ddl.auto value="create": 실행할 때마다 테이블을 새로 생성
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="jakarta.persistence.jdbc.user" value="sa"/>
<property name="jakarta.persistence.jdbc.password" value=""/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>
2) 데이터베이스 방언
- JPA는 특정 데이터베이스에 종속적이지 않게 설계되어 있는데, 각각의 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다른 것을 JPA 입장에서는 방언이라고 표현함
- 방언은 SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능이라고 보면 됨
- 가변문자: MySQL - VARCHAR, Oracle - VARCHAR2
- 문자열 자르는 함수: SQL 표준 - SUBSTRING(), Oracle - SUBSTR()
- 페이징: MySQL - LIMIT, Oracle - ROWNUM
- 등등..
- 이렇게 각각의 DB(약 40가지의 실무에서 사용하는 DB)마다 다른 방언을 JPA가 매핑을 해두었기 때문에 hibernate.dialect 옵션에 입력만 해주면 어떤 DB를 사용하더라도 JPA를 사용할 수 있음
- 아래처럼 설정의 DB Dialect 정보만 변경해주면 됨(IDE의 자동 완성 도움을 받을 수 있음)
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <!-- H2 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> <!-- 오라클 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <!-- MySQL -->
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> <!-- PostgreSQL -->
등등
3. 애플리케이션 개발 - HelloJPA
1) JPA 구동방식
- Persistence 클래스에서 persistence.xml에 작성한 설정 정보를 조회
- EntityManagerFactory라는 클래스를 생성
- 이후에 필요할 때마다 EntityManager를 생성해서 사용
2) 코드 작성
- 다운받은 h2 데이터베이스를 실행 후(mac의 경우 ~H2/bin/h2.sh 파일) 작성했던 persistence.xml의 속성정보와 일치하도록 입력 후 연결하면 접속됨
(1) JpaMain
package hellojpa;
public class JpaMain {
public static void main(String[] args) {
// EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager 생성
EntityManager em = emf.createEntityManager();
em.close();
emf.close();
}
}
(2) 실행 결과
- JpaMain의 실행 결과가 아래처럼 보이면 정상적으로 동작이 된 것
(2) Member - Entity 생성
- 자바 클래스로 table을 생성하는 SQL을 작성한 것처럼 table이 생성
- name은 varchar(255), id는 bigint, 프라이머리키는 id로 적용
package hellojpa;
@Entity
public class Member {
@Id
private Long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
(3) 저장해보기 - JpaMain 코드 추가
- JPA에서 transaction이라는 단위가 매우 중요한데, 모든 데이터를 변경하는 작업은 transaction 안에서 해야하므로 트랜잭션을 얻고 시작후 실제 DB에 반영하기 위해서는 작업 후에 commit or rollback을꼭 해줘야 함
- 성공하면 commit, 실패하면 rollback, 작업 후에는 EntityManager를 종료하도록 하는 것이 정석적인 JPA 사용법이나 스프링과 통합하여 사용하면 이러한 코드들을 전부 생략해서 간편히 작성할 수 있음
package hellojpa;
public class JpaMain {
public static void main(String[] args) {
// ... 기존 코드 생략
// 트랜잭션 얻고 시작
EntityTransaction tx = em.getTransaction();
tx.begin();
// 트랜잭션 얻고 시작
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
em.persist(member); // 저장
tx.commit(); // 커밋 - 실제 db에 반영
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
}
(4) 조회, 수정, 삭제
- 조회: em.find로 id값이 1인 Member 데이터를 조회 -> 조회는 commit 필요 없음
- 삭제: 조회된 회원을 em.remove()에 아규먼트 값으로 입력후 commit 하면 회원데이터를 삭제
- 수정: 조회된 회원에 변경하고자 하는 컬럼의 값을 수정후 commit하면 DB에 반영됨
- 마치 자바 컬렉션을 이용하는 것처럼 개발자가 코드를 사용하여 DB의 데이터를 CRUD 할 수 있음
- 특히 수정의 경우에는 저장에 했던 것처럼 persist()를 해야할 것 같지만 JPA가 동작할 때 내부에서 변경이 일어났는지, 트랜잭션이 신규인지 등의 여부를 체크를 하고 이에 맞춰서 변경이 일어났으면 UPDATE 쿼리를 만들어서 날림
// ... 코드 생략
try {
/* 단건 조회 */
Member findMember = em.find(Member.class, 1L);
System.out.println("findMember.getId() = " + findMember.getId());
System.out.println("findMember.getName() = " + findMember.getName());
/* 삭제 */
em.remove(findMember);
tx.commit();
/* 수정 */
Member findMember = em.find(Member.class, 1L);
findMember.setName("HelloJPA");
tx.commit();
} catch (Exception e) {
// ... 코드 생략
(5) 주의
- EntityManagerFactory는 하나만 생성해서 애플리케이션 전체에서 공유함
- EntityManager는 쓰레드간에 공유하면 안됨(사용하고 버려야 함)
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행됨
3) JPQL 소개
- JPA를 사용하면 엔터티 객체를 중심으로 개발하는데 검색 쿼리에 대한 부분의 문제를 JPQL로 해결
- 테이블이 아닌 Entity를 대상으로 작성하는 쿼리 -> SQL을 추상화한 객체 지향 쿼리라고 보면 됨
- DB에 종속적인 SQL을 날리는 것이 아니기 때문에 만약 페이지네이션을 적용해두었다면 DB가 바뀌더라도 라이브러리 방언세팅을 바뀐 DB로 설정하면 해당 DB에서 사용하는 SQL 쿼리가 날라감
- 해당 쿼리에 대한 내용은 뒤에서 자세하게 다룸
/* JPQL 페이지네이션 예시*/
List<Member> result = em.createQuery("select m from Member m", Member.class)
.setFirstResult(4) // index번호 4번째 결과부터
.setMaxResults(8) // 8개 출력
.getResultList();