2018년 4월 11일 수요일

Spring JDBC

JDBC란?

  • java.sql 패키지를 이용한 Database프로그래밍을 JDBC 프로그래밍이라고 합니다. 
  • java.sql의 핵심인터페이스를 보면, 구현하는 클래스를 자바는 제공하지 않는다. 
java를 만든 사람들은 DBMS와 상관없이 같은 방법으로 프로그래밍을 하길 원했다.
- 데이터베이스 프로그래밍에 대한 인터페이스를 정의하였다.
- DBMS마다 구현하는 방법은 다르다. 그렇다면? DB제작사에서 해당 인터페이스를 구현한 클래스를 제공한다. --> JDBC 드라이버(*.jar)

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
</dependency>

데이터베이스 프로그래밍은 어떻게 하는가?

1-1. DB접속(java.sql) : 예전 연결방식으로 잘 쓰지않는다.

https://docs.oracle.com/javase/8/docs/api/index.html?java/sql/package-summary.html
  • Class.forName("드라이버 클래스이름"); //DBMS마다 이름이 다르다.
  • // dburl은 DBMS마다 형식이 다르다.
  • Connection conn = DriverManager.getConnection(dburl, dbid, dbpassword);

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/helloboard?useUnicode=true&characterEncoding=utf8
spring.datasource.username=fastcamp
spring.datasource.password=fastcamp

1-2. DB접속 (java.datasource)

https://docs.oracle.com/javase/8/docs/api/javax/activation/DataSource.html

  • 커넥션을 얻는다.
  • DataSource ----> 커넥션풀객체(Connection들을 여러개 가짐) ---> DB
  • DataSource를 구현한 객체가 필요합니다.
    DataSource dataSource = ....;
    Connection conn = dataSource.getConnection();
  • DataSource구현을 하고 있고 커넥션 풀이라고도 부른다. DB에 연결하기 위해서는 Driver를 사용합니다.
<dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.1.1</version>
</dependency>

2. SQL을 준비한다. (문자열을 준비)

  • role_id가 자동으로 생성된다면 다음과 같이 insert 한다.
    insert into ROLE( description ) values ( 'ADMIN' )
  • role_id가 자동으로 생성되지 않는다면 다음과 같이 insert 한다.
    insert into ROLE (role_id, description) values ( 500, 'ADMIN' );
프로그래밍을 할떄 나쁜 예제
  • + 연산자로 sql을 작성한다. SQL Injection 을 당할 수있다. 성능이 느려진다.
  • DBMS에서 SQL을 실행하면, DBMS는 SQL을 분석한 후 실행한다. 분석하는 시간이 오래걸린다.
  • 완전히 동일한 SQL은 이미 분석한 결과를 사용하여 실행한다. 분석을 한 결과는 캐싱에 남기 때문에.. (대소문자 구분한다.)
  • String sql = "insert into ROLE (role_id, description) values ( " + id + ", '" + description + " ');
좋은예제
  • 물음표 부분만 제외하고 분석됩니다.. 개발자는 이 물음표에 나중에 값을 채운 후 실행한다.
  • 물음표에 값을 채우는 것을 바인딩한다. 물음표의 순서는 1부터 시작한다.
  • String sql = "insert into ROLE (role_id, description) values ( ? , ? );

3. DB에서 SQL을 실행할 준비를 시킨다.

PreparedStatement ps = conn.prepareStatement(sql);
//물음표부분을 바인딩 한다. 각각의 type에 맞게
ps.setInt(1, 500); // 첫번째 물음표에 500을 설정
ps.setString(2, "ADMIN") // 두번째 물음표에 ADMIN설정
3.1. SQL이 준비가 되어있다면, SQL을 실행한다.

ps.execute()
int count = ps.excuteUpdate() // insert, update, delete sql을 실행
ResultSet rs = ps.executeQuery() // select를 실행. (0, 1건이 나올경우), 0건이상
3.2. Select일 경우
0,1건 Select일 경우
  • ResultSet은 select한 결과를 참조하는 인터페이스다.
    ps.executeQuery()가 실행되면, 실행된 결과는 DBMS에 있다.
  • 한건을 읽어온다. 한건이라는 것은 record를 말한다. 여기서 record란 select다음에 나온 컬럼들을 말한다.
  • 읽어오지 못하면 false를 반환하고, 읽어오면 true를 반환한후 다음 레코드를 참조한다.
if(rs.next()) { //한건의 record를 읽어온다. select role_id, description from ROLE where role_id = ?
int roleId = rs.getInt(1); // 첫번째 컬럼의 값을 읽어온다. 
String description = rs.getString(2); // 두번째 칼럼의 값을 읽어온다.
}

3.3 마지막 처리

ResultSet을 close(), PreparedStatement를 close(), Conntction을 close();
Finally를 하거나 try - with - resources구문을 이용하거나 한다.

Spring JDBC

기존 JDBC프로그래밍을 아주 간단하게 할 수 있도록 도와준다.
  • BeanPropertySqlParameterSource는 자동으로 Map객체를 만들어 준다.
  • JdbcTemplate, dataSource 이 핵심객체이다.
  • SimpleJdbcInsert는 insert를 쉽게 도와주는 객체이다. 
  • RowMapper는 한건의 recode를 쉽게 객체에 담아 줄수 있도록 도와준다.
    jdbc에서 RsultSet으로부터 칼럼별로 값을 읽어오는 것을 도와주는 것
  • 테이블에서 자동으로 id가 생성될 경우는 usingGeneratedKeyColumns를 사용한다.
  • where 조건문이 없을때 Collections.emptyMap()를 활용한다.
  • 자동으로 생성되는 id 는 executeAndReturnKey(params).intValue() 을 통해 pk를 반환할 수 있다.
  • Map형태로 Collections.singletonMap를 사용하여 jdbc에 바인딩 한다.
  • SqlParameterSource params = new BeanPropertySqlParameterSource(VO클래스)는 VO클래스에 있는 변수들을 자동으로 바인딩 해준다.
@Repository
public class RoleDao {
    // Spring JDBC는 기존 JDBC기존 프로그래밍을 아주 간단하게 할 수 있도록 도와준다.
    // JdbcTemplate, dataSource 이 핵심객체이다.
    private NamedParameterJdbcTemplate jdbc;
    // insert를 쉽게 도와주는 객체이다.
    private SimpleJdbcInsert insertAction;
    // RowMapper는 한건의 recode를 쉽게 객체에 담아 줄수 있도록 도와준다.
    // jdbc에서 RsultSet으로부터 칼럼별로 값을 읽어오는 것을 도와주는 것
    private RowMapper rowMapper = BeanPropertyRowMapper.newInstance(Role.class);

    public RoleDao(DataSource dataSource) {
        this.jdbc = new NamedParameterJdbcTemplate(dataSource);
        this.insertAction = new SimpleJdbcInsert(dataSource)
                .withTableName("ROLE"); //테이블명
//                .usingGeneratedKeyColumns("id"); // 자동으로 id가 생성될 경우
    }

    public List selectAll() {
        //  Collections.emptyMap() -> new HashMap 과 같음
        return jdbc.query("select role_id, description from ROLE order by role_id", Collections.emptyMap(), rowMapper);
    }

    public int insertRole(Role role){
        SqlParameterSource params = new BeanPropertySqlParameterSource(role);
        // 자동으로 id를 생성할 경우에는 아래와 같이 생성된 pk를 반환할 수 있다.
//        return insertAction.executeAndReturnKey(params).intValue();
        int count = insertAction.execute(params);
        return role.getRoleId();
    }

    public int deleteRole(int roleId){
        Map params = Collections.singletonMap("roleId", roleId);
        return jdbc.update("delete from ROLE where role_id = :roleId", params); //Map형식으로 활용 , :바인딩 처리
    }

    public int updateRole(Role role){
        SqlParameterSource params = new BeanPropertySqlParameterSource(role); //자동으로 map객체를 만들어 준다.
        return jdbc.update("update ROLE set description = :description where role_id = :roleId", params);
    }

    public Role selectRole(int roleId){
        Map params = Collections.singletonMap("roleId", roleId);
        try{
            Role role = jdbc.queryForObject("select role_id, description from ROLE where role_id = :roleId", params, rowMapper);
            return role;
        }catch(){
            return null;
        }
    }
}
Share:

0 개의 댓글:

댓글 쓰기