Sad Puppy 3 Spring JDBC(Java Database Connectivity) 1 :: 개발자 아지트

[JdbcTemplate]

: 스프링은 데이터베이스와의 연동을 쉽게 도와주는 여러 도구와 방식을 제공한다. 

JDBC는 Java Database Connectivity의 약어로, 자바에서 데이터베이스에 접속하도록 해주는 API이다. 

JdbcTemplate은 이 API를 잘 사용할 수 있도록 스프링에서 제공하는 템플릿 클래스이다. 

이는 스프링 JDBC의 핵심이고, 다른 고수준의 기능들도 결국 내부에서는 이를 활용한다. 

 

jdbcTemplate은 핵심 JDBC 작업 흐름에 기본적인 업무를 수행하고, 애플리케이션 코드는 SQL을 제공하고 결과를 추출하는 역할을 한다. 

 

사용 효과 

: 데이터베이스 연동 코드를 보다 간결하고 안정적으로 작성할 수 있다. 

 

JdbcTemplate 클래스의 제공 기능

:

1. SQL 쿼리 실행

2. statements 및 저장된 procedure all 업데이트

3. ResultSet 인스턴스를 반복하고 반환된 매개 변수 값 추출을 수행

4. JDBC 예외를 캡처하고, org.springframework.dao 패키지에 정의된 일반적이고 더 유용한 에외 계층으로 변환함

 


[Querying (SELECT)]

 

스프링에서 JdbcTemplate을 이용해 SELECT 쿼리를 실행할 수 있는 여러 방법을 제공한다. 

queryForObject, query, queryForList, queryForRowSet, queryForMap 등의 메서드를 통해 쿼리를 실행할 수 있다. 

 


[Querying for a Single Object]

 

JdbcTemplate의 queryForObject 메서드를 이용해 단일 객체를 조회할 수 있다. 

 

[Object with Count]

queryForObject의 첫 매개변수는 쿼리문이고, 두 번째 매개변수는 조회 결과를 매핑할 클래스 타입이다. 

 

public int count() {
        int rowCount = jdbcTemplate.queryForObject("select count(*) from customers", Integer.class);
        return rowCount;
    }

사용예시

 

queryForObject는 Spring Framework의 JdbcTemplate 클래스에서 제공하는 메서드로, SQL 쿼리를 실행하여 단일 결과 값을 반환할 때 사용된다. 이 메서드는 데이터베이스 쿼리를 수행하고, 쿼리 결과의 첫 번째 행을 특정 타입의 객체로 매핑하여 반환한다. 주로 다음과 같은 상황에서 사용된다. 

 

 

단일 값 반환: 쿼리 결과가 단일 값(예: COUNT, SUM 등)인 경우, 이를 적합한 자바 기본형 또는 객체로 반환한다.

String sql = "SELECT COUNT(*) FROM users WHERE active = ?"; int count = jdbcTemplate.queryForObject(sql, Integer.class, true);

 

 

 

객체 반환: 쿼리 결과를 자바 객체로 매핑하여 반환한다. 이 경우, RowMapper 또는 해당 클래스의 타입을 지정해야 한다. 여기서는 User라는 클래스의 객체를 반환한다. BeanPropertyRowMapper는 결과셋의 컬럼 이름과 User 클래스의 필드 이름을 자동으로 매핑해준다.

String sql = "SELECT id, name, email FROM users WHERE id = ?"; User user = jdbcTemplate.queryForObject(sql, new Object[]{1}, new BeanPropertyRowMapper<>(User.class));

 

효과

:

예외 처리: 쿼리 결과가 없거나 두 개 이상의 결과가 반환될 경우 EmptyResultDataAccessException 또는 IncorrectResultSizeDataAccessException이 발생한다. 

 

단순성과 편리함: 간단한 쿼리를 사용하여 단일 결과를 처리할 때 매우 유용하다. 

queryForObject는 복잡한 SQL 쿼리 결과를 간단하게 자바 객체로 매핑할 수 있게 해준다. 

 

[Object with Parameter]

 

queryForObject의 세 번째 매개변수를 이용해, 쿼리문에 바인딩할 파라미터를 전달할 수 있다. 

public String getLastName(Long id) {
        //TODO : 주어진 Id에 해당하는 customers의 lastName을 반환
        String lastName = jdbcTemplate.queryForObject("select last_name from customers where id = ?", String.class, id);

        return null;
    }

 

첫번째 인자는 쿼리문

두번째 인자는 SQL  쿼리문의 결과값의 반환값

세번째 인자는 쿼리문 속에 ?에 들어갈 값 

 

*반환 타입을 명시하는 이유는?

:queryForObject 메서드에서 반환 타입을 명시하는 이유는 SQL 쿼리의 결과를 자바 객체로 매핑하기 위해서이다. 데이터베이스에서 가져온 데이터를 자바 객체로 변환할 때, 어떤 타입으로 변환할지를 명확히 지정해줘야한다.

 

예를 들어, last_name이 VARCHAR 타입이라면 이를 자바의 String 타입으로 반환해야 한다. 이를 위해 String.class를 한다

 

반환 타입을 명시함으로써 컴파일 시점에 타입 체크를 할 수 있어, 타입 안전성을 보장할 수 있다.

반환 타입을 명시함으로써 코드를 읽는 사람이 쿼리 결과가 어떤 타입인지 명확하게 이해할 수 있다. 코드의 가독성을 높이고, 유지보수를 쉽게 만들어주는 효과를 받을 수 있다. 

 

 

[Object with RowMapper]

 

queryForObject의 두 번째 매개변수에 RowMapper을 전달해 조회 결과를 매핑할 수 있다. 

 

*RowMapper란?

 

:Spring Framework에서 제공하는 인터페이스로, 데이터베이스 쿼리의 결과인 ResultSet을 자바 객체로 매핑해준다.  

RowMapper를 통해 ResultSet의 각 행(row)을 원하는 자바 객체로 변환할 수 있다. 데이터베이스의 테이블에서 데이터를 조회한 후, 해당 데이터를 특정 클래스의 객체로 매핑할 수 있다.

 

 public Customer findCustomerById(Long id) {
        String sql = "select id, first_name, last_name from customers where id = ?";

        Customer customer = jdbcTemplate.queryForObject(
                sql,
                (resultSet, rowNum) -> {
                    Customer customer1 = new Customer(
                            resultSet.getLong("id"),
                            resultSet.getString("fist_name"),
                            resultSet.getString("last_name")
                    );
                    return customer1;
                }, id);

        return null;
    }

RowMapper의 기능을 직접 람다식으로 구현한 것

 

resultSet에서 데이터베이스 쿼리의 결과를 담고 있다.

rowNum은 결과셋에서 몇 번째 행인지를 나타내는 숫자이다. 해당 코드에서는 사용되지 않았다. (작성은 했으나, 쓰이지 않았음)

 

getLong(), getString()등을 통해 각 열의 값을 가져온다. 

 

RowMapper는 데이터베이스에서 가져온 결과(ResultSet)를 자바 객체로 변환하는 인터페이스이다. 

이를 통해, 데이터베이스의 한 행을 자바 객체로 매핑하는 방법을 정의할 수 있다. 

 

queryForObject함수의 두번째 인자 값이 RowMapper의 mapRow메서드를 람다식으로 구현한 것이다. 

 

*mapRow()란?

: ResultSet의 각 행을 읽어들이고, 그 데이터를 사용해 자바 객체를 생성한다. 

 

결과적으로, queryForObject의 두번째 인자 값이 객체로 표현함으로써 저런식의 반환 타입을 명시하는것이다. 


[Querying for a List]

 

[List with RowMapper]

: jdbcTemplate의 query 메서드를 이용해 여러 객체를 조회할 수 있다. 두 번째 매개변수에 RowMapper을 전달해 조회 결과를 매핑할 수 있다. 

public List<Customer> findAllCustomers() {
        String sql = "select id, first_name, last_name from customers";

        List<Customer> customers = jdbcTemplate.query(
                sql,
                (resultSet, rowNum) -> {
                    Customer customer = new Customer(
                        resultSet.getLong("id"),
                        resultSet.getString("first_name"),
                        resultSet.getString("last_name")
                    );
                    return customer;
                });

        return customers;
    }

 

queryForObject의 세 번째 매개변수를 이용해 쿼리문에 바인딩할 파라미터를 전달할 수 있다. RowMapper의 경우, 별도 선언해 사용할 수 있다. 

 

public List<Customer> findAllCustomers() {
        String sql = "select id, first_name, last_name from customers";

        List<Customer> customers = jdbcTemplate.query(
                sql,
                (resultSet, rowNum) -> {
                    Customer customer = new Customer(
                        resultSet.getLong("id"),
                        resultSet.getString("first_name"),
                        resultSet.getString("last_name")
                    );
                    return customer;
                });

        return customers;
    }

 

이렇게 쓰면 쿼리로 불러와서 만든 customer이 customers에 들어간다. 


(추가적으로 공부하기)


[Updating (INSERT, UPDATE, and DELETE)]

: jdbcTemplate을 이용해, INSERT, UPDATE, DELETE 쿼리를 실행할 수 있다. 

 

쿼리문을 통해 insert를 할 때는 update함수를 이용한다. 

 

[Update (INSERT)]

 

 public void insert(Customer customer) {
        String sql = "insert into customers (first_name, last_name) values (?, ?)";
        jdbcTemplate.update(sql, customer.getFirstName(), customer.getLastName());
    }

 

insert into customers (first_name, last_name) values (?, ?) 
 
위 구문에 대한 설명
: 해당 SQL 구문은 customers 데이터베이스 테이블에 새로운 데이터를 삽입하는 INSERT 문이다. 
데이터베이스의 first_name과 last_name이라는 두 개의 열에 값을 삽입하겠다는 구문이다. 
 
values (?, ?)에 삽입할 값을 지정한다. (?는 플레이스 홀더 라고 하고, 나중에 코드에서 구체적인 값으로 대체된다)
 
 

[Update (DELETE)]

 

    public int delete(Long id) {
        //todo: id에 해당하는 customer를 지우고, 해당 쿼리에 영향받는 row 수반환하기
        String sql = "delete into customers where id = ?";
        Integer affectedRow = jdbcTemplate.update(sql, Long.valueOf(id));

        return affectedRow;
    }

 

jdbcTemplate.update() 메서드는 해당 쿼리에 의해 영향을 받은 행의 수를 반환한다. 

 

Long.valueOf()란?

:  Java에서 타입 변환을 수행하는 메서드 중 하나로, 주로 원시 타입 long 또는 int 값을 객체 타입 Long으로 변환하는 데 사용된다. (형변환 및 성능 최적화에 도움됨)

 

Long.valueOf(id)를 사용하는 이유는

: jdbcTemplate.update 메서드는 SQL 쿼리의 파라미터로 객체 타입을 받기 때문에 id 값이 long 또는 int 타입일 경우, 이를 명시적으로 Long 타입으로 변환하기 위해이다. 

 

 

[KeyHolder]

: 이는 Spring Framework에서 제공하는 인터페이스로, JdbcTemplate을 통해 데이터베이스에 새로운 행을 삽입하고 하고 자동으로 생성된 primary key를 가져올 수 있다. 

 

사용방법

: update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) 메서드를 통해 사용 

 

public Long insertWithKeyHolder(Customer customer) {
        String sql = "insert into customers (first_name, last_name) values (?, ?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(connection -> {
            PreparedStatement ps = connection.prepareStatement(
                    sql,
                    new String[]{"id"});
            ps.setString(1, customer.getFirstName());
            ps.setString(2, customer.getLastName());
            return ps;
        }, keyHolder);

        Long id = keyHolder.getKey().longValue();

        return keyHolder.getKey().longValue();
    }

 

connection은 객체이고, jdbc api에서 제공하는 데이터베이스와 상호작용 하기 위한 클래스이다. 

이는 자바 애플리케이션과 데이터베이스 간의 연결을 관리하는 객체이다. 

sql 쿼리 실행을 위해 Statement나 PreparedStatement 객체를 생성한다. 

 

prepareStatement 객체는  jdbc api에서 제공하는 데이터베이스와 상호작용 하기 위한 클래스이며, SQL문을 미리 준비해두기 위해 사용함. ? 를 사용하여 바인딩 한다. 

 

connection.prepareStatement(sql, new String[] {"id"});

 

해당 구문의 첫번째 인자는 sql문, 두번째 인자는 반환값으로 받을 값의 타입 설정 및, 어떤 값을 받을지에 대한 설정을 하는 자리이다. 위 구문은 id열을 String 배열로 받겠다는 의미이다. 

 

 

ps.setString(1, customer.getFirstName()); 

 

해당 구문의 첫번째 인자는 sql문에 있는 ?의 위치를 의미한다. 만약 1이라면, 첫번째 ?를 의미한다. 

두번째 인자는 삽입할 값을 의미한다. 

 

ps가 리턴된 후, keyHolder에는 해당 ps를 처리하고 난 후 발생한 key값을 저장하게된다. 

 

 

 

 

'프레임워크 > Spring' 카테고리의 다른 글

Spring Core 2  (0) 2024.08.14
Spring Core 1  (0) 2024.08.14
Spring mvc 4 (MVC Configuration, View Controller, Interceptor, Argument Resolver)  (0) 2024.08.06
Spring mvc 3 (예외처리)  (0) 2024.08.05
Spring mvc 2 (CRUD API)  (0) 2024.08.05

+ Recent posts