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)
- ex)
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
'Spring Framework > [인프런] Java ORM 표준 프로그래밍 - JPA' 카테고리의 다른 글
Chapter 06. 다양한 연관관계 매핑 (0) | 2022.09.05 |
---|---|
Chapter 05. 연관 관계 매핑 기초 (0) | 2022.09.05 |
Chapter 04. 엔티티 매핑 (entity mapping) (0) | 2022.09.04 |
Chapter 03. 영속성 관리 - 내부 동작 방식 (0) | 2022.09.03 |
Chapter 01. JPA 소개 (2) | 2022.09.02 |