JDBC
JDBC는 Java에서 DB에 접속할 수 있게 하는 API이다. JDK에 포함되어있다.
Persistence Layer(즉 DB와 연결하는 부분)을 위해 존재한 최초의 Component가 JDBC이다. 즉 JDBC는 역사가 깊은 API다.
JDBC driver
Java에서 DB를 사용하려면 JDBC 드라이버가 있어야 한다.
JDBC 인터페이스를 각 DBMS에 적절하게 구현한 것이 JDBC 드라이버이다.
JDBC 드라이버는 자신이 사용할 DBMS에 맞춰 다운로드 받으면 된다.
백엔드 개발자는 DB 종류와 상관없이 JDBC API를 사용해서 DB와 연결한다.
아래 그림을 보면 개발자는 JDBC api를 사용하여 db에 접근하도록 되어있다.
DB와 연결 ─> Connection을 얻는다
JDBC api를 통해 DB와 연결될 수 있다고 했다.
이것을 connection을 (Connection 객체를) 얻는다고 표현하자.
Connection는 다음 두가지 방법으로 받아올 수 있다.
- DriverManager를 통해 받아오기
- DataSource를 통해 받아오기
Connection을 얻는 측면에서 DriverManager와 DataSource의 기능은 어떻게 다를지를 살펴볼 것이다.
그전에 Connection Pooling을 이해할 필요가 있다.
Connection Pooling
■ Connection을 매번 생성/삭제하는 시간적 비용이 크다
첫번째 DriverManager를 사용할 경우, Connection 필요시마다 매번 생성하고 close한다.
Connection 객체를 얻는 작업은 시간을 많이 소모한다.
하지만 이렇게 불필요하게 Connection 객체를 생성하고 close하는 과정은 필요없을 것 같다.
■ Connection Pooling 방법을 사용한 DBCP
(DataBase Connection Pool)
그래서 Connection Pooling 개념이 등장했다.
Connection을 매번 생성/삭제하는 방법과 다르게
일정량의 Connection을 미리 여러개 생성해서 Pool에 보관해둔다.
그리고 Connection이 필요할 때마다 이미 만들어둔 Connection을 Pool에서 꺼내서 쓴다.
다 쓰고 이제 필요 없으면 다시 반환한다. 이렇게 Connection을 재사용하는 것이다.
Connection을 매번 생성/삭제하지 않으므로 빠르다.
따라서 더 좋은 성능을 위해 Connection Pooling을 사용할 수 있는 것이다.
예시) 여러 사용자가 동시에 DB Connection을 요청한다면, Connection을 매번 새로 맺어서 시간을 낭비할 것이 아니라 Connection Pooling 방법을 사용한다. 필요한 사용자에게 Connection을 주고, 사용완료 후 반환되면, 대기중이었던 다른 사용자가 재사용할 수 있다.
참고) Connection Pool 크기를 크게 하면 메모리를 더 쓰지만 빠르고, 작게 하면 그 반대다.
Java DBCP의 대표적인 라이브러리 (Connection Pool 구현체)
- Commons DBCP
- Tomcat DBCP
- HikariCP ★
SpringBoot 2.0 이전에는 Tomcat(아파치)의 DBCP를 사용했는데, 이후에는 HikariCP를 사용한다고 보면 된다. HikariCP의 장점들이 많은데, 따로 찾아보면 될 것이다.
■ DriverManager와 DataSource가 Connection을 받아오는 방법
두 경우 모두 getConnection() 메서드를 통해 Connection 객체를 얻어온다.
하지만 DataSource를 통해서는 Connectino Pool을 사용 가능하고, DriverManager는 그렇지 못하다.
interface DataSource의 구현체 중 Connection Pool의 구현체가 존재한다. 즉 DataSource는 Connection Pool 기능을 사용 가능하게 한다. 반면 DriverManager는 Connection을 매번 생성하고 close한다. Connection Pool을 사용하지 못하는 것이다.
DataSource의 구현체가 모두 Connection Pool을 지원하는 것은 아님을 유의하자.
- SimpleDriverDataSource : 얘는 Connection Pool을 지원하지 않아서, DriverManager를 사용할 때와 동일하게 매번 Connection을 생성하고 close한다.
- HikariDataSource : 얘는 Connection Pool 기능을 사용한다.
그래서 Connection Pool을 사용하고 싶으면 DataSource의 구현체로 Connection Pool을 지원하는 구현체를 사용하면 된다. DataSource 객체를 생성할 때, 위에서 언급한 Java DBCP들 (Commons DBCP, Tomcat DBCP, HikariCP) 관련된 DataSource를 지정해주면 된다.
참고) SimpleDriverDataSource는 Driver를 사용한다
SimpleDriverDataSource는 Driver를 필드로 가지고 있다.
만약 이 DataSource 구현체를 통해 getConnection()하면 무슨 일이 일어날까?
일단 이 SimpleDriverDataSource 클래스에는 getConnection()은 없다. (부모에게 있음)
부모인 AbstractDriverBasedDataSource로 들어가보자.
이곳의 getConnection()을 확인해보면,
결국 getConnectionFromDriver(...)를 호출한다.
getConnectionFromDriver는 abstract method로, SimpleDriverDataSource가 구현하게끔 되어있다.
다시 SimpleDriverDataSource 클래스로 돌아와서,
구현된 getConnectionFromDriver를 보면 결과적으로 driver.connect()하고있다.
따라서 Pool이 아니라 매번 DriverManager를 통해 Connection을 가져오게 된다.
예를들어 Spring에서 DataSource Bean을 다음과 같이 설정해줄 수 있다.
DataSourceBuilder를 create하고, type()을 통해 구현체를 사용해준다.
static class Config {
@Bean
public DataSource dataSource(){
var datasource = DataSourceBuilder.create()
.url("jdbc:mysql://localhost/~~~~")
.username("~~~~")
.password("~~~~")
.type(HikariDataSource.class) // 구현체를 이런식으로 넣는다!
.build();
datasource.setMaximumPoolSize(1000);
datasource.setMinimumIdle(100);
return datasource;
}
}
지금까지는 Connection을 받아오는 방법 두가지를 설명했다.
- 매번 생성/close 하는 방식으로 Connection 받아오기
- Connection Pool 개념
: 미리 Connection 한번에 만들어두고, 필요하면 Pool에서 꺼내쓴 뒤 Pool에 반환하는 방식으로 재사용
이렇게 DB와 연결되어 Connection을 얻었으면 당연히 Query도 날릴 수 있다.
Connection을 받아온 뒤 query실행 과정
* 코드는 이해를 돕기 위한 단순 예시임
① Connection을 받아온다.
var connection = dataSource.getConnection();
② Connection을 통해서 Statement를 가져온다.
var statement = connection.prepareStatement("select * from customers");
③ Statement를 통해서 query를 실행한다.
- query를 실행해서 ResultSet을 가져오거나
- query를 통해 그냥 update만 진행한다.
// ResultSet 얻기 (ex. select문처럼 얻어올 결과가 있는 경우)
var resultSet = statement.executeQuery()
// 그냥 update만 하기 (ex. 삭제, 수정하는 쿼리문)
statement.executeUpdate();
statement를 통해 쿼리 파라미터를 넣어주는 등의 이야기는 여기서 하지 않겠다.
그래도 예시를 간단히 적어두자면 이렇다.
// 아래처럼 ?를 통해 값이 들어갈 자리를 마련해주고
private final String UPDATE_BY_ID_SQL = "UPDATE customers SET name = ? WHERE customer_id = UUID_TO_BIN(?)";
// statement가 만들어지면 이런 식으로 값을 세팅하고 나서 statment를 시행할 수 있다.
statement.setString(1, name);
statement.setBytes(2, customerId.toString().getBytes());
statement.executeUpdate();
④ query 완료 후 DB Connection을 close한다 (중요)
이때 직접 close하지 않아도, 사용한 DataSource 구현체가 Closeable을 구현하고 있다면
블록{ }을 벗어났을 때 connection이 자동으로 close된다.
글 내용 요약
▶ JDBC와 JDBC driver를 알아봄
- JDBC는 DB를 사용가능케 하는 java의 api이며
- JDBC driver는 JDBC api가 특정 DBSM에 연결할 수 있게 하는 driver다. DBSM에 따라 적절한 driver를 설치해 사용한다.
▶ DB 연결한다는 것은 Connection 객체를 얻는 것임
- Connection 객체를 얻는 방법으로 DriverManager, DataSource를 공부함
- 이때 Connection Pool 개념을 학습했음
▶ Connection을 받아온 뒤 query를 날리는 과정을 공부함
- connection을 통해 statement를 만들고, statement를 필요에 따라 잘 세팅한 뒤 query를 시행할 수 있음
'JAVA' 카테고리의 다른 글
[Java] Generic Type erasure / 제네릭 타입 소거 (0) | 2024.08.22 |
---|---|
[Java] 시간낭비 방지 메모: HashXX를 보고 O(1)을 곧바로 떠올렸어야지...! (1) | 2023.12.06 |
[JAVA] Iterator의 remove() 이해하기 (0) | 2022.09.08 |
[Java] String 연결하기(더하기) 효율적인 방법 (0) | 2022.09.07 |
웹개발 집중학습용으로 블로그 개설 (0) | 2022.07.19 |