Spring Framework/[인프런] Java ORM 표준 프로그래밍 - JPA

Chapter 02. JPA 시작하기

계란💕 2022. 9. 3. 23:33

2.1 프로젝트 생성

 

H2 데이터베이스 설치와 실행

  • H2는 웹 용 Query tool을 제공하는 가벼운 실습용 데이터 베이스이다. 시퀀스, auto increment 기능 지원
  • 데이터베이스 시뮬레이션 기능이 있다. 
  • H2 데이터베이스를 설치한다.    https://www.h2database.com/html/main.html

다운로드 선택

 

  • maven 프로젝트 생성
  • maven - 라이브러리 자동 다운로드 및 의존성 관리
    • 아까 사이트에서 다운받은 2.1.214 버전과 같은 드라이버를 사용해야한다.
<hide/>
<?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>
  <dependencies>
<!--    jpa 하이버네이트-->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>5.3.10.Final</version>
    </dependency>
<!--h2데이터베이스-->
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>2.1.214</version>
    </dependency>
  </dependencies>
<!--  <properties>-->
<!--    <maven.compiler.source>8</maven.compiler.source>-->
<!--    <maven.compiler.target>8</maven.compiler.target>-->
<!--    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
<!--  </properties>-->
</project>

 

  • JPA 설정 -  persistence.xml파일을 만들어서 META-INF에 저장한다.
    • 필수 속성: h2-console에서 사용할 id, password,url이다. hibernate.dialect의 "H2diarect"를 오라클 버전으로 변경할 수도 있다. 그러면 Main클래스를 실행했을 때 오라클 버전으로 쿼리(format_sql)가 출력된다. 
<hide/>
<?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="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
  <property name="javax.persistence.jdbc.user" value="sa"/>            
  <property name="javax.persistence.jdbc.password" value=""/>
  <property name="javax.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>

 

 

 

데이터베이스의 방언(dialect) - SQL 표준을 지키지 않는 특정 데이터베이스의 고유 기능

  • JPA는 특정 데이터베이스에 종속적이지 않다
  • 각각 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다르다.
    • ex)
      • 부분 문자열: substr(오라클), substring(sql 표준),
      • 페이징(limit - MySQL, ROWNUM - Oracle)

 

 

2.2 애플리케이션 개발

 

  • JPA 구동 방식

 

  • Main 클래스
    • 엔티티 매니저 팩토리를 만들면 데이터베이스 연결이 된다.
<hide/>
package hellojpa;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaMain {
    public static void main(String[] args) {
        //
        EntityManagerFactory emf =
            Persistence.createEntityManagerFactory("hello");
    }
}

 

============================ 오류 ============================ 

JdbcSQLNonTransientConnectionException

 

  - 오류: jdbc와 연결할 수 없다는 오류가 난다.

    -> JdbcSQLNonTransientConnectionException

    -> JDBCConnectionException

  - 원인: H2에 연결되지 않아서 오류가 났다. 그런데 H2에 연결되지 않은 이유가 Java가 설치되어 있지 않았기 때문이었다. JDK가 설치되어 있어야 H2도 돌아간다.

<hide/>
C:\Users\Ran\.jdks\corretto-1.8.0_342\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53579,suspend=y,server=n -javaagent:C:\Users\Ran\AppData\Local\JetBrains\IntelliJIdea2022.2\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\charsets.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\access-bridge-64.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\cldrdata.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\dnsns.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\jaccess.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\jfxrt.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\localedata.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\nashorn.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunec.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunjce_provider.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunmscapi.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunpkcs11.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\zipfs.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jce.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jfr.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jfxswt.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jsse.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\management-agent.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\resources.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\rt.jar;C:\Users\Ran\Desktop\R\inflearn\JPA프로그래밍_기본편\새 폴더\jpa-basic\target\classes;C:\Users\Ran\.m2\repository\org\hibernate\hibernate-entitymanager\5.3.10.Final\hibernate-entitymanager-5.3.10.Final.jar;C:\Users\Ran\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\Ran\.m2\repository\org\hibernate\hibernate-core\5.3.10.Final\hibernate-core-5.3.10.Final.jar;C:\Users\Ran\.m2\repository\org\javassist\javassist\3.23.2-GA\javassist-3.23.2-GA.jar;C:\Users\Ran\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\Ran\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\Ran\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Ran\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\Ran\.m2\repository\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;C:\Users\Ran\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.0.4.Final\hibernate-commons-annotations-5.0.4.Final.jar;C:\Users\Ran\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\Ran\.m2\repository\net\bytebuddy\byte-buddy\1.9.5\byte-buddy-1.9.5.jar;C:\Users\Ran\.m2\repository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;C:\Users\Ran\.m2\repository\com\h2database\h2\2.1.214\h2-2.1.214.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\lib\idea_rt.jar" hellojpa.JpaMain
대상 VM에 연결되었습니다, 주소: '127.0.0.1:53579', 전송: '소켓'
9월 03, 2022 1:20:05 오후 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
	name: hello
	...]
9월 03, 2022 1:20:05 오후 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {5.3.10.Final}
9월 03, 2022 1:20:05 오후 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
9월 03, 2022 1:20:05 오후 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
9월 03, 2022 1:20:05 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!)
9월 03, 2022 1:20:05 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [org.h2.Driver] at URL [jdbc:h2:tcp://localhost/~/test]
9월 03, 2022 1:20:05 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {user=sa}
9월 03, 2022 1:20:05 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
9월 03, 2022 1:20:05 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
9월 03, 2022 1:20:08 오후 org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator initiateService
WARN: HHH000342: Could not obtain connection to query metadata : null
9월 03, 2022 1:20:08 오후 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
9월 03, 2022 1:20:08 오후 org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl makeLobCreatorBuilder
INFO: HHH000422: Disabling contextual LOB creation as connection was null
9월 03, 2022 1:20:08 오후 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 90067, SQLState: 90067
9월 03, 2022 1:20:08 오후 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Connection is broken: "java.net.SocketTimeoutException: connect timed out: localhost" [90067-214]
Exception in thread "main" org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:275)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:179)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:119)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935)
	at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
	at hellojpa.JpaMain.main(JpaMain.java:13)
Caused by: org.hibernate.exception.JDBCConnectionException: Error calling Driver#connect
	at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:48)
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
	at org.hibernate.engine.jdbc.connections.internal.BasicConnectionCreator.convertSqlException(BasicConnectionCreator.java:118)
	at org.hibernate.engine.jdbc.connections.internal.DriverConnectionCreator.makeConnection(DriverConnectionCreator.java:41)
	at org.hibernate.engine.jdbc.connections.internal.BasicConnectionCreator.createConnection(BasicConnectionCreator.java:58)
	at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections.addConnections(DriverManagerConnectionProviderImpl.java:363)
	at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections.<init>(DriverManagerConnectionProviderImpl.java:282)
	at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections.<init>(DriverManagerConnectionProviderImpl.java:260)
	at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections$Builder.build(DriverManagerConnectionProviderImpl.java:401)
	at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.buildPool(DriverManagerConnectionProviderImpl.java:112)
	at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.configure(DriverManagerConnectionProviderImpl.java:75)
	at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:100)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:246)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.buildJdbcConnectionAccess(JdbcEnvironmentInitiator.java:145)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:66)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
	at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:94)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
	... 14 more
Caused by: org.h2.jdbc.JdbcSQLNonTransientConnectionException: Connection is broken: "java.net.SocketTimeoutException: connect timed out: localhost" [90067-214]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:678)
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:477)
	at org.h2.message.DbException.get(DbException.java:212)
	at org.h2.engine.SessionRemote.connectServer(SessionRemote.java:437)
	at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:325)
	at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:122)
	at org.h2.Driver.connect(Driver.java:59)
	at org.hibernate.engine.jdbc.connections.internal.DriverConnectionCreator.makeConnection(DriverConnectionCreator.java:38)
	... 29 more
Caused by: java.net.SocketTimeoutException: connect timed out
	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:607)
	at org.h2.util.NetUtils.createSocket(NetUtils.java:135)
	at org.h2.util.NetUtils.createSocket(NetUtils.java:99)
	at org.h2.engine.SessionRemote.initTransfer(SessionRemote.java:130)
	at org.h2.engine.SessionRemote.connectServer(SessionRemote.java:433)
	... 33 more
대상 VM에서 연결 해제되었습니다. 주소: '127.0.0.1:53579', 전송: '소켓'

종료 코드 1(으)로 완료된 프로세스

 

  • cmd창에서 윈도우는 h2.bat를 실행해야 H2가 연결된다. 맥은 H2.sh 실행
  • Member 클래스를 만든다.
<hide/>
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Member {

    @Id
    private Long id;
    private String name;
    
    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;
    }
}

 

  • H2 에 들어가서 위 클래스와 필드를 맞춰서 member 테이블을 새로 만든다.
<hide/>
CREATE TABLE member(
  id bigint NOT NULL,
  name varchar(255),
  primary key(id)
);

 

  • Main 클래스
    • EntityManagerFactory를 애플리케이션 로딩 시점에 딱 하나만 만들어야한다.
    •  em.persist()
<hide/>
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaMain {
    public static void main(String[] args) {

        EntityManagerFactory emf =
            Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        Member member = new Member();
        member.setId(1L);
        member.setName("HELLOa");
        em.persist(member);
        em.close();
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

  Note) 실행 결과 - 대상 VM에서 연결 해제되었습니다.

  • 멤버에 매개변수를 넣어줬는데도 자동으로 연결이 해제된다는 메시지가 나온다.
    • 왜? 
      • JPA는 "트랜잭션"이라는 단위가 중요하다. 데이터를 변경하는 단위는 반드시 트랜잭션 안에서 작업해야한다.
      • 따라서, EntityTransaction을 꼭 써줘야한다.

 

  • EntityTransaction 추가한다.
    • commit() => 바꾼 내용을 반영하고, close() => 자원을 다 쓰고 나면 데이터베이스 커넥션을 반환해야한다.
<hide/>
package hellojpa;
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();

        Member member = new Member();
        member.setId(1L);
        member.setName("HELLOa");

        em.persist(member);
        tx.commit();

        em.close();
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

 

  Note) Main 클래스 실행 결과

  • persistence에 옵션을 정한대로  주석이 표시 되고 그 다음에 sql 포맷이 출력되는 걸 볼 수 있다.
  • 쿼리를 직접 만들지 않았지만 JPA가 알아서 쿼리를 만들고 데이터를 넣어준 것이다.

 

 

  • 위 매인 클래스를 실행한 다음에 H2를 들어가서 멤버 클래스를 조회하면 아래와 같이 데이터베이스에 데이터가 들어간 걸 볼 수 있다.
    • jdbc:h2:tcp://localhost/~/test 
    • 이 부분도 persistence 파일에서 정한대로 ip 주소를 따라가야한다.
    • persistence에 로컬 호스트로 지정해두고 다른 아이피로 들어가지 않도록 해야한다. 
    • 윈도우는 cmd 창에서 h2.bat을 실행해야한다.

 

 

  cf) 만약 클래스의 클래스 / 필드 이름과 데이터베이스의 테이블 / 필드명이 다른 경우에

    - @Table(name=""), @Column(name="")을 이용해서 쌍따옴표 안에 데이터베이스의 테이블 명 또는 필드 명을 적어줘서 매핑할 수 있다.

 

 

 

  Ex) 예외 처리

  • 앞서 작성한 코드는 예외처리가 안되어 있어서 좋은 코드가 아니다. 
  • 중간에 에러가 날 경우에 close() 까지 실행이 되지 않을 것이기 때문이다.
<hide/>
package hellojpa;
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{
            Member member = new Member();
            member.setId(2L);
            member.setName("helloB");
            em.persist(member);
            tx.commit();

        }catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

 

 

 

  Ex) 회원 정보 찾기

<hide/>
package hellojpa;
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{
           Member findMember = em.find(Member.class, 1L);
            System.out.println("find member : " + findMember.getId());
            System.out.println("find name : " + findMember.getName());
            tx.commit();

        }catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

  Note) 실행 결과

    - em.remove()를 이용하면 찾은 멤버를 삭제 가능하다.

 

 

  Ex) 회원 정보 수정

  • 그런데 여기서 setName()을 한 뒤에 member를 다시 저장할 필요가 없다.
  • 왜?
    • 엔티티 매니저는 Java의 Collection과 비슷하다. 객체를 대신 저장해준다.
    • JPA를 통해 엔티티를 가져오면 JPA가 관리하도록 설정된다.
    • 그러면 JPA가 커밋하는 시점에 수정된 사항이 있는지 등을 모두 체크한다.
    • 그럼 커밋하기 직전에 update 쿼리를 날린 다음에 커밋이 된다.
<hide/>
package hellojpa;
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{
           Member findMember = em.find(Member.class, 1L);
//            System.out.println("find member : " + findMember.getId());
//            System.out.println("find name : " + findMember.getName());
            findMember.setName("helloJPA");
            tx.commit();

        }catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

  Note) 실행 결과

  • H2 데이터베이스 내용도 바뀐 걸 볼 수 있다.

 

 

EntityManagerFactory 주의 사항

  • 엔티티 매니저 팩토리는 하나만 만들어서 애플리케이션 전체에서 공유한다.
  • 앤티티매니저는 스레드 간에  공유하지 않고 쓰고 버려야 한다. - 데이터베이스의 Connection 과 비슷
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행된다. 단순 조회는 상관 없다.

 

 

JPQL(Java Persistence Query Language, 객체 지향 쿼리)이란?

  • 가장 단순한 조회 방법이다.
  • JPA에서도 쿼리를 쓸 수 있다. 
  • EnityManager.find()
  • 객체 그래프 탐색 ex) a.getB()
  • 객체를 대상으로 하는 객체 지향 쿼리이다.
  • JPA를 사용하면 엔티티 객체를 중심으로 개발한다.
  • 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색한다.
  • 모든 DB 데이터를 객체로 변환해서 검색하는 것을 불가능하다
  • 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요하다.
  • 그래서 JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.
  • ex) SELECT FROM, WHERE, GROUP BY ..HAVING, JOIN 지원
  • JPQL엔티티 객체 대상, SQL은 데이터베이스 테이블을 대상으로 쿼리

 

 

  Ex) JPQL - createrQuery()

  • JPA에서는 코드를 짤 때 테이블을 대상으로 짜지 않는다.
  • FROM 뒤에 테이블이 아닌 객체(클래스)명이 와야한다.
<hide/>
package hellojpa;
import java.util.List;
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{
//           Member findMember = em.find(Member.class, 1L);
            List<Member> result = em.createQuery("SELECT m FROM Member AS m", Member.class)
                .getResultList();
            for (Member member  : result) {
                System.out.println("member = " + member.getName());
            }
            tx.commit();

        }catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

  Note) 실행 결과

  • 주석 안에는 createQuery() 안에 작성한대로 JPQL 쿼리가 그대로 들어가있다. -  멤버 엔티티를 선택한 것이다.
  • 실제 SQL문을 보면 필드를 모두 나열했다.

 

 

  Ex) pagination - LIMIT &OFFSET 

  • 몇 번 부터, 몇 개 가져올 것인지 setFirstResult(), setMaxResults()로 표현 가능하고 get()으로 가져올 수 있다. 
<hide/>
package hellojpa;
import java.util.List;
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{
//           Member findMember = em.find(Member.class, 1L);
            List<Member> result = em.createQuery("SELECT m FROM Member AS m", Member.class)
                .setFirstResult(5)
                .setMaxResults(8)
                .getResultList();
            for (Member member  : result) {
                System.out.println("member = " + member.getName());
            }
            tx.commit();

        }catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();    // 팩토리를 나중에 닫는다.
    }
}

  Note) 실행 결과

  • 쿼리를 짜면 properties의 sql 속성(H2 dialect 인지  Oracle dialect인지)에 따라, 각 sql에 맞는 쿼리문을 작성해준다.
  • 현재 결과는 H2 dialect로 설정된 상태이다.

 

 

 

 

출처 - https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com