13
0. 스프링 부트, JPA에서 데이터베이스 초기화 스프링 부트에서는 클래스패스 경로에(src/main/resources) schema.sql, data.sql, schema-${platform}.sql, data-${platform}.sql 파일등이 존재한다면 자동으로 실행해서 스키 구조와 데이터를 초기화 시켜주는데. ${platform} 값은, 만약 application.properties 일에서 spring.datasource.platform=mysql 이라고 했다면 schema-${platform}.sql 파일의 이름은 schema-mysql.sql것이다.

(스프링JDBC와 Spring Data JPA비교)Spring JDBC와 JPA를 간단한 CRUD 예제로 만들면서 비교해보자

  • Upload
    3-2

  • View
    247

  • Download
    2

Embed Size (px)

Citation preview

0. 스프링 부트, JPA에서 데이터베이스 초기화

스프링 부트에서는 클래스패스 경로에(src/main/resources등) schema.sql, data.sql,

schema-${platform}.sql, data-${platform}.sql 파일등이 존재한다면 자동으로 실행해서 스키

마 구조와 데이터를 초기화 시켜주는데. ${platform} 값은, 만약 application.properties 파

일에서 spring.datasource.platform=mysql 이라고 했다면 schema-${platform}.sql 파일의

이름은 schema-mysql.sql이 될 것이다.

JPA는 시작시점에 DDL을 자동 생성 및 실행해 주는 기능이 있는데 두개의 외부 속성으로 정

의한다. spring.jpa.generate-ddl (boolean) on, off 값을 가지고 DB벤더에 종속적이지 않으며

spring.jpa.hibernate.ddl-auto (enum)는 하이버네이트 특성으로 하이버네이트 Sessionfactory가

시작할 때 JPA의 엔티티 매핑, 연관관계 설정을 기본으로 테이블과 같은 스키마 생성 스크립

트를 만들고 실행하여 데이터베이스 초기화를 지원하는데 열거형 값인 none, validate, update,

create, create-drop 값을 가진다.

hsqldb, h2 and derby 데이터베이스는 create-drop이 기본이며 그외 DB는 none이 기본값이다.

none : 자동 DDL 생성 안함.

create : 하이버네이트 Sessionfactory 가 시작될 때 항상 다시 생성, 이미 있다면 지우고

생성.

create-drop : Sessionfactory 가 시작될 때 생성 후 종료할 때 삭제한다.

update : Sessionfactory 가 시작될 때 엔티티 클래스(도메인 클래스)와 DB 에 생성된 스키마

구조를 비교해서 DB 쪽에 생성이 안된 테이블 또는 칼럼이 있다면 DB 스키마를 변경해서

생성시키지만 기 생성된 스키마 구조를 삭제하지는 않는다.

validate : Sessionfactory 가 시작될 때 엔티티 클래스(도메인 클래스)와 DB 에 생성된 스키마

구조를 비교해 같은지 확인만 할 뿐 DB 스키마 구조는 변경하지 않고 만약 다르다면 예외를

발생시킨다.

1. jdbcTemplate을 이용한 CRUD 예제

Spring Boot, MariaDB를 이용해서 EMP 테이블을 만들고 JdbcTemplate을 이용하여 CRUD 기

능을 구현해 보자.

STS에서

File -> New -> Project -> Spring Starter Project

Name : jdbc2

Package : jdbc

다음화면에서 SQL : JDBC, MySQL 선택

MariaDB에서 직접 SQL을 작성하여 테이블 및 데이터를 생성할 수 있지만 스프링 부트

에서는 클래스패스 경로에 schema.sql, data.sql이 존재하면 자동실행 하므로 스키마 생성

부분과 데이터 생성부분을 파일로 만들어두면 된다.

[src/main/resources/schema.sql]

drop database if exists jdbc2;

create database jdbc2;

use jdbc2;

create table emp

(

empno int(4) not null auto_increment,

ename varchar(50),

primary key (empno)

) ENGINE=InnoDB;

[src/main/resources/data.sql(파일 속성에서 text encoding을 UTF-8로)]

insert into emp(ename) values ('1길동');

insert into emp(ename) values ('2길동');

insert into emp(ename) values ('3길동');

[src/main/resources/application.properties]

spring.datasource.platform=mysql

spring.datasource.url=jdbc:mysql://localhost/jdbc2?createDatabaseIfNotExist=true

spring.datasource.username=root

spring.datasource.password=1111

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.sql-script-encoding=UTF-8

#커넥션풀에서 커넥션을 가져올 경우 커넥션이 유효한지 검사

도메인 클래스(Emp.java) – 테이블구조와 동일하다.

package jdbc.domain;

public class Emp {

private Long empno;

private String ename;

public Emp() { }

public Emp(String ename) { this.ename = ename; }

public Emp(Long empno, String ename) {

this.empno = empno;

this.ename = ename;

}

public Long getEmpno() { return empno; }

public void setEmpno(Long empno) {

this.empno = empno;

}

public String getEname() {

return ename;

}

public void setEname(String ename) {

this.ename = ename;

}

public String toString() { return "[empno=" + empno +",ename=" + ename + "]"; }

}

RowMapper 구현체(EmpRowMapper.java)

package jdbc.repository;

import java.sql.ResultSet;

import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import org.springframework.stereotype.Repository;

import jdbc.domain.Emp;

@Repository

public class EmpRowMapper implements RowMapper {

@Override

public Emp mapRow(ResultSet rs, int rowNum) throws SQLException {

Long empno = rs.getLong("empno");

String ename = rs.getString("ename");

return new Emp(empno, ename);

}

}

Repository 인터페이스(EmpRepository.java) – 영속성 서비스용 인터페이스

package jdbc.repository;

import java.util.List;

import jdbc.domain.Emp;

public interface EmpRepository {

List<Emp> findAll();

Emp findOne(Long empnno);

Emp save(Emp emp);

void delete(Long empno);

}

Repository 구현체(EmpRepositoryImpl.java) – 영속성 서비스용 구상클래스

package jdbc.repository;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.List;

import javax.annotation.PostConstruct;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.core.RowMapper;

import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;

import org.springframework.jdbc.core.namedparam.SqlParameterSource;

import org.springframework.jdbc.core.simple.SimpleJdbcInsert;

import org.springframework.stereotype.Repository;

import org.springframework.transaction.annotation.Transactional;

import jdbc.domain.Emp;

@Repository

@Transactional(readOnly=true)

public class EmpRepositoryImpl implements EmpRepository {

private SimpleJdbcInsert jdbcInsert;

private JdbcTemplate jdbcTemplate;

@Autowired

RowMapper<Emp> empRowMapper;

@Autowired //스프링부트에서 DataSource를 자동 주입해 준다.

public void setDataSource(DataSource dataSource) {

this.jdbcTemplate = new JdbcTemplate(dataSource);

}

@Override

public List<Emp> findAll() {

List<Emp> emps = jdbcTemplate.query("select empno, ename from

emp",empRowMapper);

return emps;

}

@Override

public Emp findOne(Long empno) {

return (Emp)jdbcTemplate.queryForObject("select empno, ename from emp

where empno = ?", empRowMapper, empno);

}

@Override

@Transactional(readOnly=false)

public Emp save(Emp emp) {

SqlParameterSource param = new BeanPropertySqlParameterSource(emp);

if (emp.getEmpno() == null) {

Number key = jdbcInsert.executeAndReturnKey(param);

emp.setEmpno(key.longValue());

}

else {

this.jdbcTemplate.update(

"insert into emp (empno, ename) values (?, ?)",

emp.getEmpno(), emp.getEname()

);

}

return emp;

}

@Override

@Transactional(readOnly=false)

public void delete(Long empno) {

this.jdbcTemplate.update(

"delete from emp where empno = ?",

empno

);

}

//생성자가 실행된 후에 실행된다.

@PostConstruct

public void init() {

//INSERT SQL Auto Create

jdbcInsert = new

SimpleJdbcInsert( jdbcTemplate).withTableName("emp").usingGeneratedKeyColumns("emp

no");

}

}

스프링 부트 메인(Jdbc2Application.java)

package jdbc;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import jdbc.domain.Emp;

import jdbc.repository.EmpRepository;

@SpringBootApplication

public class Jdbc2Application implements CommandLineRunner {

public static void main(String[] args) {

SpringApplication.run(Jdbc2Application.class, args);

}

@Autowired

EmpRepository empRepository;

public void run(String...args) {

//전체 사원 SELECT

List<Emp> emps = empRepository.findAll();

for(Emp e : emps) { System.out.println(e); }

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

//2번 사원 SELECT

Emp e = empRepository.findOne(2L);

System.out.println(e);

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

//3번 사원 DELETE

empRepository.delete(3L);

emps = empRepository.findAll();

for(Emp e1 : emps) { System.out.println(e1); }

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

//4번 사원 INSERT

e = empRepository.save(new Emp(4L, "4길동"));

emps = empRepository.findAll();

for(Emp e1 : emps) { System.out.println(e1); }

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

//'5길동' 사원 INSERT

Emp e5 = new Emp(“5길동”);

e = empRepository.save(e5);

emps = empRepository.findAll();

for(Emp e1 : emps) { System.out.println(e1); }

}

}

[결과]

2. Spring Data JPA를 이용한 CRUD 예제

이전의 예제를 Spring Data JPA를 이용한 형태로 변형하여 작성해 보자.

간단히 Spring Data JPA 구조, 기본적인 CRUD인 경우 쿼리를 직접 만들지 않아도 된다는 것

만 확인하자.

마리아DB는 아래 URL을 참조하여 설치하자.

http://ojc.asia/bbs/board.php?bo_table=LecSpring&wr_id=524

STS에서 File -> New -> Project -> Spring Starter Project

Name : springjpa, Package name : jpa

다음화면에서 SQL-> JPA, MySQL 선택

src/main/resources/data.sql(파일 속성에서 text encoding을 UTF-8로)

insert into emp(ename) values ('1길동');

insert into emp(ename) values ('2길동');

insert into emp(ename) values ('3길동');

Spring Data JPA는 테이블을 자동으로 생성하므로 이전 예제에서 작성한 schema.sql은 필요 없다.

src/main/resources/application.properties(파일 속성에서 text encoding을 UTF-8로)

spring.datasource.platform=mysql

spring.datasource.url=jdbc:mysql://localhost/emp2?createDatabaseIfNotExist=true

spring.datasource.username=root

spring.datasource.password=1111

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.sql-script-encoding=UTF-8

# 자동으로 DDL을 만들어 테이블 생성

spring.jpa.hibernate.ddl-auto=create

# 실행되는 SQL문을 로그에서 보이도록

spring.jpa.show-sql=true

도메인 클래스(Emp.java) – 테이블구조와 동일하다.

package jpa.domain;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

@Entity

public class Emp {

@Id //PK임을 지정

@GeneratedValue //자동증분 칼럼

private Long empno;

//@Column 어노테이션이없더라도 테이블 칼럼명과 자동 매핑

private String ename;

public Emp() { }

public Emp(String ename) { this.ename = ename; }

public Emp(Long empno, String ename) { this.empno = empno; this.ename = ename; }

public Long getEmpno() { return empno; }

public void setEmpno(Long empno) { this.empno = empno; }

public String getEname() { return ename; }

public void setEname(String ename) { this.ename = ename; }

public String toString() { return "[empno=" + empno +",ename=" + ename + "]";}

}

Repository 인터페이스(EmpRepository.java) – 영속성 서비스용 인터페이스

package jpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import jpa.domain.Emp;

/*

* JpaRepository에는 기본적인 CRUD(findAll, findOne, save, delete, deleteAll) 메소드가

* 정의되어 있으며 이를 상속한 인터페이스를 만듦으로써 구현클래스 없이 레포지터리 구성이

가능하다. 제너릭으로 도메인 클래스와 키칼럼(ID칼럼)의 타입을 기술하면 된다.

*/

public interface EmpRepository extends JpaRepository<Emp, Long> { }

스프링 부트 메인(SpringjpaApplication.java)

package jpa;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import jpa.domain.Emp;

import jpa.repository.EmpRepository;

@SpringBootApplication

public class SpringjpaApplication implements CommandLineRunner {

public static void main(String[] args) {

SpringApplication.run(SpringjpaApplication.class, args); }

@Autowired

EmpRepository empRepository;

public void run(String...args) {

//전체 사원 SELECT

List<Emp> emps = empRepository.findAll();

for(Emp e : emps) { System.out.println(e); }

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

//2번 사원 SELECT

Emp e = empRepository.findOne(2L);

System.out.println(e);

System.out.println("---------------------2번사원 SELECT");

//3번 사원 DELETE

empRepository.delete(3L);

emps = empRepository.findAll();

for(Emp e1 : emps) { System.out.println(e1); }

System.out.println("---------------------3번 DELETE후");

//4번 사원 INSERT

e = empRepository.save(new Emp(4L, "4길동"));

emps = empRepository.findAll();

for(Emp e1 : emps) { System.out.println(e1); }

System.out.println("---------------------4번 INSERT후");

//'5길동' 사원 INSERT

Emp e5 = new Emp(“5길동”);

e = empRepository.save(e5);

emps = empRepository.findAll();

for(Emp e1 : emps) {

System.out.println(e1);

}

System.out.println("---------------------5번 INSERT후");

//'5길동' 사원 이름 수정

e = empRepository.findOne(5L);

e.setEname("수정된5길동");

empRepository.save(e);

emps = empRepository.findAll();

for(Emp e1 : emps) {

System.out.println(e1);

}

System.out.println("---------------------5번 수정후");

}

}