JPA, hibernate 스터디

5 minute read

JPA 장단점

장점

  • 생산성
    Hibernate는 SQL를 직접 사용하지 않고, 메서드 호출만으로 쿼리가 수행됩니다. 즉, SQL 반복 작업을 하지 않으므로 생산성이 매우 높아집니다. 그런데 SQL을 직접 사용하지 않는다고 해서 SQL을 몰라도 된다는 것은 아닙니다.
    Hibernate가 수행한 쿼리를 콘솔로 출력하도록 설정을 할 수 있는데, 쿼리를 보면서 의도한 대로 쿼리가 짜여졌는지, 성능은 어떠한지에 대한 모니터링이 필요하기 때문에 SQL을 잘 알아야 합니다.
  • 유지보수
    테이블 컬럼이 하나 변경되었을 경우, Mybatis에서는 관련 DAO의 파라미터, 결과, SQL 등을 모두 확인하여 수정해야 함 JPA를 사용하면 JPA가 이런 일들을 대신해주기 때문에 유지보수 측면에서 좋습니다.
  • 특정 벤더에 종속적이지 않음 여러 DB 벤더( MySQL, Oracle 등.. )마다 SQL 사용이 조금씩 다르기 때문에 애플리케이션 개발 시 처음 선택한 DB를 나중에 바꾸는 것은 매우 어렵습니다. 그런데 JPA는 추상화된 데이터 접근 계층을 제공하기 때문에 특정 벤더에 종속적이지 않습니다.
    즉, 설정 파일에서 JPA에게 어떤 DB를 사용하고 있는지 알려주기만 하면 얼마든지 DB를 바꿀 수가 있습니다.

단점

  • 성능
    메서드 호출로 쿼리를 실행한다는 것은 내부적으로 많은 동작이 있다는 것을 의미하므로, 직접 SQL을 호출하는 것보다 성능이 떨어질 수 있습니다.
    실제로 초기의 ORM은 쿼리가 제대로 수행되지 않았고, 성능도 좋지 못했다고 합니다.
    그러나 지금은 많이 발전하여, 좋은 성능을 보여주고 있고 계속 발전하고 있습니다.
  • 세밀함
    메서드 호출로 SQL을 실행하기 때문에 세밀함이 떨어집니다. 또한 객체간의 매핑( Entity Mapping )이 잘못되거나 JPA를 잘못 사용하여 의도하지 않은 동작을 할 수도 있구요. 복잡한 통계 분석 쿼리를 메서드 호출로 처리하는 것은 힘든 일입니다. 이것을 보완하기 위해 JPA에서는 SQL과 유사한 기술인 JPQL을 지원합니다. 물론 SQL 자체 쿼리를 작성할 수 있도록 지원도 하고 있습니다.
  • 러닝커브
    JPA를 잘 사용하기 위해서는 알아야 할 것이 많습니다.
    그래서 이러한 복잡성을 해결하고자 최근에는 Spring Data JDBC가 주목을 받고 있습니다.

ORM 정의 - 객체관계맵핑

  • 객체는 객체대로 설계
  • 관계형 데이터베이스는 관계형 데이터베이스대로 설계
  • ORM프레임워크가 중간에서 맵핑

하이버네이트

jpa 예시 마이바티스 비교

저장 : jpa.persist(member) 조회 : Member member = jpa.find(memberId) 수정 : member.setName(“변경할 이름”) 삭제 : jpa.remove(member)

jpa설정에서 방법을 선택할 수 있음. 성능에따라 설정

  • 지연로딩

  • 즉시로딩

데이터베이스 스키마 자동생성

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

중요

엔티티매니저는 영속성 컨텍스트이다. EntityManager.persist();->위의뜻 영구적 저장

JPA 엔티티매니저 -> 영속성 컨텍스트 내부로직

  • 1차캐시 (JPA내부에 임시저장하는캐시속에 동일데이터있을경우 DB에서조회하지않고 캐시내 값을 리턴함 캐시내 값이없으면 DB에서 조회)
  • 동일성 보장 (jpa의 동일한값을 비교할경우 true가나옴 1차캐시가 이유임)
  • 트랜잭션
  • 변경감지 기능 제공 (캐시 내부에 스냅샷기능이있어 1차캐시와 내부스냅샷기능이 비교하여 변경된 값을 인지하여 데이터를 업데이트하는 구조임)

em.flush(); 영속성컨텍스트를 DB와 동기화시킴 플러시는 값을 DB에 동기화해주는것 최종 커밋이되야 반영확정됨. em.clear(); 영속성 컨텍스트를 클리어시킴

JPQL 실행

명령어

JPA 어노테이션 종류
@Entity : JPA 엔티티 클래스 라는 선언, final enum interface 사용 안됨.
@Table : 매핑할 클래스 정보,
@Id : 엔티티 클래스의 PK값,
@Column : 매핑할 테이블 컬럼명을 name 속서의로 매핑하는 용도 name 생략시, 필드의 이름을 따름
//현업에서많이씀
@ManyToOne : N:1 매핑
@OneToMany : 1:N 매핑
p.s ) JPA에서는 주인쪽이 외래키를 관리하고
보통 DB에서는 1:N관계에서 N쪽이 외래키를 관리한다.
ManyToOne 관계에서는 Many쪽에 외래키가 생성되고
OneToMany 관계에서도 Many쪽에 외래키가 생성된다.
JoinColumn 생성 위치랑은 별도로 그렇다.

//알고만있어도됨
@OneToOne : 1:1 매핑
@ManyToMany : N:M 매핑
@JoinColumn : Join에 쓰일 외래키 지정, name에 매핑할 외래키를 지정한다.
@JoinTable : M:N 관계에서 중간 경유 테이블 지정할때 쓰임
@JoinTable.name : 연결 테이블 지정
@JoinTable.joinColumns:현재 방향에서 매핑할 연결 테이블의 컬럼명 지정
@JoinTable.inverseJoinColumns : 반대 방향에서 매핑할 연결 테이블의 컬렴명을 지정, Join컬럼처럼 owner쪽에서 선언해주면됨,

referencedColumnName은 외래키가 참조하는 대상 테이블 명을 따로 지정할때 쓰이는 속성이다.

@IdClass(Class.class) : 복합 기본키 선언
PK가 여러 key로 이루어진 테이블을 복합 기본키 라고 부름.
복합 기본 키를 이용하기 위해선 엔티티 위에 이 어노테이션을 붙여야한다.

@Inheritance : 상속 매핑,
Inheritance.strategy : 상속 매핑 전략 지정

@DiscriminatorColumn : 부모 클래스에 구분 컬럼 지정,
DiscriminatorColumn.name 컬럼명 지정

@DiscriminatorValue : 엔티티 저장시, 구분 컬럼에 입력할 값 지정

Inheritance,DiscriminatorColumn,DiscriminatorValue 모두
SuperType SubType relationShip 모델링 기법을 사용할때 쓰임

Inheritance는 조인 전략을 지정하고,

DiscriminatorColumn은 테이블의 타입을 지정하여 사용하도록 함
조인 전략에서 자식 테이블의 기본 값은 부모 테이블의 PK 컬럼명을 따르고 있다.
만약 자식 테이블의 컬럼명을 변경하고 싶다면 @PrimaryKeyJoinColumn을 이용해서 재 정의 해야한다.

@GeneratedValue : pk 생서 전략 설정
@Lob : BLOB,CLOB 타입 매핑
@Temporal : 날짜 타입 매핑
@Transient : 해당 필드는 매핑되지 않음
@Access : JPA 필드 접근 제한
AccessType.FIELD : 필드로 접근 private이어도 접근 가능
AccessType.PROPERTY : 접근자 Getter를 통해 접근

실습 코딩예제


		//jpa 호출 로직 시작
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");//퍼시스턴스유닛 네임 설정한 DB랑 맵핑해서 정보 호출
		EntityManager em = emf.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		
		tx.begin();//트랜잭션시작
			try {
				//내부 로직 돌리기
				MainData md = new MainData();
				//테이블 여러번 호출테스트
				md.getSeq() ;//롬복으로 데이터 끌어와야함. 근데 안불러짐 게터세터 수동설정 (추후 롬복연동 ㄱㄱ) 
				md.getBRAND_NAME();
				md.getSEASON_REASON();
				em.persist(md);
				tx.commit();//작업내용 삽입
			}catch(Exception e) {
				tx.rollback();//트랜잭션 오류발생시 롤백
			}finally {
				em.close();//엔티티매니저 종료
			}
		emf.close();//모든 플로우 진행 후종료
		//jpa 호출 로직 종료
@Entity
@Table(name = "AP_STATISTIC")
public class MainData {
     @Id
    //일반적으로 키(primary key)를 가지는 변수에 선언합니다. 
    //@GeneratedValue 어노테이션은 해당 Id 값을 어떻게 자동으로 생성할지 전략을 선택할 수 있음
    @GeneratedValue(strategy = GenerationType.AUTO)//
    private Long seq;
   
    /*  @Column에서 지정한 멤버 변수와 데이터베이스의 컬럼명을 다르게 주고 싶다면 @Column(name="XXX") 
       같은 형식으로 작성해주면 됩니다. 그렇지 않으면 기본적으로 멤버 변수명과 일치하는 데이터베이스의 컬럼을 매핑*/
    @Column(name="item_number", nullable = false) //nullable은 false시 낫널조건주는거임
    private String item;
    @Column(name="brand_name" ,length = 20) //길이 제약조건 length주면됨.
    private String brand;
    @Column(name="season_reason")
    private String season;
    @Column(name="status")
    private String status;
    @Column(name="test")
    private String test;
    
    
    //날짜 
    @Temporal(TemporalType.DATE)
    private Date date;
    @Temporal(TemporalType.TIME)
    private Date time;
    @Temporal(TemporalType.TIMESTAMP)
    private Date timestamp;
    
    
    //CLOB,BLOB 맵핑
    @Lob
    private byte[] blob;
    @Lob
    private String clob;
   
}

spring data jpa

Spring Data JPA는 JPA를 쓰기 편하게 만들어놓은 모듈이다 DB에 접근할 필요가 있는 대부분의 상황에서는 Repository를 정의하여 사용했다. Repository가 바로 Spring Data JPA의 핵심이다.

Spring Data JPA는 Spring에서 제공하는 모듈 중 하나로, 개발자가 JPA를 더 쉽고 편하게 사용할 수 있도록 도와준다. 이는 JPA를 한 단계 추상화시킨 Repository라는 인터페이스를 제공함으로써 이루어진다. 사용자가 Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring이 알아서 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록해준다. Spring Data JPA가 JPA를 추상화했다는 말은, Spring Data JPA의 Repository의 구현에서 JPA를 사용하고 있다는 것이다. 예를 들어, Repository 인터페이스의 기본 구현체인 SimpleJpaRepository의 코드를 보면 아래와 같이 내부적으로 EntityManager을 사용하고 있는 것을 볼 수 있다.

@Configuration
@EnableJpaRepositories(basePackages = "com.joont.repository")
public class AppConfig(){}
//JpaRepository(공통 인터페이스)
public interface MemberRepository extends JpaRepository<Member, Long>{

}

JPA와 Spring Data JPA는 똑같이 JPA가 들어가서 처음 접하는 사람은 상당히 헷갈릴 수 있다. 이 세 개념의 차이점을 정확히 인지하고 숙지하고 있으면 개발이 한층 편해질 것이다.

JPQL 직접정의

// 위치기반 파라미터
public interface MemberRepository extends JpaRepository<Member, Long>{
    @Query("SELECT m FROM Member m WHERE m.name = ?1")
    Member findByName(String name);
}

// 이름기반 파라미터
public interface MemberRepository extends JpaRepository<Member, Long>{
    @Query("SELECT m FROM Member m WHERE m.name = :name")
    Member findByName(@Param("name") String name);
}

결과 조회 API

query.getResultList()

  • 결과가 하나 이상인 경우, 리스트를 반환한다. query.getSingleResult()
  • 결과가 정확히 하나, 단일 객체를 반환한다.(정확히 하나가 아니면 예외 발생)

Named 쿼리 - 정적 쿼리

미리 정의해서 이름을 부여해두고 사용하는 JPQL 어노테이션, XML에 정의 애플리케이션 로딩 시점에 초기화 후 재사용 애플리케이션 로딩 시점에 쿼리를 검증

어노테이션 예시)
@Entity
@NamedQuery(
   name = "Member.findByUsername",
   query = "select m from Member m where m.username = :username")
public vlass Member {
...
}

Comments