[Spring in Action] JDBC & JPA 데이터로 작업하기

728x90
반응형
SMALL

Spring in Action (5판)

  • 스프링 5의 강력한 기능과 생산성을 활용한 웹 애플리케이션 개발
  • Walls, Craig 지음
  • 제어펍 출판사
  • 2020.05.14

 

스프링 인 액션 개인 스터디 내용을 정리하자.

 

 

 

 


JDBC (Java Database Connectivity)

출처 : https://www.oracle.com/database/technologies/appdev/jdbc.html

JDBC는 DB에 접근할 수 있도록 자바에서 제공하는 API이다.

 

Spring의 JDBC 지원은 JdbcTemplate 클래스에 기반을 둔다.

 

JdbcTemplate은 JDBC를 사용할 때 요구되는 모든 형식적이고 상투적인 코드 없이 개발자에게 관계형 데이터베이스에 대한 SQL 연산을 수행할 수 있는 방법을 제공한다.

JdbcTemplate

데이터베이스 연결(connection), 명령문(statement), 결과 세트(result set) 등의 코드를 작성하지 않아도 된다.

 

또한, SQLException 예외 처리도 필요 없다.

 

쿼리를 수행하고, 그 결과를 객체로 생성하는 코드만 필요하다.

 

DAO (Data Access Object)
실제로 DB에 접근하는 Persistence Layer(DB에 data를 CRUD) 계층이다.

Service와 DB를 연결하는 역할을 한다.

DTO (Data Transfer Object)
계층 간 데이터 교환을 위한 객체(Bean)이다.

DB에서 데이터를 얻어 Service, Controller 등으로 보낼 때 사용한다.
즉, DB Data가 Presentation Logic Tier로 넘어오게 될 때 DTO 형태로 넘어온다.

VO (Value Object)
DTO와 동일한 개념이지만 Read Only 속성을 갖는다.

VO는 특정한 비즈니스 값을 담는 객체이고, DTO는 Layer 간 통신 용도로 사용되는 객체이다.

Entity
실제 DB Table과 매칭되는 클래스이다.

 

DataSource

JDBC 명세의 일부분 이면서 일반화된 연결 팩토리이다.

 

DB와 관련된 connection 정보를 담고 있으며, DB Server와 연결을 담당한다.

 

DB Connection Pool

애플리케이션에서 DB에 Connection 객체를 얻는 작업은 시간이 소요되는 작업 중 하나이다.

 

그렇기에, 매번 DB Connection 객체를 얻는 것이 아니라, 일정량의 Connection 객체를 미리 생성시켜 저장했다가 꺼내 사용하는 개념이다.

 

즉, 애플리케이션의 속도와 성능이 좋아진다.

 

 

 

 

Dependency (build.gradle)

 
compile('org.springframework.boot:spring-boot-starter-jdbc')
compile("mysql:mysql-connector-java:5.1.45")
compile('org.mybatis:mybatis:3.4.2')
compile('org.mybatis:mybatis-spring:1.3.1')
compile('org.apache.commons:commons-dbcp2:2.2.0')

 

DB 설정 (yml)

 
spring:
    datasource:
    master:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://{IP:PORT}/{DB Name}?useUnicode=true&characterEncoding=utf-8&connectionCollation=utf8_bin&characterSetResults=utf8&autoReconnect=true&autoReconnectForPools=true&serverTimezone=Asia/Seoul&useSSL=false&zeroDateTimeBehavior=convertToNull&connectTimeout=3000&socketTimeout=30000
        username: userName
        password: userPw
        validation-query: select 1
        test-on-borrow: true
        validation-interval: 10000
        default-auto-commit: true
        pool-prepared-statements: true
        initialSize: 2
        maxActive: 2
        maxIdle: 2
        minIdle: 2
        maxWait: 10000

    slave:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://{IP:PORT}/{DB Name}?useUnicode=true&characterEncoding=utf-8&connectionCollation=utf8_bin&characterSetResults=utf8&autoReconnect=true&autoReconnectForPools=true&serverTimezone=Asia/Seoul&useSSL=false&zeroDateTimeBehavior=convertToNull&connectTimeout=3000&socketTimeout=30000
        username: userName
        password: userPw
        validation-query: select 1
        test-on-borrow: true
        validation-interval: 10000
        default-auto-commit: true
        pool-prepared-statements: true
        initialSize: 2
        maxActive: 2
        maxIdle: 2
        minIdle: 2
        maxWait: 10000

 

최근에는 JPA를 사용하는 추세이므로 JDBC는 간단하게 살펴보았다.

 

JPA (Java Persistence API)

Java ORM 기술에 대한 표준 명세로, Java에서 제공하는 API이다.

 

Java 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다.

 

ORM이기에 SQL 매핑이 아닌, Java 클래스와 DB Table을 매핑한다.

ORM (Object-Relation Mapping)

DB <- Mapping -> Obejct, 즉 객체를 통해 간접적으로 DB Data를 다룬다.

객체와 DB Data를 자동으로 매핑해준다.
SQL 쿼리가 아닌 메서드로 Data를 조작, 객체 간 관계를 바탕으로 SQL을 자동으로 생성한다.

JPA는 애플리케이션과 JDBC 사이에서 동작한다.

 

개발자가 JPA를 사용하면, JPA 내부에서 JDBC API를 사용하여 SQL을 호출, DB와 통신한다.

 

JPA 동작 과정

MemberDAO 객체를 저장할 때, JPA에 Member 객체를 넘기면 JPA는 Member Entity를 분석하여 Insert Query를 생성, JDBC API를 사용하여 SQL을 DB에 날린다.

 

 

Member의 PK 값을 JPA에 넘기면, JPA는 Entity 매핑 정보를 바탕으로 Select Query를 생성, JDBC API를 사용하여 SQL을 DB에 날린다.

 

이후, DB로부터 결과를 받아 Result Set을 객체에 매핑한다.

 

JPA 특징

  • 데이터를 객체지향적으로 관리할 수 있기에, 개발자는 비즈니스 로직에 집중할 수 있고 객체지향 개발이 가능하다.
  • SQL 중심 개발이 아닌, 객체 중심 개발이 가능하다.
  • 간단한 메서드로 CRUD가 가능하여 생산성이 증가한다.
  • 필드 변경 시 SQL 수정이 아닌 필드 자체만 수정하면 JPA가 SQL을 처리한다.
  • Java 객체와 DB Table 사이의 매핑 설정을 통해 SQL을 자동으로 생성한다.
  • 즉, SQL을 JPA가 생성하기에 Obejct와 RDB간 패러다임 불일치를 해결할 수 있다.
Hibernate
JPA 구현체의 한 종류로, DB와 Java 객체를 매핑하기 위한 인터페이스를 구현한 것이다.

 

Dependency (build.gradle)

 
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2'

 

JPA 설정 (yml)

 
 
spring:
  profiles:
    active: local

  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:tcp://localhost:1521/test
    username: sa
    password: ''

  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    open-in-view: false
    show-sql: true
    hibernate:
      format_sql: true
      ddl-auto: create

 

Persistence 설정

 
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.1">
  <persistence-unit name="jpaSample">
    <properties>
      <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
    </properties>
  </persistence-unit>
</persistence>

 

Config

 
@Configuration
@EnableTransactionManagement
@RequiredArgsConstructor
public class PersistenceJPAConfiguration{

    private final DataSource dataSource;
    private final Properties properties;

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em
            = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("toy.repositories.entity");

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(properties);

        return em;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
        return new PersistenceExceptionTranslationPostProcessor();
    }

}

 

Entity

 
@Getter
@Setter
@Entity
public class Member implements Serializable {

    @Id
    @Column
    private String id;

    @Column
    private String name;

    @Column
    private int age;

    @Column
    private String sex;
}

 

Repository

 
@Repository
public interface MemberRepository extends MongoRepository <Member, ObjectId> {
 
    Member findById(String id);
 
    List<Member> findBySex(String sex);
 
}

 

Provider

 
 
@RequiredArgsConstructor
@Component
public class MemberProvider {
     
    private final MemberRepository memberRepository;
 
 
    public Member getById(String id) {
        return MemberRepository.findById(id);
    }
}

 

Sample

 
public class JpaSample {

    public static void main(String[] args) {
        // 1. entityManageFactory 생성
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpaSample");

        // 2. entityManager 생성
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        // 3. transaction 획득
        EntityTransaction transaction = entityManager.getTransaction();

        try {
            transaction.begin();

            System.out.println("logic...");

            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        } finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }
}
728x90
반응형
LIST