
JDBC(Java Database Connectivity)란 Java 애플리케이션이 데이터베이스 드라이버를 통해 데이터베이스의 데이터를 생성, 수정, 삭제, 조회가 가능하도록 만들어진 API입니다.
라이브러리의 위치는 java.sql에 위치하고 있습니다.
JDBC는 크게 3가지로 분류됩니다.
자바와 데이터베이스의 연결을 담당하며 자바 애플리케이션에서 데이터베이스와 통신하는 가장 중심이 되는 메서드라고 생각할 수 있습니다.
데이터베이스와 연결을 위한 모든 정보가 Connection에서 사용됩니다.
Connection conn = DriverManager.getConnection({데이터베이스URL}, {유저 아이디}, {유저 비밀번호});
프로토콜://서버주소:포트번호/데이터베이스이름으로 구성됩니다. (MariaDB의 경우 jdbc:mariadb://localhost:3306/todo)연결된 Connection을 통해 질문할 내용을 작성하며 SQL을 사용하는 데이터베이스의 경우 SQL문이 Statement에 정보를 넣고 송신하게 됩니다.
데이터베이스와 통신하는 정보를 가지고 있는 객체입니다.
Statement stmt = conn.createStatement();
생성자를 통해 생성하는 것이 아닌 connection을 통해 새롭게 생성됩니다.
데이터베이스와 통신을 한 후 수신받는 데이터를 모아둔 객체입니다.
Statement객체를 통해 데이터베이스에게 얻는 정보를 모아둔 객체입니다.
ResultSet은 Statement에 있으며 getResultSet()으로 가져올 수 있습니다.
ResultSet rs = stmt.executeQuery({sql문});
try-catch-finally에서 finally에서 모든 변수를 close()처리를 하기 위해 try문 밖에 변수를 미리 선언합니다.
String sql = "SELECT " +
"id, title, state, create_at " +
"FROM " +
"todo " +
"WHERE " +
"delete_state = 0";
Connection conn = null;
Statement stmt = null;;
ResultSet rs = null;
List<TodoTitleDto> result = new ArrayList<>();
쿼리를 날릴 SQL문을 미리 선언해도 되지만 선언하지않고 바로 메서드에 넣어도 좋습니다.
변수가 아닌 다른 방법을 사용해도 괜찮습니다
사용할 DB와 통신하기 위해 해당 DB에 맞는 JDBC드라이버를 로딩합니다.
해당 드라이버가 존재하지 않을 경우 ClassNotFoundException이 발생합니다.
Class.forName("org.mariadb.jdbc.Driver");
DriverManager를 통해 인스턴스를 받아옵니다.
DriverManager는 해당 드라이버 라이브러리가 존재하는지 확인 후 반환해주는 역할
conn = DriverManager.getConnection("jdbc:mariadb://localhost:3306/todo");
해당 URL을 통해 해당 URL에 맞는 드라이버를 찾아 Connection을 생성합니다.
URL이 Null이거나 해당 URL을 통해 데이터베이스에 연결이 되지 않는다면 SQLException이 발생합니다.
Connection에서 DB에 쿼리를 보내기 위한 Statement 인스턴스를 받아옵니다.
stmt = conn.createStatement();
Statement를 생성한 후 SQL을 통해 데이터베이스와 통신합니다.
rs = stmt.executeQuery(sql);
excute를 통해 실행한 후 getResultSet()을 통해 결과를 받아올 수 있지만 이를 한번에 처리해주는 executeQuery를 사용하여 select문의 결과를 빠르게 가져올 수 있습니다.
rs는 Iterator처럼 사용할 수 있습니다.
결과를 한 줄씩 가져올 수 있으며 getInt와 같이 get{Type}을 사용하여 원하는 타입으로 가져올 수 있습니다.
next를 통해 다음 줄로 넘어갈 수 있습니다.
while (rs.next()) {
result.add(TodoTitleDto.builder()
.id(rs.getInt("id"))
.title(rs.getString("title"))
.state(rs.getInt("state"))
.createAt(rs.getTimestamp("create_at").toLocalDateTime())
.build());
}
사용한 인스턴스를 닫아줍니다.
conn.close();
stmt.close();
rs.close();
public List<TodoTitleDto> findAll() {
String sql = "SELECT " +
"id, title, state, create_at " +
"FROM " +
"todo " +
"WHERE " +
"delete_state = 0";
Connection conn = null;
Statement stmt = null;;
ResultSet rs = null;
List<TodoTitleDto> result = new ArrayList<>();
try {
Class.forName("org.mariadb.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mariadb://localhost:3306/todo");
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()) {
result.add(TodoTitleDto.builder()
.id(rs.getInt("id"))
.title(rs.getString("title"))
.state(rs.getInt("state"))
.createAt(rs.getTimestamp("create_at").toLocalDateTime())
.build());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.close();
stmt.close();
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return result;
}
Spring프레임워크에서 제공하는 JDBC API입니다.
기존 JDBC는 Connection을 만들기 위한 작업을 줄이고 Statement를 따로 꺼내는 작업을 할 필요없이 이를 단순화시킨 것입니다. 또한 Spring형태에 일부가 변형되었으며 JdbcTemplate을 통해 일괄적으로 모든 과정을 처리합니다.
쉽게 말해서 복잡한 과정을 단순화시켜 편의성을 높이고 Spring의 형태에 맞게 변형시킨 것을 말합니다.
NamedParameterJdbcTemplate와 같은 Template으로 편의성을 더 높이는 것도 존재합니다.

데이터베이스와 연결하기 위해 필요한 정보를 작성합니다.
반드시 이 경로로 작성해야하며 properties를 사용하여도 마찬가지입니다.
만약 yml을 이용하지 않는다면 DataSource객체에 setter를 통해 해당 정보를 직접 추가해주어야 합니다.
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3306/{데이터베이스 이름}
username: {유저 이름}
password: {유저 비밀번호}
아래 Configuration이 생성될 때 환경변수(yml)에 넣어둔 값을 이용하여 만들어진 DataSource를 사용하여 빈을 생성합니다.
@Configuration
@RequiredArgsConstructor
public class config {
private final DataSource dataSource;
@Bean
public TodoDao todoDao() {
return new TodoDaoImpl(dataSource);
}
}
만약 yml을 사용하지 않고 제작한다면
DataSource를 만들어 setter를 통해 해당 값을 직접 넣어주면 좋습니다.
여러 데이터베이스의 경우 이 방법을 통해 만들지는 못하겠지만 Value어노테이션과 yml에 커스텀 경로에 데이터베이스와 연결할 정보를 넣어두어 사용할 것 같습니다.
SQL문을 다룰 때 ?와 같이 빈칸을 만든 후 순서대로 값을 넣는 JdbcTemplate과 달리 :{변수명}을 통해 더 가독성도 높고 실수가 적은 NamedParameterJdbcTemplate를 사용하였습니다.
TodoDaoImpl(DataSource dataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
SQL문에 :{변수명}을 사용하여 해당 위치에 들어갈 값을 지정할 수 있습니다. Map<String, ?>를 통해 해당 변수에 값을 지정할 수 있습니다. Key가 변수명 Value가 변수가 가진 값입니다.
public List<TodoTitleDto> findByState(int state) {
Map<String, Object> map = new HashMap<>();
map.put("state", state);
String sql = "SELECT " +
"id, title, state, create_at " +
"FROM " +
"todo " +
"WHERE " +
"state = :state";
return jdbcTemplate.query(sql, map, todoTitleMapper);
}
위와 같이 map을 통해 사용할 수 있지만 스프링에서 주로 사용하는 DTO에 getter가 있다면 SqlParameterSource을 통해 객체로 바로 작성할 수 있습니다.
KeyHolder를 통해 추가된 데이터의 인덱스값을 가져올 수 있습니다.
public int postTodo(PostRequestDto postRequestDto) {
String sql = "INSERT INTO todo(title, content, create_at, update_at)" +
"VALUES(:title, :content, :createAt, :updateAt)";
SqlParameterSource param = new BeanPropertySqlParameterSource(Todo.builder()
.title(postRequestDto.getTitle())
.content(postRequestDto.getContent())
.createAt(LocalDateTime.now())
.updateAt(LocalDateTime.now())
.build());
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(sql, param, keyHolder);
return keyHolder.getKey().intValue();
}
@Getter
@AllArgsConstructor
public class PostRequestDto {
String title;
String content;
}
JPA만 사용하던 사람으로써 JDBC와 Spring JDBC에 대해 알아보는 시간을 가졌습니다.
인터넷 서칭을 하여도 자세하게 설명해주는 블로그가 없어 아쉬웠던 와중 직접 JDBC를 통해 간단한 DAO를 만들어보며 사용법을 간단하게나마 익혔습니다.
JDBC와 Spring JDBC는 동작과정 자체에는 큰 차이가 없었지만 Spring JDBC는 Spring JDBC이란 이름에 맞게 Spring구조에 굉장히 밀접하게 만들어져있어 기존의 JDBC를 Spring의 스타일로 추상화시켰다고 생각할 수 있었습니다.!