이전 글: 2025.05.21 - [Backend/JAVA] - JDBC
JDBC
JDBC (Java Database Connectivity)자바에서 DB에 접근하기 위한 표준 API즉, 자바 애플리케이션이 DBMS 종류와 상관 없이 동일하게 사용할 수 있는 클래스와 인터페이스로 구성JDBC API (java.sql 패키지에 포함)
0woy.tistory.com
JDBC 요약
JDBC(Java Database Connectivity)는 애플리케이션이 다양한 RDB와 통신할 수 있도록 하는 `JAVA API`
DB에 접속하고, SQL 쿼리를 실행하며, 결과를 가져오는 등의 작업을 수행하기 위한 표준 인터페이스를 제공
주요 역할
- 드라이버 로딩: 특정 DB 벤더가 제공하는 JDBC 드라이버 로드
(예: MySQL JDBC 드라이버, PostgreSQL JDBC 드라이버) - 연결 설정: DriverManager 클래스를 사용하여 데이터베이스와의 연결(Connection)을 설정
이때 JDBC URL, 사용자 이름, 암호 등이 必 - 쿼리 실행: Statement 또는 PreparedStatement 객체를 사용하여 SQL 쿼리를 데이터베이스에 보내고 실행
- 결과 처리: 쿼리 실행 후 반환되는 ResultSet 객체를 통해 결과를 가져와 처리
- 자원 해제: 사용이 끝난 Connection, Statement, ResultSet 객체를 닫아 자원 해제
문제점
DB 연결을 생성하고 해제하는 작업은 많은 시간과 자원을 소모하는 일임
웹 애플리케이션처럼 여러 사용자가 동시에 DB에 접근해야 하는 환경에서는 매번 새로운 연결을 만들고 닫는 것이 성능에 큰 오버헤드를 발생시킬 수 있음
∴ 이러한 문제를 해결하기 위해 `커넥션 풀 (Connection Pool)` 등장
Connection Pool
커네견 풀은 DB 연결을 미리 생성해두고, 필요할 때 이 풀에서 연결을 빌려 쓰고 반환하는 방식으로 동작함
매번 새로운 연결을 생성하고 해제하는 비용을 줄여 애플리케이션의 성능을 향상시킬 수 있음
또한, 동시에 사용할 수 있는 연결 수를 제한하여 DB 과부화 방지
HikariCP (Hikari Connection Pool)
HikariCP는 JDBC Connection Pool을 구현한 라이브러리 中 하나로, 현재 JAVA 진영에서 가장 빠르고 효율적인 커넥션 풀로 평가받고 있음
📌 Spring Boot 2.0 부터는 기본 JDBC CP로 HikariCP 사용
특징
- 높은 성능: 바이트코드 수준의 최적화와 마이크로 최적화를 통해 매우 빠른 성능 제공
- 경량성 : 라이브러리 크기가 매우 작아 리소스 사용량 少
- 안정성: 다양한 옵션을 통해 커넥션 관리 안정적 수행 가능
- 쉬운 설정: 간결한 설정으로 사용 가능
JDBC와 HikariCP
- HikariCP는 JDBC DataSource 인터페이스의 구현체
- HikariCP는 JDBC API를 기반으로 동작하며,
데이터베이스 연결을 효율적으로 관리하기 위한 커넥션 풀 기능을 제공함 - 애플리케이션은 직접 JDBC의 DriverManager를 사용하여 연결을 생성하는 대신
HikariCP가 제공하는 DataSource 객체를 통해 데이터베이스 연결을 얻음 - DataSource는 내부적으로 HikariCP 커넥션 풀을 사용하여 미리 생성된 연결을 제공하거나,
풀에 연결이 부족할 경우 새로운 연결을 생성하여 제공합니다.
JDBC | Java 애플리케이션이 데이터베이스에 접근하기 위한 표준 API 데이터베이스와의 기본적인 연결 및 쿼리 실행 방법을 정의 |
HikariCP | JDBC API를 사용하여 데이터베이스 연결을 효율적으로 관리(Connection Pooling) 해주는 라이브러리 JDBC 연결의 생성 및 해제 오버헤드를 줄여 애플리케이션 성능 향상 |
📌 Java 애플리케이션에서 데이터베이스를 연동할 때는 JDBC를 사용하여 데이터베이스와 통신하고, 그 과정에서 발생하는 연결 생성/해제 오버헤드를 줄이기 위해 HikariCP와 같은 커넥션 풀 라이브러리를 함께 사용하는 것이 일반적
MyBatis (SQL Mapper Framework)
MyBatis는 SQL Mapper 프레임워크로, JDBC를 사용하여 *DB와 직접 통신하는 방식의 번거로움을 줄여주고 SQL을 `XML파일`이나 `어노테이션`으로 분리하여 관리할 수 있도록 함
👉 이를 통해 개발자는 DB 연동 코드를 깔끔히 작성 및 유지보수 가능
❓ DB와 직접 통신하는 방식
매번 Connection, Statement, ResultSet 객체를 생성하고 관리하는 것
MyBatis의 주요 역할
- SQL 분리: SQL 쿼리를 애플리케이션 코드와 분리하여 XML 매퍼 파일 또는 어노테이션으로 정의
- 객체-관계 매핑 (ORM 유사 기능): SQL 쿼리 결과를 자바 객체에 자동으로 매핑
완벽한 ORM은 아니지만, 데이터베이스 레코드와 자바 객체 간의 매핑을 편리하게 해줌 - 간편한 파라미터 처리: PreparedStatement의 setX() 메서드를 일일이 호출할 필요 없이, 자바 객체를 파라미터로 넘겨 SQL 쿼리에 바인딩
- 트랜잭션 관리: 스프링과 연동 시 트랜잭션 관리를 쉽게 할 수 있도록 지원
예시:
TimeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.scoula.springdb.mapper.TimeMapper"> // Mapper 인터페이스와 같은 패키지 경로
<select id="getTime2" resultType="java.lang.String">
SELECT sysdate() //id= 메서드 명, resultType= return 타입
</select>
</mapper>
TimeMapper.java
import org.apache.ibatis.annotations.Select;
public interface TimeMapper {
@Select("SELECT sysdate()")
public String getTime();
public String getTime2();
}
MyBatis는 이 인터페이스의 메서드 호출을 `TimeMapper.xml`에 정의된 SQL 쿼리와 매핑하여 실행
개발자는 JDBC의 Statement나 ResultSet을 직접 다룰 필요 없이, 이 인터페이스의 메서드를 호출하는 것만으로 데이터베이스 작업을 수행할 수 있음
MyBatis의 SqlSession
- DB 상호작용하는 핵심 인터페이스, SQL 실행을 위한 단위 작업(Unit of Work)을 나타냄.
- DB 연결(Connection)을 빌려와 SQL 쿼리를 실행하고, 트랜잭션을 관리하며, 결과를 매핑하는 등 모든 DB 작업을 수행하는 데 사용
📌 SqlSession은 쓰레드에 안전하지 않은(thread-unsafe) 객체임
∴ 각 쓰레드(요청)마다 고유한 SqlSession 인스턴스를 생성하고 사용 후에는 반드시 닫아주어야 합니다.
SqlSession의 주요 역할
- SQL 실행 (Executing SQL):
- selectList(), selectOne(), insert(), update(), delete() 등의 메서드를 제공하여 XML 매퍼 파일이나 어노테이션에 정의된 SQL 쿼리 실행
`예: sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);` - 매퍼 인터페이스 방식(MyBatis 3부터 권장)에서는 `sqlSession.getMapper(UserMapper.class).selectUserById(1);` 와 같이 사용됨
- selectList(), selectOne(), insert(), update(), delete() 등의 메서드를 제공하여 XML 매퍼 파일이나 어노테이션에 정의된 SQL 쿼리 실행
- 트랜잭션 관리 (Transaction Management):
- SqlSession은 내부적으로 데이터베이스 커넥션의 트랜잭션 상태를 관리
- `commit()`: 현재 수행된 모든 DML(Insert, Update, Delete) 작업을 데이터베이스에 영구적으로 저장
- `rollback()`: 현재 수행된 모든 DML 작업을 취소하고 이전 상태로 되돌립니다.
- `close()`: SqlSession을 닫고, 내부적으로 사용했던 데이터베이스 연결을 커넥션 풀에 반환
- commit()이나 rollback()이 호출되지 않은 상태에서 close()되면 기본적으로 롤백
(AutoCommit 설정에 따라 다를 수 있음)
- 데이터베이스 연결(Connection) 관리:
- SqlSession이 생성될 때, SqlSessionFactory로부터 DataSource(예: HikariCP)를 통해 데이터베이스 Connection을 빌려옴
- SqlSession이 닫히면, 빌려왔던 Connection을 커넥션 풀에 다시 반환.
개발자가 직접 JDBC Connection을 다룰 필요가 없어 편리하고 자원 누수를 방지 가능
- 매퍼 인터페이스와 연동 (Mapper Interface Integration):
- MyBatis 3부터는 매퍼 인터페이스(예: UserMapper.java)를 사용하는 것이 권장
- `SqlSession.getMapper(Class<T> type)` 메서드를 사용하여 매퍼 인터페이스의 구현체를 얻을 수 있음.
이 구현체는 MyBatis가 런타임에 동적으로 생성하는 프록시 객체입니다. - 이 프록시 객체를 통해 개발자는 마치 일반 자바 메서드를 호출하듯 DB 작업 수행
- 캐싱 (Caching):
- MyBatis는 1차 캐시와 2차 캐시를 제공
- 1차 캐시 (Local Cache): SqlSession 스코프의 캐시
동일한 SqlSession 내에서 동일한 쿼리가 실행되면 데이터베이스에 다시 접근하지 않고 캐시된 결과를 반환. SqlSession이 닫히면 1차 캐시는 비워짐 - 2차 캐시 (Global Cache): SqlSessionFactory 스코프의 캐시
여러 SqlSession 간에 공유될 수 있으며, 매퍼 단위로 설정
SqlSession 생명 주기
SqlSession은 짧은 생명 주기를 가지는 것이 일반적임
웹 애플리케이션 | 하나의 HTTP 요청이 시작될 때 SqlSession 생성, 요청 완료 시 SqlSession을 닫음 |
배치 처리 | 하나의 배치 작업 단위마다 SqlSession을 생성하고 사용 후 닫음 |
주의 사항: SqlSession은 반드시 사용 후 닫아주어야 함.
📌 `try-with-resources` 문을 사용하면 close() 메서드를 명시적으로 호출하지 않아도 자동으로 닫히도록 할 수 있음
// SqlSession 사용 예시 (이전 Main.java 코드에서 발췌)
try (SqlSession session = sqlSessionFactory.openSession()) { // 여기서 SqlSession이 열리고, 트랜잭션 시작
UserMapper userMapper = session.getMapper(UserMapper.class);
// SQL 실행
userMapper.insertUser(new User("Charlie", "charlie@example.com"));
// 트랜잭션 커밋
session.commit();
} // try-with-resources 블록을 벗어나면 SqlSession이 자동으로 닫히고, 연결이 커넥션 풀에 반환됨
catch (Exception e) {
// 예외 발생 시 트랜잭션 롤백 (선택 사항, 스프링에서는 자동)
// session.rollback();
e.printStackTrace();
}
결론
- SqlSession은 MyBatis를 사용하여 데이터베이스 작업을 수행하는 데 필요한 모든 기능을 제공하는 중심 객체
- 개발자는 SqlSession을 통해 간편하게 SQL을 실행하고, 트랜잭션을 제어하며, 데이터베이스 연결을 관리할 수 있음
- 특히 매퍼 인터페이스와 함께 사용될 때 개발 편의성이 극대화됨