(스프링프레임워크학원/JPA학원)Spring JDBC와 Spring Data JPA를 간단한 CRUD 예제로 만들면서 비교해…

(스프링프레임워크학원/JPA학원)Spring JDBC와 Spring Data JPA를 간단한 CRUD 예제로  만들면서 비교해보자. 

 

 

Spring JDBC JPA 간단한 CRUD 예제로

만들면서 비교해보자.



0. 스프링 부트, JPA에서 데이터베이스 초기화
n  스프링 부트에서는 클래스패스 경로에(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  것이다.
 
n  JPA는 시작시점에 DDL을 자동 생성 및 실행해 주는 기능이 있는데 두개의 외부 속성으로 정의한다. spring.jpa.generate-ddl (boolean) on, off 값을 가지고 DB벤더에 종속적이지 않으며 spring.jpa.hibernate.ddl-auto (enum)는 하이버네이트 특성으로 하이버네이트 Sessionfactory가 시작할 때 JPA의 엔티티 매연관관계 설정을 기본으로 테이블과 같은 스키마 생성 스크립트를 만들고 실행하여 데이터베이스 초기화를 지원하는데 열거형 값인 none, validate, update, create, create-drop 값을 가진다.
 
n  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 예제
n  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("empno");
           }
}
 
스프링 부트 메인(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 예제
 
n  이전의 예제를 Spring Data JPA를 이용한 형태로 변형하여 작성해 보자.      
n  간단히 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번 수정후");                       
           }
}
  

 

, , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Comments