관련된 글
- [JAVA] JPA(Java Persistence API)란?(1)
- [JAVA] JPA 기본설정 및 객체생성(2)
- [JAVA] JPA 어노테이션 및 트랜잭션(3)
- [JAVA] JPA 연관관계(4)
- [JAVA] JPA 영속성 컨텍스트(5)
이전 시간에 Member와 Club객체를 생성했습니다. 이 두 객체를 이용해서 JPA에 대해 좀더 알아 보겠습니다.
실습은 TEST Code로 진행할 수 있지만 h2-console을 활용하기 위해서 main applicaion에서 진행하였습니다.
main Apllication
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
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jongmin");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
// 트랜잭션 시작
transaction.begin();
try{
Member member = new Member();
member.setName("jongmin");
member.setAge(30);
entityManager.persist(member); // 영구저장
transaction.commit(); // 커밋
}catch (Exception e){
transaction.rollback();
}finally {
entityManager.close();
}
entityManagerFactory.close();
}
}
- EntityManagerFactory를 이용하여 persistence.xml에서 지정한 설정을 가지고 옵니다.
이 전에 지정한 persistence.xml설정에서
<persistence-unit name="jongmin">
에 지정한 name으로 설정을 가지고 옵니다. - EntityManager를 생성하고 Transaction을 얻습니다.
- 트랜잭션을 시작하고 Member객체를 만들어 값을 넣은 후 persist를 통해서 값을 넣어줍니다.
- commit()해주어 값을 물리적으로 데이터베이스에 넣어주고 close해줍니다.(에러시 rollback)
EntityManagerFactory는 애플리케이션 전체에서 하나만 생성하여 공유해야합니다. EntityManagerFactory를 생성할 때마다 DB와의 연결이 발생함으로 성능상에 문제가 발생할 수 있기 때문입니다.
(DB와의 연결을 해주는 인터페이스라고 생각합니다.)
EntityManager는 EntityManagerFactory의 구현체입니다. EntityManager를 통해서 DB와 데이터를 주고 받을 수 있습니다. 모든 Transaction이 끝나면 close()를 사용해서 자원을 해제합니다.
주의사항
- 모든 변화(update, delete, save)는 Transaction안에서 일어나야 합니다.
- EntityManagerFactory는 애플리케이션 전체에 하나만 생성하여 공유해야 합니다.
- Transaction이 끝나면 close()를 통해서 자원을 해제시켜야 합니다.
- EntityManager는 쓰레드간에 공유해서는 안된다.(사용하고 버린다.)
참고
persist()를 수행했다고 데이터베이스에 값이 완전히 들어가는 것이 아닙니다!!
commit()을 해야 물리적으로 데이터페이스에 들어가며 이부분은 뒤에서 다루겠습니다.
main 메소드를 수행한 후 결과를 확인해 보겠습니다. 먼저 console창 입니다.
console
다른 특별한 작업을 하지 않았음에도 불구하고 create table
을 통해서 테이블을 생성하는 것을 알 수 있습니다. 이것이 persistence.xml에 <property name="hibernate.hbm2ddl.auto" value="create" />
를 지정했기 때문입니다.
여기서는 캡쳐하지 않았지만 테이블을 생성하기 전에 drop table Member if exists
을 먼저 수행하는 것을 콘솔에서 확인하실 수 있을 것입니다. 그 다음 persist를 통해서 데이터베이스에 값을 넣어주어 insert쿼리가 자동으로 날라가는 것을 볼 수 있습니다.
h2-console에 접속하여 Member 객체를 확인해보면 값이 성공적으로 들어간 것을 볼 수 있습니다. 여기서 ID는 값을 따로 넣어주지 않았지만 1값이 들어가 있습니다. 저희가 Member객체를 만들때 @GeneratedValue
를 지정했기 때문에 H2데이터베이스에서 자체적으로 만들어 주었기 때문입니다.
이제 추가적인 어노테이션에대해 알아보겠습니다.
@Column
가장 많이 사용하는 어노테이션입니다.
- name : 데이터베이스의 Column이름 지정
- insertable, updateble : 읽기 전용여부
- nullable : null 허용 여부, DDL 생성시 사용
- unique : 유니크 제약조건, DDL 생성시 사용
@Enumerated
열거형 매핑으로 Enum 타입과 관련하여 매핑해줍니다.
- EnumType.STRING : String 타입으로 열거형 이름그대로 저장해줍니다.
- EnumType.ORDINAL : 순서를 저장해줍니다.(default)
- 실제로 사용할 때는 EnumType.STRING을 사용해 줍시다.
Enum Type이 추가되서 순서가 변경되면 ORDINAL이 무의미 해지기 때문에 STRING을 사용을 권장.
@Lob
컨텐츠의 길이가 길경우 사용합니다.(CLOB, BLOB)
- CLOB : String, char[], java.sql.CLOB
- BLOB : byte[], java.sql.BLOB
@Transient
Transient어노테이션이 추가된 필드는 매핑하지 않습니다. 데이터베이스에서는 사용하지 않지만 Class에 추가하고 싶은 경우 사용합니다.
@Temporal
시간에 관련된 어노테이션 입니다.
- Date(java.sql.Date)
- Time(java.sql.Time)
- TimeStamp(java.sql.TimeStamp)
그러나 LocalDateTime이 있으므로 잘 사용하지 않게 되는 어노테이션입니다.
@Embedded
그 다음 @Embeddable, @Embedded, @AttributeOverrides, @AttributeOverride 등이 있습니다. 이 4개의 어노테이션은 한번에 살펴보겠습니다.(아래의 예시가 적절하지 않을 수도 있습니다…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int age;
@Enumerated(value = EnumType.STRING)
private RoleType roleType;
private String friendName;
private String friendPhone;
}
Member 객체가 위와 같이 구성되어 있다고 하겠습니다.(Getter, Setter는 Lombok을 이용해서 처리했습니다.)
아래의 friend(친구)와 관련된 컬럼이 있다고 할 때 이 컬럼을 따로 관리하고 싶습니다. 테이블에서는 분리하지 않고 객체(클래스)만을 분리해서 사용하고 싶을 때 위의 어노테이션들을 사용할 수 있습니다.
먼저 분리시킬 Friend클래스를 생성해줍니다.
1
2
3
4
5
6
7
8
@Embeddable
@Getter @Setter
public class Friend {
private String name;
private String phoneNumber;
}
분리시킨 Friend클래스에 @Embeddable어노테이션을 추가해줍니다. 그 다음 Member클래스를 바꿔 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int age;
@Enumerated(value = EnumType.STRING)
private RoleType roleType;
@Embedded
private Friend friend;
}
위와 같이 @Embedded 어노테이션을 사용하여 분리시킨 Friend클래스를 불러올 수 있습니다.
이대로 사용할 수 있지만 에러가 발생합니다. 그 이유는 name컬럼이 중복되기 때문입니다.
기존에는 friendName을 사용했지만 분리시키면서 name으로 변경 시켰습니다. 이 경우 @AttributeOverrides을 사용하여 분리한 클래스의 매핑명을 지정해 줄 수 있습니다.
1
2
3
4
5
6
@Embedded
@AttributeOverrides(
@AttributeOverride( name = "name", column = @Column(name = "friendNsame")),
@AttributeOverride( name = "phoneNumber", column = @Column(name = "friendPhsne"))
)
private Friend friends
다음과 같이 지정하므로써 매핑할 컬럼명을 바꿔줄 수 있습니다.
여기까지 Transaction과 기타 어노테이션에 대해 알아봤습니다.
다음 시간에는 가장 중요한 연관관계에 대해 알아보겠습니다.
전체 코드는 Github에서 확인해보실수 있습니다.
참고
- objectdb (https://www.objectdb.com/java/jpa/persistence/overview)
- Baeldung (https://www.baeldung.com/hibernate-date-time)
- SKplanet Academy (https://www.youtube.com/channel/UCtV98yyffjUORQRGTuLHomw)