[ KOSTA 교육 45일차 ] Hibernate / JPA | persistence.xml | persistence-unit | EntityManager

junjun·2024년 7월 2일
0

KOSTA

목록 보기
43/48

MyBatis의 목표

  • DAO 클래스를 없애자.
    • DAO를 인터페이스화 시키고, 이를 Spring Framework에게 mapper.xml 파일과 연동하도록 설정하여 SQL문과 Application을 분리했다.
    • 물론 Spring의 도움 없이 MyBatis만으로도 DB연동이 가능하다.
  • resultMap을 통해 VO를 조립할 수 있습니다.
    • 여러 JOIN을 가능하게 할 수 있습니다. ( 2중, 3중.. N중 조인 )

Hibernate / JPA

  • 쿼리가 다시 Application 단으로 돌아왔다.
    • 단순 CRUD 세트나 단순한 쿼리작업의 경우 기본 제공 or 메서드 이름만으로 생성이 가능한 장점이 있다.
    • 쿼리에 대한 빈번한 수정 작업이 없다면 굉장히 좋다.
    • 캐싱을 지원해서 성능에 유리하다.
      - 1차 캐시 ( 트랜잭션마다 생성되는 EntityManager )
      • 2차 캐시 ( Application 전체에서 공유 )
  • 복잡한 쿼리의 경우 @Query 혹은 createQuery()를 직접 사용해야할 수도 있다.
  • MyBatisDAO를 없앴다면, HibernateVO를 없앤다.
  • 내 생각) MyBatis의 경우 SQL과 App의 완전한 분리가 가능했는데,
    Hibernate/JPA는 자동화의 이점과 완전한 쿼리-어플리케이션 분리를 트레이드-오프한 것 같다.

Hibernate를 쓰기 위해 필요한 설정 두가지

  • 설정 파일 ( hibernate.cfg.xml ) = mybatis-context.xml
  • Entity 파일 ( UserVO.java )

Hibernate의 SessionFactory

  • HibernateSessionFactory 설정에는 setter로 가득차있다.
    • SessionFactory Bean 설정 및 조립 시, Spring Context가 수정자 주입을 해준다.
  • Hibernate의 방언(Dialect)
    • SQL을 생성할 때, 어떤 문법으로 할 지 결정
      - Oracle은 버전에 따라 SQL문 문법이 조금씩 다르다.
      • Oracle10gDialect, Oracle11gDialect와 같이 방언 설정 가능
  • hibernate.hbm2ddl.auto : 스키마 자동 생성 ( CREATE, CREATE-DROP, NONE .. )

JPA에서 Date, @Temporal(Temporal.TIMESTAMP)

  • Java의 Date 타입과 DB의 TIMESTAMP 타입은 다르게,
    JPA Entity 컬럼 위에 Temporal.TIMESTAMP 이렇게 넣어주어야 한다.

  • 정산/결제 쪽에서는 Date 타입 자체가 중요하지만,
    일반적인 서비스에서는 날짜는 보여주기 용도이기에, String으로 담아도 된다.

ORM : MyBatis vs Hibernate

  • MyBatis는 Mapper를 사용한 인터페이스로 DB 테이블에 접근하여 사용하지만, Hibernate는 SQL 스키마를 자동 생성하고 객체에 대한 접근으로 DB 테이블에 접근합니다. ( DDL, DML의 자동화 )

  • Hibernate는 1차 캐시와 2차 캐시를 사용하여 DB 접근 횟수를 줄일 수 있습니다.

Hibernate에서는 MyBatis와 다르게 DAO 클래스가 생성됩니다.

  • JpaRepository를 상속하여 I/F화 시켜서 DAO 클래스가 없어진 것은 Spring Data JPA의 도움을 받아 가능합니다.

  • SessionFactory로 부터 Session을 받아와서 쿼리를 수행합니다.

@Autowired
priavte SessionFactory sessionFactory;

...

@Transactional
public Lit<UserEntity> getAll() {
	// Hibernate Criteria API 사용 방식 - 컴파일 시점에 에러 검사
    // return sessionFactory.getCurrentSession()
    //	.createCriteria(UserEntity.class).list();
    
    // HQL (Hibernate Query Language) 사용 방식 - 런타임 시점에 검사
    return sessionFactory.getCurrentSession()
    		.createQuery("from UserEntity").list();
}

JPA

[JPA를 Spring에서 사용하기 위한 설정]

1. pom.xml 에 관련 의존성 설치

<!-- JPA API -->
<dependency>
  <groupId> javax.persistence </groupId>
  <artifactId> javax.persistence-api </artifactId>
  <version>2.2</version>
</dependency>

<!-- Spring Data JPA -->
<dependency>
  <groupId> org.springframework.data</groupId>
  <artifactId> spring-data-jpa </artifactId>
  <version>1.7.0.RELEASE</version>
</dependency>
  • JPA 자체에 대한 의존성 ( javax.persistence-api )과 JPA를 스프링에 통합해서 사용하기 위한 의존성 ( spring-data-jpa )를 받아온다.

2. servlet-context.xml에 spring-data-jpa 관련 xmlns 설정, 소스로 spring-data-jpa.xsd 연결

pom.xml 상태

<?xml version="1.0" encoding="UTF-8"?>
<beans 
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
	http://www.springframework.org/schema/mvc 
	http://www.springframework.org/schema/mvc/spring-mvc.xsd
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/data/jpa 
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
	">
	
<!--  ******************************** ROOT CONTEXT [START] *********************************  -->
	
	
	
	<!-- ===============================[어노테이션 기반으로 동작]================================== -->
	<!--  어노테이션 기반 -->
	<mvc:annotation-driven /> 
	<!-- 적용 대상 패키지 -->
	<context:component-scan base-package="com.lec11.orm.jpa" />
	
	
	<!-- datasource : 프로퍼티 파일을 사용한 형태 -->
	<context:property-placeholder location="classpath:oracle.properties" />
	<bean id="MY_tomcat_ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
	    <property name="driverClassName" 	value="${lec.driver}" />
	    <property name="url" 				value="${lec.url}" />
	    <property name="username" 			value="${lec.username}" />
	    <property name="password" 			value="${lec.userpw}" /> 
	</bean>
	
	
	 <!-- JPA 설정 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="MY_tomcat_ds" />
        <property name="packagesToScan" value="com.lec11.orm.jpa.entity" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>
	
	<!-- JPA 레포지토리 설정 -->
    <jpa:repositories base-package="com.lec11.orm.jpa" />
	
	
    <!-- 트랜잭션 매니저 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
	 
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
	 
	<!--  ******************************** ROOT CONTEXT [END] *********************************  -->	
	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/" />
		<property name="suffix" value=".jsp" />
	</bean>
	
	
</beans>
  • 웹 어플리케이션의 /WEB-INF/web.xmlDispatcherServlet의 초기화 매개변수로 lec11-servlet-context.xml을 넣어줍니다.
<!-- web.xml -->
<servlet>
  	<servlet-name>SPRING_SERVLET</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/lec11-servlet.context.xml</param-value>
    </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>SPRING_SERVLET</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

3. Hibernate의 SessionFactory에서 EntityManager로 변경

AS-IS) 기존 Hibernate SessionFactory 설정

<!-- Hibernate SqlSessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  	<property name="dataSource" ref="MY_tomcat_ds"/>
  	<property name="packagesToScan" value="com.lec10.orm.hibernate.entity"/>
	<property name="hibernateProperties">
      	<props>
          	...// Hibernate 관련 설정

TO-BE) JPA의 EntityManager 설정

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  	<property name="dataSource" ref="MY_tomcat_ds" />
  	<property name="packagesToScan" value="com.lec11.orm.jpa.entity"/>
  	<property name="jpaVendorAdapter">
      	<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
  </property>
  <property name="jpaProperties">
    <props>
      ... // Hibernate 관련 설정
  • org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
    • Hibernate를 JPA에서 가져다가 좀 더 확장해서 쓰겠다는 의미
    • 기본 JPA 기능에 더불어 Hibernate만의 확장된 기능 또한 사용할 수 있다.

JPA의 규약) JPA 관련 설정은 src/main/resources/META-INF 안에 persistence.xml이란 이름으로 존재해야 한다.

  • MyBatis의 mybatis-context.xml, Hibernate에서의 hibernate.cfg.xml 으로 영속성 관련 설정을 한 것과 마찬가지로, JPA 에도 persistence.xml이 존재한다.
<!-- persistence.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<persistence 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_1.xsd"
             version="2.1">
             
    <persistence-unit name="my-persistence-unit">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        
        <!-- 엔티티 클래스 패키지를 지정 -->
        <class>com.lec11.orm.jpa.UserEntity</class>
        
        <!-- 데이터베이스 연결 설정 -->
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
            <property name="javax.persistence.jdbc.user" value="it"/>
            <property name="javax.persistence.jdbc.password" value="0000"/>
            <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
            
            <!-- Hibernate 설정 -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="none"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

persistence.xml & Persistence Unit

  • peresistence-unit : 응용 프로그램의 EntityManager 인스턴스에 의해 관리되는 모든 엔티티 클래스의 집합을 정의합니다.

  • JPAVO=Entity 객체와 테이블을 매핑하기 위한 다양한 설정 정보가 필요합니다.
    이를 위해 persistence.xml 파일을 설정 파일로 사용헙나더,

    • persistence.xml의 위치는 src/java/resources/META-INF 내에 존재해야 함, <persistence>를 루트 요소로 사용합니다.
    • 영속성 유닛(persistence-unit)과 관련한 다양한 정보가 설정됩니다.
    • 영속성 유닛은 연동할 DB당 하나씩 등록한다.
      영속성 유닛에 설정된 이름은 나중에 DAO 클래스 구현 시, EntityManagerFactory 객체에 사용됨.
  • JPA를 이용하여 DB 연동을 구현하기 위해서는 EntityManager 객체가 필요함.
    EntityManager를 얻으려면, EntityManager 객체를 생성하기 위한 EntityManagerFactory 객체가 필요합니다. EntityManagerFactory 객체를 생성할 때 설정으로 PersistenceUnit이 사용됨.

  • Spring 과 연동하여 JPA를 사용할 수도 있겠지만, JPA 자체만으로 Java 코드에 연동하여 DB에 접근할 수 있습니다.

    • JPA는 Java EE 에서 제공하는 API이기 때문입니다. 서블릿 API와 성질이 같다 생각하면 됩니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");
EntityManager em = emf.createEntityManager();
if( em != null )
	System.out.println("em ok");

em.getTransaction().begin();

UserEntity user = em.find(UserEntity.class, 1L);
em.close();
emf.close();
  • 물론 단일 조회 연산이기에 트랜잭션은 필요없지만, 예시를 위해 em.getTransaction() 을 사용했습니다.

  • CriteriaBuilderCriteriaQuery라는 문법을 사용하여 코드로 쿼리를 생성할 수 있습니다.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");
EntityManager em = emf.createEntityManager();

em.getTransaction().begin();

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<UserEntity> query = builder.createQuery(UserEntity.class);

Root<UserEntity> root = query.from(UserEntity.class);
Predicate userIdPredicate = builder.equal(root.get("userId"), "admin");
Predicate userPwPredicate = builder.equal(root.get("userPw"), "0000");
query.where(builder.and(userIdPredicate, userPwPredicate));

UserEntity entity = null;
try {
    	entity = em2.createQuery(query).getSingleResult();
    	if (entity  != null) {
            System.out.println("로그인 성공: " + entity);
        }
    } catch (Exception e) {  //결과가 없을 경우 NoResultException을 발생
        System.out.println("로그인 실패");
    }

    // Transaction 커밋 및 엔티티 매니저 종료
   	em2.getTransaction().commit();
    em2.close();
    emf2.close();
  • 흠... 코드가 좀.. 지금 당장 봤을 때는
    JPA는 단독으로 안쓰는게 좋다는 생각이 듭니다.

JpaRepository 가 기본 제공하는 메서드

//select count(1) from users3;
userRepository.count();
//delete from users3 where userSeq= ?
userRepository.delete(Long id);

//delete from users3 where userId= ? and userPw=? and userName=?
userRepository.delete(UserEntity entity);
//delete from users3;
userRepository.deleteAll();
//select * from users3 wehre userSeq=?
userRepository.exists(Long id);
//select * from users3;
userRepository.findAll()
//sub.. sub.. sub..select * from users3 where~~~ rnum between 1 and 10;
userRepository.findAll(Pageable);
//select * from users3 order by userName asc, userSeq desc;
userRepository.findAll(Sort)

//select * from users3 where userSeq=?
userRepository.findOne(Long id);
userRepository.getOne(Long id);

//insert into users3 (...)
//update users3 set (...)
userRepository.save(UserEntity entity);
  • 기본적으로 제공하는 쿼리 메서드가 없는 경우 커스터마이징이 가능합니다 ( 사용자가 필요로 하는 쿼리는 쿼리 이름으로 커스터마이징 가능합니다. )
    • JPA가 제시하는 메서드 규칙에 따라 만들어주면 쿼리를 자동으로 생성해줍니다.
  • 쿼리가 너무 복잡하면 @Query, createQuery() 메서드를 통해 쿼리를 생성할 수 있습니다.

@GeneratedValue의 strategy : IDENTITY, SEQUENCE

  • IDENTITY 전략
    - 유일하기만 하면 됨.
    • ex) 주문 코드
  • SEQUENCE 전략
    - DB의 시퀀스처럼 자동 증가 번호, 순차적인 번호
    • ex) 게시물 번호

0개의 댓글