JDBC는 Driver loading -> 접속 -> 객체 생성 -> 쿼리 받기 -> 결과 꺼내기 -> 객체 닫기 ..처럼 반복되는 코드가 많다. 이것을 스프링 프레임워크가 처리해준다.

반드시 개발자가 알려줘야 하는 작업들만 하면 된다. 나머지는 Spring framework가 해준다.
Select 예제
int rowCount = this.jdbcTemplate.queryForInt("select count(*) from actor");
변수 바인딩 예제
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt("select count(*) from actor where first_name =?", "Joe");
String 결과 받기 예제
String lastName = this.jdbcTemplate.queryForObject("select last_name from actor where id = ?", new Object[]{1212L}, String.class)
한 행 조회 예제
Actor actor = this.jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
new Object[]{1212L},
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
queryForObject을 사용하고 RowMapper 메서드를 사용하여 가져온다.
ConnectionPool을 관리하는 역할을 한다. Connection을 얻고 반납하는 역할을 맡는 객체이다.

DB를 연결해보자.
@Configuration
@Import({DBConfig.class})
public class ApplicationConfig {
public static void main(String[] args) {
}
}
Config package안에 ApplicationConfig를 생성한다. 여기에서는 프로그램을 만들면서 필요한 Bean의 관리를 해준다. 대신 DB에 관한 내용은 따로 관리하기로 했다. Import 어노테이션을 통해, ApplicationConfig가 실행될 때, DBConfig도 실행되도록 한다.
@Configuration
@EnableTransactionManagement
public class DBConfig {
private String url = "jdbc:mysql://localhost:3306/connectdb?useUnicode=true&characterEncoding=utf8";
private String username = "connectuser";
private String password = "connect123!@#";
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
DBConfig를 작성하였다.
DB 접속 시 필요한 url, name, pw를 변수로 지정한다.
dataSrouce를 bean으로 등록한다.
package kr.or.connect.main;
import java.sql.Connection;
import javax.sql.DataSource;
import kr.or.connect.config.ApplicationConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
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("Connection is created");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(conn != null) {
try{
conn.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
}
}
연결이 잘 되는지 확인하기 위해 /main/DataSourceTest 파일을 만든다. 여기에서는 DB 연결에 필요한 모든 객체를 열고, Bean을 가져와 연결이 잘 되는지 확인하고 있다.
DBConfig.class가 아닌 ApplicationConfig.class를 가져와야한다. 왜냐하면 ApplicationConfig.class가 실행되면 DBConfig.class도 자동으로 같이 실행되기 때문에 !!
아니면 쓸데없이 2번 호출하는 불상사가 일어난다.
이렇게 하면 DB 연결이 성공하는 것을 알 수 있다.
package kr.or.connect.dto;
public class Role {
private int roleId;
private String description;
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", description='" + description + '\'' +
'}';
}
}
dto 패키지 안에 Role 클래스를 만들고 getter, setter, toString()을 만들어주었다.
이때, DB를 보면 role_id라고 컬럼명을 붙여놨기 때문에 여기서는 roleId라고 적어줘야 제대로 변환이 된다.
package kr.or.connect.dao;
public class RoleDaoSqls {
public static final String SELECT_ALL = "select role_id, description from role order by 1";
}
dao 패키지 안에 RoleDaoSqls를 만들었다.
앞으로 SQL문을 작성할 때는 static으로 이 클래스 안에 작성하여 유지보수가 쉽도록 한다.
@Repository
public class RoleDao {
private NamedParameterJdbcTemplate jdbc;
private RowMapper<Role> rowMapper = BeanPropertyRowMapper.newInstance(Role.class);
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
}
public List<Role> selectAll() {
return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper);
}
}
dao 패키지 안에 RoleDao 클래스를 생성하였다.
Repository 어노테이션을 붙여서 나중에 빈을 읽을 수 있도록 한다.
여기서는 DB 내용을 가져오도록 작성하였다. NamedParameterJdbcTemplate는 파라미터를 직접 넣을 수 있도록 해주는 template이다.
jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper)는 SQL에 바인딩 할 값이 있을 때 바인딩할 값을 전달할 목적으로 사용한다. rowMapper는 column의 값을 자동으로 담아준다.
query() 메서드는 결과가 여러개 있을 때 내부적으로 반복하면서 DTO를 생성한다. 생성한 DTO를 list에 담아주는 역할을 한다.
BeanPropertyRowMapper가 SQL의 role_id와 roleId를 같게 만들어주는 메소드이다.
public class SelectAllTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleDao roleDao = (RoleDao) ac.getBean(RoleDao.class);
List<Role> roleList = roleDao.selectAll();
for(Role role : roleList){
System.out.println(role);
}
}
}
main 패키지 안에 SelectAllTest 클래스를 생성하여 Bean을 가져오고, 데이터가 제대로 불러와지는지 확인한다.
public class RoleDaoSqls {
public static final String SELECT_ALL = "select role_id, description from role order by 1";
public static final String UPDATE = "update role set description= :description where ROLE_ID= :roleId";
}
RoleDaoSqls에 UPDATE 명령어를 작성하였다. :description 이 부분은 ?와 같은 의미로, 변수명처럼 사용한다는 의미이다.
ROLE_ID, roleId가 중요하다...... 매핑될 때 자동으로 될 수 있게 ID, id 이런게 아니라 Id로 입력해줘야 한다.
package kr.or.connect.dao;
@Repository
public class RoleDao {
private NamedParameterJdbcTemplate jdbc;
private SimpleJdbcInsert insertAction;
private RowMapper<Role> rowMapper = BeanPropertyRowMapper.newInstance(Role.class);
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
this.insertAction = new SimpleJdbcInsert(dataSource).withTableName("role");
}
public List<Role> selectAll() {
return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapper);
}
public int insert(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role);
return insertAction.execute(params);
}
public int update(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role);
return jdbc.update(UPDATE, params);
}
}
RoleDao.java 안에 insert와 update 메소드를 추가한다.
BeanPropertySqlParameterSource는 객체 role을 받아서, SQL문법에 맞게 바꿔준다. (roleId -> role_id)
insert는 SQL 명령어가 필요없고, 들어온 객체를 바로 실행시켜주면 된다.
update의 경우에는 입력된 UPDATE 명령어에 role 객체 값을 파라미터로 주고 있다. 이렇게 하면 내가 입력한 값이 자동으로 UPDATE 명령어 안에 매핑된다.
public class JDBCTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleDao roleDao = (RoleDao) ac.getBean("roleDao");
Role role = new Role();
role.setRoleId(500);
role.setDescription("Programmer");
int count = roleDao.insert(role);
System.out.println(count + " 입력하였습니다.");
int count = roleDao.update(role);
System.out.println(count);
}
}
입력 값을 확인하면 된다.
public static final String DELETE_BY_ROLE_ID = "delete from role where ROLE_ID= :roleId";
RoleDaoSqls 에 DELETE_BY_ROLE_ID를 추가하였다.
public int deleteById(Integer id) {
Map<String, ?> params = Collections.singletonMap("roleId", id);
return jdbc.update(DELETE_BY_ROLE_ID, params);
}
RoleDao에 id를 넣으면 삭제될 수 있도록 메소드를 구현하였다.
singleton을 통해 값이 1개 들어갈 때를 사용할 수 있다.
int count = roleDao.deleteById(102);
System.out.println(count);
테스트를 통해 제대로 실행되는 것을 확인할 수 있다.