1. Spring JDBC란?
- JDBC 프로그래밍을 보면 반복되는 개발 요소가 있음
(드라이버 로드하고 Connection, Statement, ResultSet 객체 생성 등)
- 이러한 JDBC의 모든 저수준 세부사항을 Spring이 처리해줌
개발자가 해야 할 일
Spring JDBC 패키지
org.springframework.jdbc.core
- JdbcTemplate 및 관련 Helper 객체 제공
org.springframework.jdbc.datasource
- DataSource를 쉽게 접근하기 위한 유틸 클래스, 트랜잭션매니저 및 다양한 DataSource 구현을 제공
org.springframework.jdbc.object
- RDBMS 조회, 갱신, 저장 및 안전하고 재사용 가능한 객체 제공
org.springframework.jdbc.support
- jdbc,core 및 jdbc.object를 사용하는 JDBC 프레임워크를 지원
JdbcTemplate
- org.springframework.jdbc.core에서 가장 중요한 클래스
- 리소스 생성, 해지를 처리해서 연결을 닫는 것을 잊어 발생하는 문제 등을 피할 수 있도록 함
- Statement의 생성과 실행을 처리
- SQL조회, 업데이트, 저장 프로시저 호출, ResultSet 반복 호출 등을 실행함
/* Select 예제 */
// column 수 구하기
int rowCount = this.jdbcTemplate.queryForInt("select count(*) form t_actor");
// 변수 바인딩 사용하기
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt("select count(*) from t_actor where first_name = ?", "Joe");
// String 값으로 결과 받기
String lastName = this.jdbcTemplate.queryForObject("select last_name from t_actor where id = ?", new Object[] {1212L}, String.class);
/* Insert 예제 */
this.jdbcTemplate.update("insert into t_actor (first_name, last_name) values (?, ?)", "Leonor". "Watling");
/* Update 예제 */
this.jdbcTemplate.update("update t_actor set = ? where id = ?", "Banjo", 5276L);
/* Delete 예제 */
this.jdbcTemplate.update("delete from t_actor where id = ?", Long.valueOf(actorId));
2. Spring JDBC 실습 - 용어 정리
DTO란?
- Data Transfer Object
- 계층간 데이터 교환을 위한 Java Beans
- 일반적으로 DTO는 로직을 갖고 있지 않은 순수한 데이터 객체임
DAO란?
- Data Access Object
- 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 객체
- 보통 데이터베이스 조작하는 기능을 전담하는 목적으로 만들어짐
CoonectionPool이란?
- DB 연결은 비용이 많이 듦
- 커넥션 풀은 미리 커넥션을 여러 개 맺어둠
- 커넥션이 필요하면, 커넥션 풀에게 빌려서 사용한 후 반납함
DataSource
- 커넥션 풀은 경우에 따라 여러 개가 생성될 수 있음
- 그러한 커넥션 풀을 관리하는 목적으로 사용되는 객체
- DataSource를 이용해 커넥션을 얻어오고 반납하는 등의 작업을 수행함
3. Spring JDBC 실습 - DB 접속하기
# pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>daoexam</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>daoexam</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
# ApplicationConfig.java
package com.example.daoexam.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({DBConfig.class}) // 하나의 클래스에 모든 설정 정보를 갖고 있는 게 아니라, DB는 DB 관련 등 따로 나눠서 작성할 거임
public class ApplicationConfig {
}
- @Configuration: 설정 정보를 담은 클래스인 것을 알려주기
- @Import: 다른 설정 정보도 import 할 거임
# DBConfig.class
package com.example.daoexam.config;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement // 트랜잭션을 위해 필요
public class DBConfig {
private String driverClassName = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/connectdb?useUnicode=true&characterEncoding=utf8";
private String username = "connectuser";
private String password = "connectuser";
// DataSource 객체를 등록하기. 이미 작성되어 있는 DataSource를 사용하는 것
@Bean
public DataSource dataSource() {
// DataSource가 생성될 때 알아야 할 정보를 설정해줌
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
- @Configuration: 이것도 설정 정보를 담고 있음
- DB 정보 담기
- DataSource를 통해 DB에 대한 정보를 담아주기
- @Bean: 객체니까
# DataSourceTest.java
package com.example.daoexam.main;
import com.example.daoexam.config.ApplicationConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class DataSourceTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
DataSource ds = ac.getBean(DataSource.class);
Connection conn = null;
try {
conn = ds.getConnection();
if(conn != null) {
System.out.println("접속 성공");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
- DB에 잘 접속할 수 있는지 테스트하기 위해 main 함수 실행해보기
- ApplicationContext를 통해 Spring 컨테이너 생성하기
- 이때 ApplicationConfig.class에서 설정 정보 읽어오도록 하기
- DataSource 객체 얻어오기
- Connection 만들어서 접속 성공 여부 확인하기
4. Spring JDBC 실습 - SELECT ALL
# Role.java
package com.example.daoexam.dto;
public class Role {
private int role_id;
private String description;
public int getRole_id() {
return role_id;
}
public void setRole_id(int role_id) {
this.role_id = role_id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Role{" +
"role_id=" + role_id +
", description='" + description + '\'' +
'}';
}
}
- DTO 생성
# RoleDaoSqls.java
package com.example.daoexam.dao;
public class RoleDaoSqls {
public static final String SELECT_ALL = "SELECT role_id, description FROM role ORDER BY role_id";
}
- sql문 정의
# RoleDao.java
package com.example.daoexam.dao;
import com.example.daoexam.dto.Role;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.List;
import static com.example.daoexam.dao.RoleDaoSqls.*;
@Repository // Bean으로 등록하기. Dao는 repository 애노테이션을 붙여줌.
public class RoleDao {
private NamedParameterJdbcTemplate jdbc; // ?를 사용해서 바인딩하거나 결과값을 가져옴
private RowMapper<Role> rowMapper = BeanPropertyRowMapper.newInstance(Role.class); // DBMS와 JAVA의 이름 규칙을 맞춰줌
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
}
public List<Role> selectAll() {
return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper); // sql문의 바인딩할 값을 전달하기 위해 Collections.emptyMap();
}
}
selectAll()의 return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper);
첫번째 파라미터
static으로 정의한 select문을 import해줌
두번째 파라미터
sql문에 바인딩할 값이 있다면, 그 값을 전달할 목적으로 사용됨
세번째 파라미터
select 한 건 한 건의 결과를 저장. column의 값을 자동으로 dto에 담아줌. 생성한 dto를 리스트에 담아 반환해줌
BeanPropertyRowMapepr는 DBMS와 JAVA의 이름의 규칙을 맞춰주는 역할을 함
ex) role_id 와 roleId를 매칭
# ApplicationConfig.java
package com.example.daoexam.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan(basePackages = {"com.example.daoexam.dao"})
@Import({DBConfig.class}) // 하나의 클래스에 모든 설정 정보를 갖고 있는 게 아니라, DB는 DB 관련 등 따로 나눠서 작성할 거임
public class ApplicationConfig {
}
@ComponentScan으로 @Repository를 붙인 RoleDao 정보를 읽어오도록 함
# SelectAllTest.java
package com.example.daoexam.main;
import com.example.daoexam.config.ApplicationConfig;
import com.example.daoexam.dao.RoleDao;
import com.example.daoexam.dto.Role;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.List;
public class SelectAllTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleDao roleDao = ac.getBean(RoleDao.class);
List<Role> list = roleDao.selectAll();
for(Role role : list) {
System.out.println(role);
}
}
}
5. Spring JDBC 실습 - INSERT 및 UPDATE
INSERT
# RoleDao.java
package com.example.daoexam.dao;
import com.example.daoexam.dto.Role;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.List;
import static com.example.daoexam.dao.RoleDaoSqls.*;
@Repository // Bean으로 등록하기. Dao는 repository 애노테이션을 붙여줌.
public class RoleDao {
private NamedParameterJdbcTemplate jdbc; // ?를 사용해서 바인딩하거나 결과값을 가져옴
private SimpleJdbcInsert insertAction;
private RowMapper<Role> rowMapper = BeanPropertyRowMapper.newInstance(Role.class); // DBMS와 JAVA의 이름 규칙을 맞춰줌
// 생성자에서 jdbc, insertAction이 만들어지도록 함
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
this.insertAction = new SimpleJdbcInsert(dataSource).withTableName("role"); // 어떤 테이블에 insert할 지 알려줌
}
public List<Role> selectAll() {
return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper); // sql문의 바인딩할 값을 전달하기 위해 Collections.emptyMap();
}
public int insert(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role);
return insertAction.execute(params);
}
}
-SimpleJdbcInsert 객체
- Insert의 경우에는 primary key를 자동으로 생성해야 하는 경우가 생김 (해당 예제는 직접 넣어줌)
- withTableName()으로 어떤 테이블에 넣어줄지 명시
# JDBCTest.java
package com.example.daoexam.main;
import com.example.daoexam.config.ApplicationConfig;
import com.example.daoexam.dao.RoleDao;
import com.example.daoexam.dto.Role;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JDBCTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleDao roleDao = ac.getBean(RoleDao.class);
Role role = new Role();
role.setRoleId(300);
role.setDescription("CEO");
int count = roleDao.insert(role);
System.out.println(count + " 입력했습니다.");
}
}
UPDATE
# RoleDaoSqls.java
package com.example.daoexam.dao;
public class RoleDaoSqls {
public static final String SELECT_ALL = "SELECT role_id, description FROM role ORDER BY role_id";
public static final String UPDATE = "UPDATE role SET description = :description where role_id = :roleId";
}
# RoleDao.java
public int update(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role); // Map으로 바꿔줌. 이름 값 매칭
return jdbc.update(UPDATE, params);
}
# JDBCTest.java
role.setRoleId(500);
role.setDescription("Programmer");
count = roleDao.update(role);
System.out.println(count + " 수정했습니다.");
5. Spring JDBC 실습 - SELECT 및 DELETE
# RoleDaoSqls.java
public static final String SELECT_BY_ROLE_ID = "SELECT role_id, description FROM role WHERE role_id = :roleId";
public static final String DELETE_BY_ROLE_ID = "DELETE FROM role WHERE role_id = :roleId";
# RoleDao.java
package com.example.daoexam.dao;
import com.example.daoexam.dto.Role;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.example.daoexam.dao.RoleDaoSqls.*;
@Repository // Bean으로 등록하기. Dao는 repository 애노테이션을 붙여줌.
public class RoleDao {
private NamedParameterJdbcTemplate jdbc; // ?를 사용해서 바인딩하거나 결과값을 가져옴
private SimpleJdbcInsert insertAction;
private RowMapper<Role> rowMapper = BeanPropertyRowMapper.newInstance(Role.class); // DBMS와 JAVA의 이름 규칙을 맞춰줌
// 생성자에서 jdbc, insertAction이 만들어지도록 함
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
this.insertAction = new SimpleJdbcInsert(dataSource).withTableName("role"); // 어떤 테이블에 insert할 지 알려줌
}
public List<Role> selectAll() {
return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper); // sql문의 바인딩할 값을 전달하기 위해 Collections.emptyMap();
}
public int insert(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role);
return insertAction.execute(params);
}
public int update(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role); // Map으로 바꿔줌. 이름 값 매칭
return jdbc.update(UPDATE, params);
}
public Role selectById(Integer id) {
try {
Map<String, ?> params = Collections.singletonMap("roleId", id);
return jdbc.queryForObject(SELECT_BY_ROLE_ID, params, rowMapper);
} catch(EmptyResultDataAccessException e) {
return null;
}
}
public int deleteById(Integer id) {
Map<String, ?> params = Collections.singletonMap("roleId", id);
return jdbc.update(DELETE_BY_ROLE_ID, params);
}
}
- selectedById, deletedById 추가
SELECT
- 한 건 select이기에 queryForObject 사용
- 두번째 파라미터에 SqlParameterSource 사용해도 되지만 id만 필요하기에 직접 Map 형식의 params를 만들어서 넣어줌
- singletonMap은 값을 딱 한 건만 넣어서 쓸 때 사용
- 조건에 맞는 레코드가 없어 select할 수 없는 경우를 고려하여 예외처리
DELETE
- select와 동일
'강의 노트 > 웹 프로그래밍(풀스택)' 카테고리의 다른 글
[boostcourse] 3.9. Spring MVC - BE (2) (0) | 2023.06.26 |
---|---|
[boostcourse] 3.9. Spring MVC - BE (1) (0) | 2023.06.26 |
[boostcourse] 3.7. Spring Core - BE (0) | 2023.06.24 |
[boostcourse] 2.11. Web API - BE (0) | 2023.06.21 |
[boostcourse] 2.10. JDBC - BE (0) | 2023.06.20 |