Spring JdbcTemplate

김정훈·2024년 7월 7일

Spring

목록 보기
5/24

JdbcTemplate

1. 설치 및 설정

1) spring-jdbc

2) tomcat-jdbc

  • 커넥션 풀
    • 미리 연결 객체를 여러개 생성해서 필요할때마다 빌려주고, 회수하는 방식
    • 반응성, 성능 향상의 효과
    • javax.sql.DataSource

3) 의존성

  • spring-jdbc
  • tomcat-jdbc
  • spring-context
  • lombok
  • spring-test
  • ojdbc11
ext{
    springVersion = '6.1.10'
}

dependencies {
    implementation "org.springframework:spring-context:$springVersion" //Spring context
    implementation "org.springframework:spring-jdbc:$springVersion" //Spring jdbc
    implementation 'org.apache.tomcat:tomcat-jdbc:10.1.25' //Tomcat jdbc
    runtimeOnly 'com.oracle.database.jdbc:ojdbc11:23.4.0.24.05' //ojdbc11
    compileOnly 'org.projectlombok:lombok:1.18.34' //lombok
    annotationProcessor 'org.projectlombok:lombok:1.18.34' //lombok
    implementation 'org.slf4j:slf4j-api:2.0.13' //slf4j api
    implementation 'ch.qos.logback:logback-classic:1.5.6' //logback 구현체

    testImplementation "org.springframework:spring-test:$springVersion" //Spring test
 }

4) HikariCp

2. DataSource 설정

  • javax.sql.DataSource
  • 연결 유효성 체크 관련 메서드 - Tomcat JDBC

3. JdbcTemplate을 이용한 쿼리실행

1) query()

데이터를 조회하는 SQL(SELECT)

  • List query(String sql, RowMapper rowMapper)
  • List query(String sql, Object[] args, RowMapper rowMapper)
  • List query(String sql, RowMapper rowMapper, Object... args)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppCtx.class)
public class Ex01 {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void test2(){
        List<Member> members = jdbcTemplate.query("SELECT * FROM MEMBER", new BeanPropertyRowMapper<Member>(){
            @Override
            public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
                return Member.builder()
                        .seq(rs.getLong("SEQ"))
                        .email(rs.getString("EMAIL"))
                        .password(rs.getString("PASSWORD"))
                        .userName(rs.getString("USER_NAME"))
                        .regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
                        .build();
            }
        });
        members.forEach(System.out::println);
    }


	@Test
    void test2(){
        List<Member> members = jdbcTemplate.query("SELECT * FROM MEMBER",
                (rs, num) -> Member.builder()
                        .seq(rs.getLong("SEQ"))
                        .email(rs.getString("EMAIL"))
                        .password(rs.getString("PASSWORD"))
                        .userName(rs.getString("USER_NAME"))
                        .regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
                        .build());
        members.forEach(System.out::println);
    }
}

2) queryForObject()

단일 데이터 조회, 조회 데이터가 반드시 1개이어야한다. 그렇지 않으면 예외가 발생
그렇기 때문에 try catch를 사용해서 사용

	@Test
    void test3(){
        String email = "user01@test.org";
        Member member = jdbcTemplate.queryForObject("SELECT * FROM MEMBER WHERE EMAIL = ?",
                (rs, num) -> Member.builder()
                .seq(rs.getLong("SEQ"))
                .email(rs.getString("EMAIL"))
                .password(rs.getString("PASSWORD"))
                .userName(rs.getString("USER_NAME"))
                .regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
                .build(), email);

        System.out.println(member);
    }

count()

@Test
    void test4(){
        int total = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM MEMBER", Integer.class);
        System.out.println(total);
    }

3) update()

데이터를 변경을 가하는 SQL(INSERT, DELETE, UPDATE : 반환값 - 반영된 레코드 갯수)

  • int update(String sql)
  • int update(String sql, Object... args)
    • PreparedStatement 방식으로 쿼리 작성
    • 값 바인딩 (?, ?)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppCtx.class)
public class Ex01 {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void test1(){
        String sql = "INSERT INTO MEMBER (SEQ, EMAIL, PASSWORD, USER_NAME) VALUES (SEQ_MEMBER.NEXTVAL, ?, ?, ?)";
        int result = jdbcTemplate.update(sql,"user03@test.org","123456","사용자03");
        System.out.println(result);
    }

}

4) mapper메서드화 해서 사용하기

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppCtx.class)
public class Ex01 {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void test2(){
        List<Member> members = jdbcTemplate.query("SELECT * FROM MEMBER", this::mapper); //메서드참조 이용
        members.forEach(System.out::println);
    }

    @Test
    void test3(){
        String email = "user01@test.org";
        Member member = jdbcTemplate.queryForObject("SELECT * FROM MEMBER WHERE EMAIL = ?", this::mapper, email);
        System.out.println(member);
    }

    private Member mapper(ResultSet rs, int num) throws SQLException {
        return Member.builder()
                .seq(rs.getLong("SEQ"))
                .email(rs.getString("EMAIL"))
                .password(rs.getString("PASSWORD"))
                .userName(rs.getString("USER_NAME"))
                .regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
                .build();
    }

}

5) 로거

slf4j-api
logback classic
의존성

4. PreparedStatementCreator

Connection 객체 매개변수로 정의되어 있는 메서드를 통해서 사용 가능
증감 번호 형태의 PK를 조회

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppCtx.class)
public class Ex01 {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Autowired
    private DataSource dataSource;

    @Test
    void test1(){
        KeyHolder keyHolder = new GeneratedKeyHolder();
        int result = jdbcTemplate.update(con -> {
                String sql = "INSERT INTO MEMBER (SEQ, EMAIL, PASSWORD, USER_NAME)"+
                        "VALUES (SEQ_MEMBER.NEXTVAL, ?, ?, ?)";
                PreparedStatement pstmt = con.prepareStatement(sql, new String[]{"SEQ"});
                pstmt.setString(1,"user05@test.org");
                pstmt.setString(2,"12345678");
                pstmt.setString(3,"사용자05");
                return pstmt;
        }, keyHolder);
        System.out.println(result);
        Number key = keyHolder.getKey();
        long seq = key.longValue();
        System.out.println(seq);
    }
}

5. INSERT 쿼리 실행 시 KeyHolder를 이용해서 자동 생성 키값 구하기

6. 스프링의 익셉션 변환 처리

각 연동 기술에 따라 발생하는 익셉션을 스프링이 제공하는 익셉션으로 변환함으로써 다음과 같이 구현 기술에 상관없이 동일한 코드로 익셉션을 처리할 수 있게 된다.

SQLExcpetion, HibernateException, PersistenceException 👉 DataAccessException (RuntimeException)

7. 트랜잭션 처리

수동관리
@Transactional : 클래스명 위, 메서드명 위

여러 쿼리를 하나로 묶어 하나의 트랜잭션을 처리함.
쿼리 중 하나라도 오류가 발생한다면 모든 쿼리를 커밋하지않는다.

Connection
	setAutoCommit(false)
SQL1...
SQL2...
SQL3...
Connection
	commit();
profile
안녕하세요!

0개의 댓글