자바를 사용해서 DB와 상호 작용하기 위한 자바 표준 인터페이스로, DBMS와 통신하여 데이터를 CRUD할 수 있게 해준다.

JDBC 특징
- 표준 API
- 대부분의 RDBMS(관계형 DBMS)에 대한 드라이버가 제공되어 여러 종류의 DB 대해 일관된 방식으로 상호 작용할 수 있다.
- Database 종류가 바뀌어도 쿼리문이 실행된다.
- 데이터베이스 연결
- SQL 쿼리 실행
- Prepared Statement
- 결과 집합 처리(Result Set)
- 데이터베이스로부터 반환된 결과 집합을 처리할 수 있다.

6. 트랜잭션 관리
- JDBC를 사용하여 데이터베이스 트랜잭션을 시작, 커밋(성공) 또는 롤백(실패)하는 등의 트랜잭션 관리 작업을 수행할 수 있다.
Statement VS Prepared Statement
Java에서 데이터베이스에 SQL 쿼리를 실행하기 위한 인터페이스
1. Statement
- DB와 연결되어 있는 Connection 객체를 통해 SQL문을 Database에 전달하여 실행하고, 결과를 반환받는 객체
- SQL 쿼리를 직접 문자열로 작성하여 DB에 전달
- 쿼리는 문자열 형태로 전달되고, 실행 시점에 DB에 파싱되어 실행
- => 실행할 때 마다 쿼리를 파싱하므로 성능에 영향을 미칠 수 있고, 보안 취약점을 가질 수 있다.
public class StatementExample {
public static void main(String[] args) {
try {
// MySqlDriver 파일을 라이브러리에 추가한다.
// Driver 연결
Class.forName("mysql.jdbc.driver.MySqlDriver");
// Database와 연결(계정 접속)
Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/mydatabase", "username", "password");
// Statement 인스턴스 생성
Statement statement = connection.createStatement();
// SQL Query 작성
String query = "SELECT * FROM MEMBER WHERE NAME = 'wonuk'";
// Query 실행 -> 결과는 ResultSet으로 반환됨
ResultSet rs = statement.executeQuery(query);
// 결과 처리
while (rs.next()) {
// 결과 처리 로직
}
// 수동으로 연결 해제
rs.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2. Prepared Statement
- SQL 쿼리를 미리 컴파일(preCompile)하여 DB에 전송할 때 값만 바뀌는 형태로 전달
- prepareStatement()를 호출할 때, 쿼리를 DB에 전달하여 미리 컴파일
- 이후 excuteUpdate()를 호출할 때, ?에 동적인 입력값을 바인딩해서 실행
- 쿼리가 한 번 컴파일되면 여러 번 실행할 수 있으며, 성능이 향상되고 보안 측면에서 더 안전함
- 동적인 입력값을 placeholder
?
로 대체하고 파라미터 바인딩을 통해 쿼리를 삽입
public class PreparedStatementExample {
public static void main(String[] args) {
try {
// MySqlDriver 파일을 라이브러리에 추가한다.
Class.forName("mysql.jdbc.driver.MysqlDriver");
// Database와 연결
Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/mydatabase", "username", "password");
// SQL Query 작성
String query = "SELECT * FROM employees WHERE department = ?";
// PreparedStatement 생성 및 값 설정
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, "HR"); // ? 위치에 HR 바인딩
// Query 실행
ResultSet resultSet = preparedStatement.executeQuery();
// 결과 처리
while (resultSet.next()) {
// 결과 처리 코드
}
// 연결 해제
resultSet.close();
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Persistence Framework
JDBC의 한계
- 간단한 SQL을 실행하는 경우에도 중복 코드가 많다.
- DB에 따라 일관성 없는 정보를 가진 채로 Checked Exception(SQL Exception) 처리를 한다.
- Checked Exception인 SQLException은 개발자가 명시적으로 예외처리해야 하는데, DBMS마다 고유한 SQL 문법과 오류 코드 체계를 가지고 있어서 모든 DBMS에 적합한 예외처리를 수행할 수 없다.
- Connection과 같은 공유 자원을 제대로 반환하지 않으면 한정된 시스템 자원(CPU, Memory)에 의해 서버가 다운되는 등의 문제가 발생한다.
- SQL Query를 개발자가 직접 작성한다.
Persistence Framework
- JDBC 처럼 복잡함이나 번거로움 없이 간단한 작업만으로 Database와 연동되는 시스템
- 모든 Persistence Framework는 내부적으로 JDBC API를 이용하므로 preparedStatement를 기본적으로 사용
- 크게 SQL Mapper, ORM 두가지로 나눌 수 있다.
SQL Mapper
- SQL 문의 실행 결과 <-> 객체(Object)의 필드를 매핑하여 데이터를 객체화
- 대표적인 SQL Mapper로 Spring JDBC Template, MyBatis가 있다.
- 한계
- SQL을 직접 다룬다.
- 특정 DB에 종속적으로 사용하기 쉽다.
- 테이블마다 비슷한 CRUD SQL, DAO(Data Access Object) 개발이 반복된다 (코드 중복)
- 테이블 필드가 변경될 시 이와 관련된 모든 DAO의 SQL문, 객체의 필드 등을 수정해야 한다.
- 객체와의 관계는 사라지고 DB에 대한 처리에 집중하게 된다.
// JDBC template
// 1. XML OR Gradle에 Spring JDBC 의존성 추가
// 2. application.properties OR application.yml에 데이터베이스 연결 설정
@RestController
public class MemberController {
private final MemberRepository memberRepository;
public MemberController(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@GetMapping("/members")
public List<Member> findById(Long id) {
return memberRepository.findById(id);
}
}
// Member Object
public class Member {
private Long id;
private String name;
private int age;
// Getter and Setter methods
}
// Repository Anotation의 역할에 대해 공부해주세요.
@Repository
public class MemberRepository {
private final JdbcTemplate jdbcTemplate;
public MemberRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// Member 객체로 리턴한다.
public List<Member> findById(Long id) {
String query = "SELECT * FROM MEMBER WHERE id = " + id;
return jdbcTemplate.query(query, (rs, rowNum) -> {
Member member = new Member ();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
member.setAge(rs.getInt("age"));
return member;
});
}
}
=> 객체 중심의 객체지향과 데이터 중심의 RDB의 패러다임 불일치 문제를 해결하기 위해 나온 것이 ORM
'언어, 프레임워크 > Java' 카테고리의 다른 글
람다 실습하기 (Comparator, Compose, andThen) (0) | 2025.03.14 |
---|---|
BufferedReader / BufferedWriter (0) | 2025.03.11 |
예외 복구/회피/전환 (예외 전환의 필요성) (0) | 2025.03.10 |
Assert 알아보기 (0) | 2025.03.08 |
Collectors 클래스 (0) | 2025.03.07 |