(▲ https://hackmd.io/@ddubson/rkn-sR4wU)
JDBC
-
Java Database Connectivity(JDBC)는 DBMS에 접근하기 위한 Java의 표준 API 스펙
-
JDBC 드라이버 로딩, DBMS 연결, SQL 전송 및 결과 반환 등의 처리
fun main() {
val driver = "com.mysql.cj.jdbc.Driver"
val url = "jdbc:mytsql://localhost:3306/test"
Class.forName(driver).newInstance()
val con: Connection = DriverManager.getConnection(url, "root", "root")
val st: Statement = con.createStatement()
val sql = "SELECT * FROM member"
val rs: ResultSet = st.executeQuery(sql)
while(rs.next()) {
const name = rs.getString(1)
const date = rs.getString(2)
println("$name - $date")
}
rs.close()
st.close()
con.close()
}
구성요소
Driver
import java.sql.Driver
-
Driver
인터페이스를 구현하여 DB와 연결할 JDBC Driver를 만들 수 있음 -
com.mysql:mysql-connector-j와 같은 라이브러리가 해당 DBMS에 맞는 드라이버를 구현함
Connection
import java.sql.Driver
import java.sql.Connection
val con: Connection = if (usingDriverManager) {
// 방법 1 - DriverManager
val driver = "com.mysql.cj.jdbc.Driver"
Class.forName(driver).getDeclaredConstructor().newInstance()
DriverManager.getConnection(url, user, password)
} else {
// 방법 2 - MysqlDataSource
MysqlDataSource().apply {
setUrl(url)
setUser(user)
setPassword(password)
}.connection
}
-
특정 DB와의 연결정보를 가지는 인터페이스
-
DriverManager.getConnection()
를 통해 JDBC 연결 생성 -
특정 database와 jdbc driver에 의존함
-
연결이 되면
Stagement
를 통해 쿼리는 실행할 수 있음
Statement
import java.sql.Statement
val st: Statement = con.createStatement()
PreparedStatement
import java.sql.PreparedStatement
-
Statement
의 하위 인터페이스 -
SQL을 미리 컴파일하여 실행 속도를 높힘
ResultSet
import java.sql.ResultSet
statement.executeQuery().let { rs: ResultSet ->
Entity(
id = resultSet.getLong(1),
name = resultSet.getString(2),
)
}
-
Statement
를 통한 쿼리 실행 결과에 사용되는 인터페이스
동작 분석
어떻게 데이터를 ResultSet
에 가져오는가?
-
mysql testcontainer 사용한 test case에서 디버깅해봤을 때
-
ResultSet
의 구현체는com.mysql.cj.jdbc.result.ResultSetImpl
사용함 -
쿼리 실행시
rowData: ResultsetRows
(실제 인스턴스는ResultsetRowsStatic
)필드의rows
에Row
인스턴가 저장되어 있음Row
인스턴스는ByteArrayRow
이고,internalRowData
가 byte-array를 가지고 있음 -
DB와 무언가를 통한 통신을 통해 데이터를 byte array로 가져와서 저장하는 것으로 보임.
-
TODO: cursor는 그럼 어떻게 동작할까?
// MySQL에서 이 설정이 있다면 스트리밍 방식으로 읽는 듯 // https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-implementation-notes.html val stmt = con.createStatement( java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, ).apply { fetchSize = Integer.MIN_VALUE }
-
-
ConnectionPool
-
일정량의 Connection 인스턴스를 미리 만들어서 pool에 저장해두고 사용하기 위함
-
HikariCP
-
DBCP(Database Connection Pool) 라이브러리
val config = HikariConfig().apply { jdbcUrl = url username = user this.password = password } val ds = HikariDataSource(config) val con: Connection = ds.connection
-
spring-jdbc
org.springframework.boot:spring-jdbc:5.2.9.RELEASE > org.springframework:spring-beans:5.2.9.RELEASE > org.springframework:spring-core:5.2.9.RELEASE > org.springframework:spring-tx:5.2.9.RELEASE
-
JDBC에서 처리하는 데이터베이스 관련 작업들을 스프링 프레임워크로 위임하고, 별도 API를 통해 데이터베이스 연결 및 쿼리 실행을 함.
구성요소
@startuml hide empty field hide empty method interface JdbcOperations { query(psc, rowMapper): List<T> } class JdbcTemplate interface NamedParameterJdbcOperations class NamedParameterJdbcTemplate { -classicJdbcTemplate: JdbcOperations } abstract class JdbcAccessor { -dataSource: DataSource } JdbcOperations <|-- JdbcTemplate JdbcAccessor <|-- JdbcTemplate NamedParameterJdbcOperations <|-- NamedParameterJdbcTemplate NamedParameterJdbcTemplate --> JdbcOperations @enduml
JdbcTemplate
-
DataSource
를 생성하고JdbcTemplate
에 주입하여 사용. -
JdbcTemplate
를 통해 JDBC를 편리하게 사용할 수 있음. -
JdbcTemplate
: 가장 저수준에서 동작하며, Spring 내부적으로JdbcTemplate
을 사용함-
thread-safe 하므로, DAO등에서 맴버 변수로 저장.
DataSource
만 외부에서 유입받아 초기화해둘 수 있음.
-
NamedParameterJdbcTemplate
-
NamedParameterJdbcTemplate
:JdbcTemplate
을 래핑해서?
가 아닌 이름이 붙은 파라미터 사용할 수 있게함.
RowMapper
-
RowMapper
:ResultSet
(쿼리 결과)에서 원하는 객체로 타입을 변환하는 역할 -
BeanPropertyRowMapper
-
DataClassRowMapper
-
spring-boot-starter-jdbc
org.springframework.boot:spring-boot-starter-jdbc:2.3.4.RELEASE > com.zaxxer:HikariCP:3.4.5 > org.springframework.boot:spring-boot-starter:2.3.4.RELEASE > org.springframework:spring-jdbc:5.2.9.RELEASE
-
spring-jdbc에 대하여 스프링부트 의존성 관리를 한번에 하고자 wrapping된 모듈.
ORM
-
ORMObject Relational Mapping
-
TODO
spring-data-jdbc
org.springframwork.data:spring-data-jdbc:2.0.4.RELEASE > org.slf4j-api:1.7.30 > org.springframework.data:spring-data-commons:2.3.4.RELEASE > org.springframework.data:spring-data-relational:2.0.4.RELEASE > org.springframework:spring-beans:5.2.9.RELEASE > org.springframework:spring-context:5.2.9.RELEASE > org.springframework:spring-core:5.2.9.RELEASE > org.springframework:spring-jdbc:5.2.9.RELEASE > org.springframework:spring-tx:5.2.9.RELEASE
-
Spring Data의 미션은 데이터 액세스를 위해 친숙하고 일관된 Spring 기반의 프로그래밍 모델을 제공하는 동시에 기본 데이터 저장소의 특수한 특성을 유지하는 것.
-
spring data repository 추상화의 목표는 데이터 액세스 레이어를 구현하는 데 필요한 상용구 코드의 양을 줄이는 것
-
Spring Data JDBC는 Spring Data의 여러 모듈중 하나로, 말 그대로 JDBC를 지원하는 모듈.
-
뭔가 문서가 JPA인것 같은데…? 엔티티매니저 가지고 더티체킹하는게 큰 차이?
-
-
CrudRepository
를 활용하여 기본적인 CRUD 구현을 쉽게 함. -
중신에
Repository
인터페이스가 있고,CrudRepository
,ListCurdRepository
인터페이스와 같이 엔티티 클래스에 대한 정교한 CURD기능을 제공-
CurdRepository
인터페이스는Iterable
반환 -
ListCurdRepository
인터페이스는List
반환
-
구성요소
@startuml hide empty field hide empty method interface Repository interface CurdRepository { save(eneity: S) saveAll(entities: Iterable<S>) findById(id: ID) existsById(id: ID) findAll() findAllById(ids: Iterable<ID>) count() deleteById(id: ID) delete(entity: T) deleteAllById(ids: Iterable<? extends ID>) deletaAll(entities: Iterable<? extends T>) deleteAll() } interface PagingAndSortingRepository { findAll(sort: Sort): Iterable<T> findAll(pageable: Pageable): Page<T> } class SimpleJdbcRepository interface JdbcAggregateOperations class JdbcAggregateTemplate Repository <|-- CurdRepository CurdRepository <|-- SimpleJdbcRepository PagingAndSortingRepository <|-- SimpleJdbcRepository SimpleJdbcRepository ..> JdbcAggregateOperations interface DataAccessStrategy interface JdbcConverter JdbcAggregateOperations <|-- JdbcAggregateTemplate JdbcAggregateTemplate ..> DataAccessStrategy JdbcAggregateTemplate ..> JdbcConverter @enduml
Repository
CurdRepository
EntityRowMapper
-
spring-jdbc의 RowMapper의 구현체
JdbcConverter
JdbcValue
spring-boot-starter-data-jdbc
org.springframework.boot:spring-boot-starter-data-jdbc:2.3.4.RELEASE > org.springframework.boot:spring-boot-starter-jdbc:2.3.4.RELEASE > org.springframework.data:spring-data-jdbc:2.0.4.RELEASE
spring-jdbc-plus
-
네이버에서 제공하는 Spring Data JDBC 확장 라이브러리.
References
-
JdbcTemplate:
-
JdbcOperation: 구현체
-
NamedParameterJdbcTemplate
-
TransactionManager
-
TransactionTemplate
-
DataAccessStrategy
findById
-
CrudRepository
구현체인SimpleJdbcRepository
-
JdbcAggregateOperations
필드의findById
메서드 실행됨 -
JdbcAggregateOperations
구현체인JdbcAggregateTemplate
-
DataAccessStrategy
필드의findById
메서드 호출-
DataAccessStrategy
구현체인DefaultDataAccessStrategy
-
SqlGeneratorSource
필드의getSqlGenerator
메서드 통해서 쿼리 생성 -
SqlParamgersFactory
필드의forQueryById
메서드 통해서 쿼리 파라미터 생성 -
getEntityRowMapper
메서드 통해RowMapper
생성 -
operations: NamedParameterJdbcOperations
필드의queryForObject
메서드 통해서 쿼리 실행 (w/ rowMapper)-
converter: JdbcConverter
필드의query
메서드 호출 -
execute
메서드 호출 -
DateSourceUtils.getConnection(DataSource)
메서드 통해서Connection
생성 -
doInPreparedStatement
실행-
PreparedStatement#executeQuery
메서드 통해서ResultSet
생성-
update
-
AbstractQueryProtocol#executeQuery
-
ComQuery.sendSubCmd
-
-
-
쿼리 결과인
result
생성
-
-
-
triggerAfterConvert
메서드 실행
-
-
-
실제 DB로부터 어떻게 값이 오는지?
-
reading converter가 동작할 수 있는지?
-
spring batch에서 cursor는 어떻게?