[JPA] JPA 연관관계 매핑하기

 

 

JPA를 이용하여 기본 객체와 관계형 데이터베이스를 엔티티로 매핑시켰다면 각 엔티티간의 연관관계를 매핑 시켜야 한다.

 

2021.08.31 - [Spring-Boot/JPA] - [JPA] JPA 엔티티 매핑과(Entity Mapping) 엔티티 설계

 

[JPA] JPA 엔티티 매핑과(Entity Mapping) 엔티티 설계

[JPA] JPA 엔티티 매핑(Entity Mapping) 엔티티 설계 JPA에서 가장 기본적이지만 중요한 두 가지 개념을 뽑아보자면 영속성 컨텍스트 객체와 관계형 데이터베이스(RDB) 매핑 이 두가지가 아닐까 한다

dbsyys.tistory.com

 

객체와 테이블을 매핑시켜 엔티티로 사용할 수 있지만 객체와 테이블이 연관관계를 맺는 형식에 차이가 있기때문에 우리는 둘 중에 어느 쪽으로 모델링을 해야한다.

 

객체와 테이블의 연관관계 형식을 보자면

 

 

(객체 연관관계)

 

 

(테이블 연관관계)

 

차례로 객체 연관관계와 테이블 연관관계를 다이어그램으로 나타냈다. 회원과 주문은 1:N 관계를 가지고 있다. 회원의 아이디 값과 주문의 아이디 값으로 서로 연관관계가 정의 되어있다. 

 

언뜻보면 맞는 관계인것 같지만 위의 다이어그램에 객체 연관관계에는 문제가 있다. 테이블은 연관관계를 지정할 때 Join을 이용하여 연관관계를 지정한다. 회원 테이블의 외래키(ORDER_ID)로 주문 테이블의 기본키(ORDER_ID)를 가질 수 있는 것이다. 이렇게 되면 테이블간의 연관관계가 성립된다.

 

하지만 객체 연관관계에서는 Join을 이용 할 수 없다. 회원의 주문아이디(orderId) 값과 주문의 아이디(id) 값은 따로 존재한다.

 

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id; // 회원 id

    private String Mname;

    private Long orderId; // 주문 id

}

 

이렇게 되면 회원 객체의 값이 변경 되었을 때 주문 객체의 값 또한 변경되어야 하고 그 반대의 경우도 마찬가지이다. 이건 객체지향적이라고 할 수 없다. 하지만 객체는 참조를 이용하여 연관관계를 지정할 수 있다.

 

 

(올바른 객체 연관관계)

 

회원 객체는 주문객체의 아이디를 따로 생성하는 것이 아니라 주문 객체를 하나의 필드로 참조하는 것이다. 이렇게 하면 객체 지향적으로 모델링을 할 수 있다.

 

하지만 테이블과 객체의 연관관계 매핑 방식의 큰 차이가 있으므로 JPA를 이용하여 객체 연관관계와 테이블 연관관계의 연관관계를 매핑할 것이다. 


 

연관관계 매핑에 있어서 연관관계는 3가지의 큰 특징을 가진다.

 

  • 방향(Direction) : 단방향, 양방향
  • 다중성(Multiplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:N)
  • 연관관계의 주인(Owner) : 객체의 양방향 연관관계 시 연관관계 주인이 필요

 

위에서 계속 언급했듯 객체와 테이블은 관계를 맺는차이가 있다. 객체의 연관관계 매핑에 방향성이 있는 이유는 

- 객체 연관관계

  • 회원 -> 주문 연관관계 (단방향)
  • 주문 -> 회원 연관관계 (단방향)

- 테이블 연관관계

  • 회원 <-> 주문 연관관계 (양방향)

의 차이가 있기 때문이다. 객체의 양방향 관계는 객체의 서로 다른 단방향 관계가 각각 있는 것이다. 객체를 단방향 연관관계만 지정해도 연관관계 매핑은 완료된 것이나 양방향 매핑을 하게되면 연관관계의 주인으로 지정하지 않은 반대 방향으로 조회가 가능하다(객체 그래프 탐색, *역 방향은 조회만 가능하다)

 

아래는 양방향 연관관계 매핑을 한것이다.

 

 

(객체의 양방향 연관관계)

 

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String Mname;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<Order>();

}
@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    private String OName;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;   

}

 

@ManyToOne

  • 다대일(N:1) 관계를 매핑할 때 사용한다. (@JoinColumn과 함께 사용)

@OneToMany

  • 일대다(1:N) 관계를 매핑할 때 사용한다. mappedBy 라는 속성을 통해 다(N)객체와 연관관계를 매핑한다.

@JoinColumn

  • @ManyToOne과 함께 사용하며 name 속성을 사용하여 일(1)객체와 연관관계를 매핑한다.

 

객체의 연관관계를 양방향으로 설정했다면 연관관계의 주인(Owner)을 정해야한다. 연관관계 주인은 객체의 두 관계 중 하나를 연관관계의 주인으로 지정하고 연관관계의 주인만이 외래 키를 관리(등록, 수정) 할 수 있다. 주인이 아닌 역방향은 조회(읽기)만 가능하다. 실제로 사용하다보면 역방향으로 탐색할 일이 많기 때문에 양방향 매핑을 사용한다.

 

위에서 본 속성들 중에서 주인은 mappedBy 속성을 사용하지 않고 주인이 아닌 속성에 mappedBy 속성을 지정한다. 연관관계의 주인은 외래 키(FK)의 위치를 기준으로 정해야 한다. 양방향 연관관계에서는 양 쪽에 값을 설정하고 연관관계에 관련된 편의 메소드를 생성하는 것을 권장한다. 또한 양방향 매핑 시에 무한루프에 빠질 수 있다는것을 주의하자.

 

 

[JPA] JPA 엔티티 매핑(Entity Mapping) 엔티티 설계

 

JPA에서 가장 기본적이지만 중요한 두 가지 개념을 뽑아보자면

 

  • 영속성 컨텍스트
  • 객체와 관계형 데이터베이스(RDB) 매핑

 

이 두가지가 아닐까 한다.

 

 

영속성 컨텍스트에 관련된 내용은 아래 링크에 정리해보았다.

 

2021.08.19 - [Spring-Boot/JPA] - [JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context)

 

[JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context)

[JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context) 지난번에는 JPA와 관련하여 기본적인 세팅과 EntitiyManagerFactory, EntitiyManager, EntitiyTransaction의 특징과 어떤 역할을 하..

dbsyys.tistory.com


 

이번엔 객체와 테이블 매핑을 할 때 사용하게되는 속성들을 정리해보겠다.

 

먼저 엔티티 매핑이란 객체와 테이블 사이의 관계와 속성을 지정할 수 있도록 매핑하는 것이다.

 

관련된 어노테이션을 사용해서 테이블과 클래스나 필드와 속성 등을 매핑할 수 있다.

 

@Entity
@Table(name = "TBMember")
public class Member {

    @Id
    @GeneratedValue
    private Long id;
    
    @Column(name = "name")
    private String username; 
 	
    @Enumerated(EnumType.STRING) 
    private RoleType roleType; 
 	
    @Temporal(TemporalType.TIMESTAMP) 
    private Date createdDate; 
 	
    @Lob 
    private String description;

}

@Entity

  • 해당 어노테이션이 붙은 클래스는 JPA에 관리되어지며 엔티티라고 한다. JPA를 사용하기 위해선 필수적으로 사용해야한다. (*기본 생성자 필수)

@Table

  • 엔티티와 매핑할 테이블을 지정한다. name 속성을 통해 매핑할 테이블 이름을 지정 할 수있다. ex) 엔티티 이름을 Order이라고 한다면 Oracle에선 order by 예약어와 중복되어 테이블 명으로 설정 할 수 없기때문에 변경 해야함.

@Id

  • 해당 필드를 기본 키로 매핑한다. (직접 할당 하는 방식)

@GeneratedValue

  • 해당 필드를 기본 키로 매핑한다. (자동 생성 하는 방식, @Id 와 같이 사용)
  • strategy 속성을 이용하여 생성되는 타입을 정할 수 있다. default 값은 Auto이며 sql 방언에 따라 자동 지정된다.

@Column

  • 필드와 컬럼의 매핑 속성을 지정한다. 컬럼 이름이나 변경 여부, null 여부, unique, 길이 등의 제약조건을 설정할 수 있다.

@Enumerated

  • 자바의 enum 타입을 매핑할 때 사용한다.
  • 속성타입으로 ORDINAL과 STRING이 있는데 꼭 STRING을 사용하길 권장한다. ORDINAL은 enum 순서를 DB에 저장하고 STRING은 이름으로 값을 저장하는데 enum 타입이 변경 되었을 시 ORDINAL은 순서로 저장하기 때문에 데이터가 꼬일 수 있다.

@Temporal

  • 날짜 타입을 매핑 할 때 사용한다. 속성 타입으로 DATE(날짜), TIME(시간), TIMESTAMP(날짜와 시간) 등 이 있다.
  • Date 객체를 쓴 이유는 하이버네이트가 최신버전이라면 LocalDate나 LocalDateTime 객체를 사용 시 해당 어노테이션을 생략 할 수있다.

@Lob

  • 데이터베이스의 BLOB, CLOB 타입과 매핑할 때 사용한다. (속성은 없으며 문자타입은 CLOB이고  나머지는 BLOB이다.)
  • CLOB: String, char[] / BLOB: byte[]

 

JPA는 데이터베이스의 스키마(DDL)를 애플리케이션 실행 시점에 자동으로 생성할 수 있다. 기존의 테이블 중심의 설계에서 객체 중심의 설계로 바꾸는 것이다. 각 데이터베이스의 특징이 있는 방언별로 적절한 DDL을 생성한다. 하지만 이렇게 생성한 DDL은 개발 서버에서만 사용하거나 초기 세팅 시에만 사용할 것을 권장한다. 운영서버에서는 적절히 수정한 후에 사용해야 한다.

 

데이터베이스 스키마를 자동생성은 설정파일인 프로퍼티(application.properties)나 야믈(application.yml)에서 설정 하면 된다.

 

* application.properties

spring.jpa.hibernate.ddl-auto=create

 

* application.yml

jpa:
  hibernate:
    ddl-auto: create

 

해당 설정파일 create부분에 들어갈 속성들로 5가지가 있다.

 

  • create : 기존 테이블 삭제 후 다시 생성(DROP 후 CREATE)
  • create-drop : create와 같으나 종료 시점에 모든 테이블 DROP
  • update : 변경분만 반영
  • validate : 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none : 사용 안함

 

위에서 언급했듯이 운영서버에는 create, create-drop, update는 사용하지 않을것을 권장한다.

 

- 개발 초기 : create 또는 update

- 테스트 서버 : update 또는 validate

- 스테이징 서버나 운영 서버 : validate 또는 none

 

[JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context)

 

지난번에는 JPA와 관련하여 기본적인 세팅과 EntitiyManagerFactory, EntitiyManager, EntitiyTransaction의 특징과 어떤 역할을 하는지 알아봤고 이번에는 본격적으로 JPA를 사용해보려고 한다.

 

 

2021.08.17 - [Spring-Boot/JPA] - [JPA] JPA? JPA 세팅 및 시작하기

 

[JPA] JPA? JPA 세팅 및 시작하기

[JPA] JPA? JPA 세팅 및 시작하기 기존에 MyBatis 방식만을 사용하다가 ORM(Object-Relation Mapping)기반인 JPA(Java Persistence Interface) 프로그래밍에 관심이 생겨서 알아보게 되었다. 자바 퍼시스턴스 API..

dbsyys.tistory.com

 

 

JPA에는 영속성 컨텍스트(Persistence Context) 라는 용어가 등장한다. 영속성 컨텍스트란 엔티티를 영구 저장하는 환경을 의미한다. 관계형 데이터베이스(RDB)는 데이터나 데이터의 집합을 테이블에 저장, 관리하며  객체지향 프로그래밍에서는 그 테이블을 엔티티라고한다.

 

영속성 컨텍스트는 논리적인 개념이며, 우리가 앞에서 선언했던 EntitiyManager를 통해서 영속성 컨텍스트에 접근이 가능하다. EntityManager에게 관리되는 엔티티는 생명주기를 가지게 되는데 영속성 컨텍스트를 기준으로 아래의 상태를 가진다.

- 비영속(new/transient)

  • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태

- 영속(managed)

  • 영속성 컨텍스트에 관리되는 상태

- 준영속(detached)

  • 영속성 컨텍스트에 저장되었다가 분리된 상태

- 삭제(removed)

  • 삭제된 상태

 

 

각각의 상태가 영속성 컨텍스트에서 어떻게 관리되는지 알아보겠다.


import javax.persistence.Entity; 
import javax.persistence.Id; 

@Entity 
@Getter 
@Setter
public class Member { 
	
	@Id 
	private String id; 
	private String name; 
 
}

Member 엔티티를 생성했다.

 

public class JpaTest {
    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("memberId");
            member.setName("memberName");
			
            // 객체를 저장한 상태(영속)
            em.persist(member);
            
            // 엔티티를 영속성 컨텍스트에서 분리(준영속)
            em.detach(member);
            
            // 객체를 삭제한 상태(삭제)
            em.remove(member);
            
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();

    }
}

 

엔티티 생명주기별 각각의 상태.

 

JPA에서는 영속성 컨텍스트에 관하여 각각의 상태를 오가며, 영속성 컨텍스트에 관리되는 영속 상태인 엔티티에서 저장, 수정, 조회, 삭제 처리한다.

 

그렇다면 왜 ? 굳이

 

비영속, 영속, 준영속, 삭제의 4가지 상태로 구분되며, 영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에서 데이터를 어떻게 관리하고 처리하는지에 대해 알아보겠다.

 


 

영속성 컨텍스트의 특징으로 아래 항목이 있다.

  • 1차 캐시
  • 동일성(identity)
  • 트랜잭션을 지원하는 쓰기 지연(transactional write-begind)
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

 

- 1차 캐시

1차 캐시는 엔티티가 영속성 컨텍스트에 저장되어 영속 상태로 된다면 key와 value 형태로 1차 캐시라는 곳에 저장이 된다. key는 엔티티에서 @Id로 지정한 pk값 (=memberId), value는 해당 엔티티 자체가 된다(=Member).

Member member = new Member();
member.setId("memberId");
member.setName("memberName");

// 영속 상태 (1차 캐시에 저장됨)
em.persist(member);

//1차 캐시에서 조회
Member findMember = em.find(Member.class, "memberId");

member 객체가 1차 캐시에 저장된 상태에서 JPA는 member 객체를 조회하면 데이터베이스에 접근해서 데이터를 가져오는 것이 아닌 1차 캐시를 먼저 확인 한 후에 해당하는 데이터가 없다면 그 때 데이터베이스를 조회한다.

 

만약 memberId2라는 키를 가진 데이터가 데이터베이스에선 존재하고 영속성 컨텍스트의 1차 캐시 안에는 없다고 가정하고 조회를 해보면

Member findMember2 = em.find(Member.class, "memberId2");

영속성 컨텍스트는 데이터베이스에서 해당하는 데이터를 가져온 후에 그 값을 1차 캐시에 저장하고 반환한다.

 

 

- 동일성(identity)

JPA는 같은 트랜잭션 안에서 1차 캐시를 통해 데이터베이스가 아닌 애플리케이션 차원으로 영속된 엔티티의 동일성을 보장해준다.

Member member1 = em.find(Member.class, "memberId");
Member member2 = em.find(Member.class, "memberId");

System.out.println(member1 == member2); // true

 

 

- 트랜잭션을 지원하는 쓰기 지연(transactional write-begind)

JPA는 영속성컨텍스트의 트랜잭션안에서 데이터가 변경된다. member1과 member2를 차례대로 저장 할 때 1차 캐시에 엔티티의 정보가 저장됨과 동시에 저장하는 insert 쿼리가 생성되지만 영속성 컨텍스트 안에만 저장되고 데이터베이스로 쿼리가 전송되지 않는다.

EntityManager em = emf.createEntityManager();

EntityTransaction transaction = em.getTransaction();

// 데이터 변경 시
transaction.begin(); // 트랜잭션 시작

em.persist(member1);
em.persist(member2);
// 영속성 컨텍스트에 등록 관리 -> query를 보내지 않음

// 커밋하는 순간 데이터베이스에 query를 보냄
transaction.commit(); // 트랜잭션 커밋

영속성 컨텍스트에 저장 되어있는 쿼리는 트랜잭션에서 commit()이라는 메서드가 실행되어야 비로소 데이터베이스로 저장 되어있던 쿼리가 보내진다.

 

 

 

- 변경 감지(Dirty Checking)

영속성 컨텍스트는 최초의 데이터 조회나 저장할 때 1차 캐시에 @Id로 지정한 pk값과 엔티티말고도 엔티티에 관련된 스냅샷을 저장한다. 스냅샷에는 최초 데이터가 조회 또는 저장된 시간이나 그 시점의 데이터가 저장되는데,  JPA에서 트랜잭션이 commit() 하는 시점에 엔티티와 스냅샷을 비교한다.

EntityManager em = emf.createEntityManager();

EntityTransaction transaction = em.getTransaction();

transaction.begin(); // 트랜잭션 시작

// 데이터 조회(영속)
Member member1 = em.find(Member.class, "memberId");

// 영속된 엔티티 데이터 수정
member1.setId("memberIdTest");
member1.setName("memberNameTest");

transaction.commit(); // 트랜잭션 커밋

비교 후에 기존 값과 스냅샷의 데이터가 다르면 영속성 컨텍스트에 update 쿼리를 만들어 놓게되고 flush라는 작업을 거치면서 데이터베이스에 반영되고 데이터베이스 commit가 실행된다. 이러한 매커니즘으로 데이터 수정 시에는 저장(persist), 조회(find), 삭제(remove)와 같은 처리가 따로 필요없다.

 

*flush

플러시는 영속성 컨텍스트에 변경된 내용을 데이터베이스에 반영(동기화)하는 것이다. 플러시가 발생하면 변경감지와 쓰기 지연 SQL 저장소에 수정된 엔티티가 등록되고 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 등록한다.(저장, 수정, 삭제)

 

플러시 후에 데이터베이스 트랜잭션 커밋이 일어나는 것은 아니다. 데이터베이스 트랜잭션 커밋은 JPA의 트랜잭션에서 commit()을 실행했을 때 일어난다.

 

flush를 수행했을 시 쓰기 지연 SQL저장소에 있거나 변경감지로 인해 만들어진 쿼리들이 데이터베이스에 반영이 되지만 1차 캐시는 그대로 남아있고 사라지지 않는다.


'JPA' 카테고리의 다른 글

[JPA] JPA 엔티티 매핑과(Entity Mapping) 엔티티 설계  (1) 2021.08.31
[JPA] JPA? JPA 세팅 및 시작하기  (1) 2021.08.17

[JPA] JPA? JPA 세팅 및 시작하기

 

 

기존에 MyBatis 방식만을 사용하다가 ORM(Object-Relation Mapping)기반인 JPA(Java Persistence Interface) 프로그래밍에 관심이 생겨서 알아보게 되었다.

 

자바 퍼시스턴스 API또는 자바 지속성 API(Java Persistence API, JPA)는 자바 플랫폼 SE와 자바 플랫폼 EE를 사용하는 응용프로그램에서 관계형 데이터베이스의 관리를 표현하는 자바 API이다.

기존에 EJB에서 제공되던 엔터티 빈(Entity Bean)을 대체하는 기술이다. 자바 퍼시스턴스 API는 JSR 220에서 정의된 EJB 3.0 스펙의 일부로 정의가 되어 있지만 EJB 컨테이너에 의존하지 않으며 EJB, 웹 모듈 및 Java SE 클라이언트에서 모두 사용이 가능하다. 또한, 사용자가 원하는 퍼시스턴스 프로바이더 구현체를 선택해서 사용할 수 있다.

 

 

MyBatis과 JPA의 특징

-MyBatis

  • 동적인 쿼리를 사용하기 유연하다.
  • 새로운 패러다임(ex. JPA) 없이 Sql을 그대로 이용할 수 있다.
  • 프로그래밍 코드와 sql을 분리하여 작업 할 수 있다.

-JPA

  • CRUD 시 반복적인 Sql쿼리 사용을 줄여준다
  • 테이블을 엔티티로 관리하여 객체지향적으로 데이터를 관리할 수 있다.
  • 스키마 변경 시 엔티티 변경만으로 관련 테이블쿼리 변경이 가능하다.
  • 동일한 트랜잭션에서 엔티티의 동일성을 보장한다.

 

추후에는 Spring Data JPA 를 쓰겠지만 JPA의 고유의 특징들을 알기위해 기본적인 설정부터 했다.

 

	<!-- JPA 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.29.Final</version>
        </dependency>
        
	<!-- H2 데이터베이스 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>

프로젝트의 의존성 추가를 위해 pom.xml에 위 항목을 추가한다. 테스트를 위한 데이터베이스로 H2 데이터베이스를 사용했다. 프로젝트나 사용하려는 데이터베이스를 맞게 설정하면 된다.

 

 

<?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="xxx">
        <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>

'src - main - resources - META-INF - persistence.xml'

위의 경로에 해당 xml파일을 생성하고 <persistence-unit name="xxx"> 태그에 이름을 등록한다. 해당 태그에 들어갈 이름은 추후에 EntityManagerFactory을 생성 시에 매핑될 이름이다.

 

 

import javax.persistence.EntityManagerFactory;

public class JpaTest {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("XXX");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
        
        	// 실행할 코드 //

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
}

 

 

-EntityManagerFactory

  • 데이터베이스와의 커넥션 등과 같은 역할을 하며 보통 하나만 생성하여 애플리케이션 전체에서 공유하며 사용한다. 그리고 persistence.xml에서 지정한 name을 통해 연결한다.

 

-EntityManager

  • 특정 작업을 위해 단위별로 데이터베이스에 접근하는 것을 관리한다. EntityManagerFactory의 경우는 Thread-Safe하지만 EntityManager는 쓰레드간의 공유하면 안되며 사용하고 버려야한다.

 

-EntityTransaction

  • JPA의 모든 데이터 변경은 트랜잭션 안에서 수행되어야 한다. 데이터의 변경이 발생했을 때, 문제 없이 성공했다면 commit을 하고 반대로 문제가 발생 했을 시 rollback을 실행한다.

 

[Spring Boot] Spring Initializr를 이용하여 프로젝트 생성하기(Intellij)

 

 

기존에 STS(Spring Tool Suite)나 Eclipse 툴 안에서 Maven이나 Gradle로 프로젝트를 생성했던 경험이 있는데 Spring Initializr 웹 도구를 사용해서 스프링 프로젝트를 만들 수 있다.

(개인적으로 이 방법이 더 편한것 같다.)

 

https://start.spring.io/

 

해당 사이트에 접속하게되면

 

 

이런 화면이 나오는데 차례대로 설정할 부분이 있다.

 

- project

Maven인지 Gradle인지 선택한다. 해당 항목은 프로젝트의 의존성을 관리한다. 과거에는 Maven을 많이 사용했지만 Maven은 xml 태그 형식이라 의존이 많아질수록 코드가 길어지기도 하고 요새는 Gradle을 많이 쓰는것 같아 Gradle로 설정했다.

 

 

- Language

Spring 프레임워크를 통한 Java 웹 개발을 할 것이기 때문에 Java 를 선택한다. Kotlin은 안드로이드 앱 개발에 사용하는 언어이며 Groovy는 Java 에 Python, Ruby, Smalltalk 등의 특징을 더한 동적 객체 지향 프로그래밍 언어이다.

 

(나중에 접해볼 수 있는 기회가 있으면 보면 좋을것 같다.)

 

 

- SpringBoot

해당 프로젝트의 버전을 설정한다. SNAPSHOT이 붙은 건 현재 개발중인 버전이며, M 또한 정상화하여 정식 릴리즈 된 버전이 아니기 때문에 버전만 명시된것을 선택한다.

 

 

- Project Metadata

해당 프로젝트의 이름과 특징을 입력하면 된다. 보통 Group과 Artifact/Name 을 .으로 연결한 값이 패키지이름이 되며 Artifact를 입력하면 Name도 같은 내용으로 등록된다. (개인 프로젝트라면 자유롭게 등록하면 될 듯하다.)

 

Pakaging에는 Jar와 War가 있는데

 

Jar는 자바 어플리케이션이 동작할 수 있도록 프로젝트를 압축한 파일(Class, Java리소스, 라이브러리 등)이며 폴더 구조 등이 자유롭고 JRE로 실행가능하다.

 

War는 서블릿이나 jsp에 배치 할 수 있는 웹 어플리케이션 압축 파일(servlet, jsp, jar, class, xml, html 등)이며 정형화 된 구조를 사용(WEB-INF, META-INF 등)하고 실행 하려면 WEB서버(Apache)나 WAS서버(Tomcat) 등이 필요하다.

 

...

 

현재 회사에서는 프로젝트를 할 때 운영서버에 웹서버, WAS 서버를 설치하고 리소스 파일을 옮기고 난 후, WAS 서버를 리부팅하는 형식으로 작업을 하고있다.

 

하지만 스프링부트는 내장 서버를 제공하고있고 서버 기동 시, 콘솔 창에서 해당 프로젝트 파일을 빌드하여 서버를 부팅시킬 수 있기때문에 Jar을 선택하자.

(빌드 하는 방법도 추후에 정리해야겠다.)

 

 

JAVA 버전은 이클립스를 쓰면서 원래 8버전을 사용했지만 개발자들 사이에서 핫하다는 인텔리제이를 접하게 되면서 11 버전을 사용하게 되었다.

 

(아직 무료버전 사용중.......)

 

추후 이직하게되거나 본격적으로 툴을 사용하게 된다면 정식 버전을 사용할것 같다.

 

 

- Dependencies

ADD DEPENDENCIES를 클릭하여 생성할 프로젝트의 의존성을 추가한다. 추후에 maven이나 gradle로 추가 할 수 있다.

* Spring Web

  • 스프링을 이용하여 웹개발을 하려면 필요한 의존성이다. MVC패턴을 사용하여 RESTful을 포함한 웹 개발이 가능하며 WEB서버 WAS를 기본 포함 컨테이너로 사용한다.

 

* Lombok

  • 어노테이션을 사용하여 반복되는 상용구 코드를 줄일 수 있다. (ex. getter, setter, constructor 등) 여러가지 기능이 있겠지만 일단 엔티티 설계할때 코드를 많이 줄일 수 있을것 같다.

 

* Thymeleaf

  • 자바 템플릿엔진으로써 스프링에서 지향하는 템플릿엔진이다. 기존 jsp보다 속도가 빠르고 확장자 명이 .jsp가 아닌 .html이기 때문에 다른 WAS서버 없이 브라우저에서 정적으로 표기가 가능하다. 

 

Spring Data JPA

  • JPA를 통해서 DB에 데이터를 관리, 유지하는데 기존 JPA의 기능을 좀 더 효율적으로 사용할 수 있다. 아직 사용 안해봤지만 추후에 자주 이용하게 될것 같다.

 

이제 Generate를 클릭하면 zip파일이 생성되고 해당 파일의 압축을 풀면 아래와 같은 파일이 나온다..

 

 

툴은 인텔리제이를 사용한다.

 

https://www.jetbrains.com/ko-kr/idea/download

 

다운로드 IntelliJ IDEA: 우수성과 인체 공학이 담긴 JetBrains Java IDE

 

www.jetbrains.com

초기화면

(프로젝트를 빌드하기전에....)

 

메뉴에서 File - settings - 검색: plugin

 

화면에서는 이미 install했지만 추후 JPA와 Lombok을 사용할거니까 marketplace에서 lombok과 JPA BUddy를 검색하여 install한다.

 

시작 메뉴의 File - Open에서 프로젝트를 지정한 경로의 "build.gradle" 파일을 선택한다.

 

 

....짓는중....

빌드가 성공적으로 완료되면 해당경로에 있는 클래스안의 메인메서드를 실행하면 서버가 올라간다.

 

끝 !


인줄 알았는데

********************************************* 추가 **********************************************

프로젝트 파일을 Generate할 때 Spring Data JPA를 dependency에 추가했는데 JPA를 사용 할 데이터베이스를 설정해주지 않았기 때문에 하위의 오류가 발생한다.

 

"

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

 

"


{해당 프로젝트 디렉토리} - build.gradle 클릭

dependencies {
//	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

해당 디펜던시를 주석처리 한다.

 

 

추가로 인텔리제이에서 설정해야할 사항이 있다. (안 맞으면 오류가 뜨는경우가 있음...)


Gradle

메뉴에서 File - settings - 검색: gradle

 

1) Settings에서 Download external annotations for dependencies를 체크하고 Build and run suing과 Run tests using을 Gradle -> IntelliJ IDEA로 변경한다.

 

2) Gradle JVM을 환경변수에 등록한 경로의 JDK버전을 프로젝트와 맞는 버전으로 등록한다.

 


성공 !

 

진짜 끝 !

+ Recent posts