JDBC 란 무엇일까?
JDBC는 Java 프로그램이 데이터베이스와 상호작용할 수 있도록 해주는 API 이다. 이를 통해 SQL쿼리 를 실행하고, 결과를 처리할 수 있다.
1. JDBC의 기본구성 요소
JDBC 드라이버, Connection, Statement, ResultSet 4가지가 있다.
1. JDBC 드라이버: 데이터베이스와 Java 애플리케이션 간의 통신을 담당하는 소프트웨어이다. (처음 데이터베이스와 연결할때, 드라이버를 다운로드 및 설정함)
2. Connection: 데이터베이스와의 연결을 나타내는 객체이다. JDBC-DB에서 데이터베이스와 연결을 나타내는 '인터페이스'이다. 이 인터페이스를 구현한 클래스들이 실제로 데이터베이스와의 연결을 관리한다.
Connection 객체를 생성하는 법
예시코드)
import java.sql.Connection;
import java.sql.DriverManager;
Connection connection = null; //Connection 은 java.sql 라이브러리에 만들어져 있다.
// JDBC 드라이버 로드
Class.forName("com.mysql.cj.jdbc.Driver");
// Connection 객체 생성
connection = DriverManager.getConnection(url, user, password);
2-1. JDBC 드라이버 로드: 위의 코드의 경우에는 Class.forName("com.mysql.cj.jdbc.Driver") 를 통해 MySQL 드라이버를 메모리에 불러왔다.
2-2. Connection 생성: DriverManager.getConnection(url, user, password) 부분을 통해 DB와 연결하고 Connection 객체를 생성한다.
3. Statement: SQL 쿼리를 실행하기 위한 객체이다. 일반적으로 Statement, PreparedStatement, CallableStatement 등이 있다.
3-1. Statement의 종류
- Statement : 일반적인 SQL 쿼리를 실행하는 데 사용된다. 주로 간단한 쿼리나 동적으로 변경되지 않는 쿼리에 적합하다.
- PreparedStatement : 미리 컴파일된 SQL 쿼리를 실행하는 데 사용된다. 파라미터를 바인딩 할 수 있어 성능이 향상되고 SQL 인젝션 공격에 대한 보안이 강화된다.
- CallableStatement : 저장 프로시저를 호출하는 데 사용된다. DB에 정의된 저장 프로시저를 실행할 수 있다.
여기서 파라미터 바인딩이란?
SQL 쿼리에서 동적으로 값을 설정하는 방법!
쿼리와 데이터 값을 분리하여 쿼리를 미리 컴파일 하고, 값만 나중에 바인딩 하는 방식이다. 장점은 성능 향상과 보안 강화이다.
예시)
String sql = "SELECT * FROM users WHERE age > ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 18); // 첫 번째 ?에 18을 바인딩
위의 예시에서 ?는 파라미터 자리 표시자이고, 나중에 setInt 메서드를 사용해서 실제값을 설정했다.
이렇게하면 쿼리의 구조가 변경되지 않아서, 데이터 값이 쿼리와 혼합되지 않는다.
여기서 SQL 인젝션 공격이란?
악의적인 사용자가 입력값을 조작하여 쿼리를 변조하고 데이터베이스에 대한 불법적인 접근을 시도 하는 공격이다. 주로 사용자의 입력값을 직접 SQL 쿼리에 포함시킬 때 발생한다.
예시)
자바에 다음과 같이 코드를 작성한다고 하자
String username = "user_input"; // 사용자가 입력한 값
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
여기서 username에 사용자가 ' OR '1'='1 과 같은 값을 입력하면, 최종 쿼리는
SELECT * FROM users WHERE username = '' OR '1'='1'
이렇게 된다. 이 쿼리는 항상 참이 되어서, 모든 사용자의 정보를 반환하게 된다. 그러면 DB의 모든 정보가 노출될 수 있다.
결론적으로 파라미터 바인딩을 통해 SQL 쿼리를 안전하고 효율적으로 실행할 수 있고, SQL 인젝션
공격에 대한 방어가 강화된다. 항상 사용자 입력값은 신뢰할 수 없으므로, PreparedStatement 같은
한전한 방법을 사용하는것이 좋다.
여기서 저장 프로시저란?
DB내에 저장된 SQL 코드 블록으로, 특정 작업을 수행하기 위해 미리 작성된 SQL 쿼리의 집합이다. 일반적으로 자주 사용하는 비즈니스 로직이나 복잡한 쿼리를 캡슐화하여, 애플리케이션에서 재사용할 수 있도록 만들어준다. 즉, Java의 라이브러리에 만들어져있는 메서드 같은것.
예시)
저장프로시저의 정의
DELIMITER //
CREATE PROCEDURE GetUserById(IN userId INT)
BEGIN
SELECT * FROM users WHERE id = userId;
END //
DELIMITER ;
IN userId INT 부분은 프로시저에 입력 파라미터를 정의하는 부분이고,
BEGIN 과 END 블록 안에 실행할 SQL 쿼리를 작성.
이렇게 하면 Java의 메서드 같이 호출하여 사용 가능하다.
3-2. Statement 사용 예시
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class StatementExample {
public static void main(String[] args) {
// 데이터베이스 연결 정보 설정
String url = "jdbc:mysql://localhost:3306/mydatabase"; // JDBC URL
String user = "username"; // 데이터베이스 사용자 이름
String password = "password"; // 데이터베이스 비밀번호
Connection connection = null; // Connection 객체 선언
Statement statement = null; // Statement 객체 선언
ResultSet resultSet = null; // ResultSet 객체 선언
try {
// JDBC 드라이버 로드
Class.forName("cohttp://m.mysql.cj.jdbc.Driver"); // MySQL JDBC 드라이버 로드
// 데이터베이스 연결 생성
connection = DriverManager.getConnection(url, user, password); // 연결 객체 생성
statement = connection.createStatement(); // Statement 객체 생성
// SQL 쿼리 작성
String sql = "SELECT * FROM users"; // 'users' 테이블에서 모든 레코드 선택하는 쿼리
// SQL 쿼리 실행 및 결과 저장
resultSet = statement.executeQuery(sql); // SELECT 쿼리 실행
// 결과 처리
while (resultSet.next()) { // 결과 집합을 반복
int id = resultSet.getInt("id"); // 'id' 컬럼의 값 가져오기
String name = resultSet.getString("name"); // 'name' 컬럼의 값 가져오기
System.out.println("ID: " + id + ", Name: " + name); // 결과 출력
}
} catch (ClassNotFoundException e) {
// JDBC 드라이버가 발견되지 않았을 때의 예외 처리
e.printStackTrace(); // 예외 출력
} catch (SQLException e) {
// SQL 관련 예외 처리
e.printStackTrace(); // 예외 출력
} finally {
// 자원 해제
try {
if (resultSet != null) resultSet.close(); // ResultSet 닫기
if (statement != null) statement.close(); // Statement 닫기
if (connection != null) connection.close(); // Connection 닫기
} catch (SQLException e) {
e.printStackTrace(); // 예외 출력
}
}
}
}
4. ResulsSet: 쿼리 결과를 저장하는 객체.
2. JDBC의 작동 방식
1. 드라이버 로드: JDBC 드라이버를 메모리에 로드한다.
2. Connection 생성: 데이터베이스에 연결을 생성한다
3. Statement 생성: SQL 쿼리를 실행하기 위한 Statement 객체를 생성
4. 쿼리 실행: 쿼리를 실행하고 결과를 받아온다
5. 결과 처리: ResultSet을 사용하여 결과를 처리한다.
6. 연결 종료: 사용이 끝난 후 연결을 종료한다.
3. JDBC의 장점과 단점
장점:
1. 표준화: JDBC는 Java의 표준 API로, 다양한 데이터베이스에 쉽게 연결할 수 있습니다.
2. 유연성: SQL 쿼리를 직접 작성할 수 있어 복잡한 데이터 조작이 가능합니다.
3. 트랜잭션 지원: 데이터베이스 트랜잭션을 처리할 수 있습니다.
트랜잭션이란?
데이터베이스에서 수행되는 하나의 작업단위이다. 여러 개의 작업을 하나의 묶음으로 처리한다.
트랜잭션은 반드시 "모두 성공하거나, 모두 실패해야 한다" 라는 특징을 가지고 있다. 쉽게 말해, 트랜잭션 내의 모든 작업이 성공하면 결과를 반영하고, 하나라도 실패하면 이전 상태로 되돌린다.
트랜잭션의 주요 특징
1. 원자성(Atomicity):
트랜잭션은 "모두 또는 전혀"라는 원칙을 따른다. 즉, 모든 작업이 성공해야만 데이터베이스에
반영되고, 하나라도 실패하면 전체가 취소된다.
2. 일관성(Consistency):
트랜잭션이 완료되면 데이터베이스의 상태가 일관된 상태로 유지되어야 한다. 트랜잭션
전과 후의 데이터는 정합성을 유지해야 한다.
3. 고립성(Isolation):
여러 트랜잭션이 동시에 실행될 때, 각 트랜잭션은 서로의 작업에 영향을 주지 않도록
격리되어야 한다. 한 트랜잭션의 중간 결과는 다른 트랜잭션에서 볼 수 없다.
4. 지속성(Durability):
트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 저장되어야 하며, 시스템 장애가
발생하더라도 잃어버리지 않아야 한다.
트랜잭션의 예시
가장 흔한 예로, 은행에서 돈을 이체하는 경우를 생각해보자.
1. 돈을 보내는 사람의 계좌에서 금액 차감(작업 A)
2. 받는 사람의 계좌에 금액 추가(작업 B)
여기서 두 작업이 모두 성공해야만 이체가 완료된다. 만약 작업 A에서 문제가 발생해 돈을 차감했지만, 작업 B에서 오류가 발생하면 이체가 잘못된 것이므로 작업 A를 취소해야 한다. 이럴때 트랜잭션을 사용하여 원자성을 확보한다.
트랜잭션 처리방법(JDBC 에서)
JDBC를 사용할 때는 트랜잭션을 수동으로 관리할 수 있다. 기본적으로 JDBC는 자동 커밋 모드가 활성화되어 있어서 각 쿼리가 자동으로 커밋된다. 수동으로 트랜잭션을 관리하려면 다음과 같은 방식으로 처리한다.
예시)Java
connection.setAutoCommit(false); // 자동 커밋 비활성화
try {
// 여러 SQL 작업 수행
statement.executeUpdate("UPDATE account SET balance = balance - 100 WHERE id = 1"); // 돈 차감
statement.executeUpdate("UPDATE account SET balance = balance + 100 WHERE id = 2"); // 돈 추가
connection.commit(); // 모든 작업이 성공하면 커밋
} catch (SQLException e) {
connection.rollback(); // 오류 발생 시 롤백
} finally {
// 자원 해제
}
요약하자면
- 트랜잭션은 여러 작업을 하나의 단위로 묶어 처리하는 것
- 원자성, 일관성, 고립성, 지속성이라는 네가지 주요특성
- 트랜잭션을 사용하면 데이터의 무결성과 안정성을 높일 수 있다
단점:
1. 상대적으로 낮은 추상화: JDBC는 SQL을 직접 다루기 때문에 개발자가 더 많은 코드와 관리 작업을 해야 한다.
2. 보일러플레이트 코드: 데이터베이스 연결, 쿼리 실행 등에서 반복적인 코드가 많이 필요하다.
3. 예외 처리 복잡성: JDBC 작업 중 발생할 수 있는 다양한 예외를 처리해야 한다.
4. JDBC와 비슷한 기술
1. ORM(Object-Relational Mapping): Hibernate, JPA(Java Persistence API) 등은 JDBC의 상위 개념으로, SQL을 추상화하여 데이터베이스 작업을 더 쉽게 할 수 있게 해준다.
2. Spring Data JPA: Spring Framework의 데이터 액세스 모듈로, JPA를 기반으로 한 데이터베이스 작업을 쉽게 만들어 준다.
5. Spring Boot 와 JDBC
Spring Boot는 JDBC를 더 쉽게 사용할 수 있도록 도와주는 여러 기능을 제공한다.
- JDBC Template: 반복적인 JDBC 코드를 줄여주고, 예외 처리를 간소화 한다.
- Spring Data: 리포지토리 패턴을 통해 데이터베이스 작업을 더 직관적으로 처리할 수 있게 해준다.
5-1. JDBC Template 의 특성
1. 간편한 사용
JDBC Template 은 복잡한 JDBC API의 코드 작성을 줄여주고, 연결, 쿼리 실행, 결과 처리 등을 간편하게 처리할 수 있다.
2. 예외 처리
JDBC Template 은 SQLException 을 RuntimeException 으로 변환하여 처리한다. 따라서 개발자는 직접 예외 처리를 할 필요가 줄어든다.
3. 리소스 관리
JDBC Template 은 DB연결, Statement, 결과 집합(ResultSet) 등의 리소스를 자동으로 관리한다. 따라서 리소스 누수의 위험이 줄어든다.
4. 다양한 작업 지원
쿼리 실행, 업데이트, 배치 작업 등 다양한 데이터베이스 작업을 지원한다.
5-2. JDBC Template 의 구성 요소
a. DataSource
JDBC Template 은 데이터베이스 연결을 위해 DataSource 객체를 필요로 한다. 이는 데이터베이스 연결 풀을 관리하고, 커넥션을 제공하는 역할을 한다.
b. JdbcTemplate 클래스
JDBC Template 의 핵심 클래스. JdbcTemplate 을 생성할 때, DataSource 를 주입하여 사용한다.
5-3. 사용방법
a. Maven 의존성 추가
Spring JDBC를 사용하려면 Maven에 의존성을 추가해야 한다. pom.xml 에 다음과 같이 추가한다.
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.x.x</version> <!-- 원하는 버전으로 변경 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.x.x</version> <!-- 원하는 버전으로 변경 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.x.x</version> <!-- 원하는 버전으로 변경 -->
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
b. 데이터 소스 설정
Spring 에서 사용할 데이터 소스를 설정한다. application.properties 파일에 다음과 같이 설정할 수 있다.
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
c. JdbcTemplate Bean 생성
스프링 설정 클래스나 XML 파일에서 JdbcTemplate 빈을 생성한다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@Configuration // 이 클래스가 Spring의 설정 클래스를 의미함을 나타냄
public class AppConfig {
@Bean // 이 메서드가 Spring의 Bean으로 등록됨을 나타냄
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource(); // DriverManagerDataSource 객체 생성
// 데이터베이스의 URL을 설정
dataSource.setUrl("jdbc:h2:mem:testdb");
// 사용할 JDBC 드라이버 클래스 이름을 설정
dataSource.setDriverClassName("org.h2.Driver");
// 데이터베이스의 사용자 이름 설정
dataSource.setUsername("sa");
// 데이터베이스의 비밀번호 설정
dataSource.setPassword("");
return dataSource; // 설정된 DataSource 객체 반환
}
@Bean // 이 메서드가 Spring의 Bean으로 등록됨을 나타냄
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource()); // 위에서 생성한 DataSource를 사용하여 JdbcTemplate 객체 생성 및 반환
}
}
d. 데이터베이스 작업 수행
이제 JdbcTemplate 을 사용하여 데이터베이스 작업을 수행할 수 있다.
import org.springframework.beans.factory.annotation.Autowired; // 의존성 주입을 위해 사용
import org.springframework.jdbc.core.JdbcTemplate; // JdbcTemplate 클래스 임포트
import org.springframework.stereotype.Repository; // DAO 클래스를 나타내는 어노테이션
import java.util.List; // List 인터페이스 임포트
@Repository // 이 클래스가 데이터 접근 객체임을 나타냄
public class UserRepository {
@Autowired // JdbcTemplate 객체를 자동으로 주입
private JdbcTemplate jdbcTemplate;
// 사용자 테이블 생성 메서드
public void createTable() {
// 테이블을 생성하기 위한 SQL 문
String sql = "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50))";
jdbcTemplate.execute(sql); // SQL 문 실행
}
// 사용자 삽입 메서드
public void insertUser(int id, String name) {
// 사용자 정보를 삽입하기 위한 SQL 문
String sql = "INSERT INTO users (id, name) VALUES (?, ?)";
// JdbcTemplate의 update 메서드를 사용하여 SQL 실행
jdbcTemplate.update(sql, id, name); // ? 자리에 id와 name이 순서대로 매핑됨
}
// 모든 사용자 조회 메서드
public List<String> findAllUsers() {
// 사용자 이름을 조회하기 위한 SQL 문
String sql = "SELECT name FROM users";
// JdbcTemplate의 queryForList 메서드를 사용하여 결과를 List<String> 형태로 반환
return jdbcTemplate.queryForList(sql, String.class); // SQL 실행 결과를 문자열 리스트로 반환
}
}
5-4. 주요 메서드
- queryForObject() : 결과가 단일 객체인 경우 사용.
- queryForList() : 결과가 리스트인 경우 사용.
- update() : 데이터 수정(INSERT, UPDATE, DELETE) 작업 수행.
- execute() : DDL 명령어 실행.
5-5. 결론
Spring 의 JDBC Template 은 JDBC 작업을 훨씬 간편하고 안전하게 처리할 수 있게 해준다. 복잡한 예외 처리나 리소스 관리에 대한 부담을 덜어주므로, 개발자가 비즈니스 로직에 집중할 수 있게 만들어 준다. 데이터베이스와의 상호작용이 많은 애플리케이션에서 특히 유용하다.
'Spring' 카테고리의 다른 글
Spring - H2 Database (0) | 2024.11.08 |
---|---|
Spring - @Transactional (1) | 2024.11.05 |
Spring - REST API 와 RESTful (0) | 2024.10.18 |
Spring - JPA, Entity, 영속성 컨텍스트 (4) | 2024.10.10 |
Spring - IoC & DI (1) | 2024.10.01 |