다양한 종류의 데이터베이스에 연결할 수 있습니다. (MySQL, Oracle, SQL Server 등)
SQL 문 실행
SQL 문을 실행하고 결과를 Java 어플리케이션에서 사용할 수 있습니다.
데이터베이스 관리
데이터베이스 테이블을 생성하거나 수정하고, 데이터를 삽입, 업데이트, 삭제할 수 있습니다.
트랜잭션 관리
데이터베이스 트랜잭션을 관리하고, 여러 작업을 하나의 단위로 묶을 수 있습니다.
[더 알아보기] 💡 spring-boot-starter-jdbc
- Spring Boot 프로젝트에서 JDBC를 더 쉽게 사용할 수 있도록 도와주는 스타터입니다. - 이를 통해 데이터베이스 Connection Pool과 같은 JDBC 관련된 빈들을 자동으로 설정해 줍니다. 이를 통해 개발자는 JDBC 연결 관련 설정이나 세부사항에 대해 신경 쓸 필요 없이 비즈니스 로직 개발에 집중할 수 있습니다.
2. JDBC 주요 객체
💡 JDBC 주요 객체
- 주요 객체를 기반으로 DataSource에서 Connection을 얻어서 이를 통해 Statement를 만들고 이를 실행한 ResultSet을 얻는 과정에서 사용하는 각각의 객체입니다.
객체
설명
메서드
Datasource
데이터베이스에 연결하기 위한 인터페이스이며 데이터베이스에 연결하기 위한 모든 세부 정보를 캡슐화 합니다.
Connection
애플리케이션과 데이터베이스 간의 연결을 나타내는 객체. SQL 문을 실행하고 결과를 반환받는 역할
createStatement(): Statement 객체를 생성
Statement
SQL 문을 데이터베이스에 보내는 역할을 수행하는 객체
executeQuery(): SQL SELECT 문을 실행하면 ResultSet 객체가 반환됨
ResultSet
SQL 쿼리의 결과를 나타내는 데이터의 집합
next(): 결과 집합의 다음 행에 액세스
💡 [참고] Spring Boot 내에서 application.properties 설정하는 과정에서 datasource를 통해서 환경설정을 구성합니다.
3. 처리과정
💡 처리과정
- Java Application - JDBC - DB 간에 처리되는 과정으로 자바 애플리케이션에서 데이터베이스에 접근하고 데이터를 처리하는 과정을 설명합니다. - 해당 처리과정은 클라이언트의 SQL 요청이 있을 때마다 아래의 과정을 수행하게 됩니다.
1. JDBC 로드 - JDBC 드라이버를 메모리에 로드합니다. 이를 통해 자바 애플리케이션과 데이터베이스 간의 통신이 가능해집니다.
2. Connection 객체 생성 - 데이터베이스에 연결을 수립합니다. 이 연결은 데이터베이스와의 모든 통신을 위한 기반을 제공합니다.
3. Statement 객체 생성 - 데이터베이스에 전송될 SQL 명령문을 포함하는 Statement 객체를 생성합니다. 이 객체를 통해 SQL 쿼리를 실행할 수 있습니다.
4. Query 수행 - SQL 쿼리를 실행하고, 데이터베이스로부터 결과를 반환받습니다.
5. ResultSet 객체로부터 데이터 저장 - Query의 결과를 ResultSet 객체에 저장합니다. 이 객체를 통해 Query의 결과를 애플리케이션에서 사용할 수 있습니다.
6. 리소스 해제 - 사용이 끝난 데이터베이스와의 연결을 종료하고, 메모리에서 JDBC 드라이버를 제거합니다. 이는 시스템 리소스를 효율적으로 관리하기 위한 중요한 단계입니다.
import java.sql.*;
public class Example {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. JDBC driver 로드
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. Connection 객체 생성
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName", "username", "password");
// 3. Statement 객체 생성
stmt = conn.createStatement();
// 4. Query 수행
rs = stmt.executeQuery("SELECT * FROM tableName");
// 5. ResultSet 객체로부터 데이터 저장
while(rs.next()) {
// Retrieve by column name
int id = rs.getInt("id");
String name = rs.getString("name");
// Display values
System.out.println("ID: " + id + ", Name: " + name);
}
} catch(SQLException se) {
se.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
} finally {
// 6. 리소스 해제
try {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(conn != null) conn.close();
} catch(SQLException se) {
se.printStackTrace();
}
}
}
}
3. 구성 과정
3.1. 의존성 주입
💡 의존성 주입
- spring-boot-starter-data-jdbc를 통해 PostgreSQL 데이터베이스와 통신을 합니다. - org.postgresql:postgresql를 통해 실제 데이터베이스를 연결하기 위한 JDBC 드라이버입니다.
- driver-class-name: 데이터베이스 연결을 위한 JDBC 드라이버 클래스를 지정합니다. - url: 연결하려는 데이터베이스의 URL을 지정합니다. - username: 데이터베이스 접속을 위한 사용자 이름을 입력합니다. - password: 데이터베이스 접속을 위한 사용자 비밀번호를 입력합니다.
spring:
# PostgreSQL DB 설정
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/postgres
username: xxx
password: xxx
2.3. JdbcTemplate.java
💡 JdbcTemplate.java - User 테이블의 사용자 정보를 모두 조회하기 위한 jdbcTemplate을 활용한 예시입니다.
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List<User> findAllUsers() {
String sql = "SELECT * FROM User";
return jdbcTemplate.query(sql, (resultSet, rowNum) -> new User(
resultSet.getLong("id"),
resultSet.getString("name"),
resultSet.getString("email")
));
}
}
[ 더 알아보기 ] 💡 JdbcTemplate 대신에 퍼시스턴스 프레임워크(MyBatis, JPA)를 사용하는 거 같은데 맞는 걸까?
- JdbcTemplate는 JDBC 코드를 단순화하고 일반적인 오류를 방지하는 데 도움이 됩니다. 그러나 이는 여전히 SQL 쿼리를 작성하고 결과를 수동으로 매핑해야 하는 단점이 있습니다. - 이러한 단점을 해결하기 위해 퍼시스턴스 프레임워크인 MyBatis를 이용하여 XML 형태로 SQL을 작성하는 편리한 방식이나 Spring Boot JPA 사용하여 SQL 쿼리를 작성하지 않고도 객체와 DB 컬럼을 매핑하는 방식을 이용하여 개발자가 집중해야 할 비즈니스 로직에 더 집중할 수 있게 돕습니다.
2) DBCP(DataBase Connection Pool)
💡 DBCP(Database Connection Pool) - 애플리케이션이 시작될 때 미리 여러 개의 데이터베이스 커넥션(Connection)을 커넥션 풀(Connection Pool)에 생성해 놓고 필요할 때마다 사용 후 반환하여 이를 재사용할 수 있게 해 줍니다.
- 대표적으로 DBCP의 종류에는 Apache Commons DBCP, HikariCP 등이 있습니다.
[ 더 알아보기 ] 💡 DBCP를 사용하면 JDBC를 사용하지 않는 것일까? - 결론적으로는 아닙니다. DBCP에서는 JDBC를 사용하여 데이터베이스와 연결하고 그 연결을 관리합니다. 그렇기에 DBCP를 사용한다고 해서 JDBC를 사용하지 않는 것은 아닙니다.
데이터베이스 연결 객체를 재사용하여 매번 새로운 연결을 생성하는 데 필요한 비용을 줄여줍니다.
효율적인 리소스 관리
미리 생성한 연결 객체들을 풀에 보관하고, 요청이 있을 때마다 제공하고 사용이 끝나면 반환함으로써 리소스를 효율적으로 사용합니다.
성능 향상
데이터베이스 연결을 미리 생성해두기 때문에, 연결을 요청할 때 연결 시간이 단축되어 성능이 향상됩니다.
2. 처리 과정
💡 처리 과정
- DBCP에 처리과정에 대해서 알아봅니다.
2.1. 최초 커넥션 풀 생성
💡 최초 커넥션 풀 생성 1. 애플리케이션 서버 시작 - 웹 애플리케이션에서 서버가 실행되면 커넥션 풀이 생성 준비가 됩니다. (* 해당 과정에서 Connection 연결 개수를 사전에 지정합니다.)
2. Connection Pool 생성 - Connection Pool 데이터베이스에 연결할 수 있는 여러 개의 Connection 객체를 미리 생성하고 관리하는 저장소입니다.
3. Connection 생성 → JDBC 연결 → Database - JDBC를 이용하여 데이터베이스에 연결합니다. 연결이 성공하면, 이 Connection 객체는 Connection Pool에 추가됩니다. - WAS가 데이터베이스 접속을 관리하므로 연결 풀링 같은 고급 기능을 이용할 수 있어 성능 향상에도 도움이 됩니다.
4. Connection Pool 구성 완료 - Connection Pool이 구성되면 애플리케이션은 필요할 때마다 Connection Pool에서 Connection 객체를 가져와서 사용할 수 있습니다.
[더 알아보기 ] 💡 커넥션 풀링(Connection Pooling)
- 여러 개의 데이터베이스 연결(Connection)을 묶어두어 필요할 때마다 재사용할 수 있게 하는 기술을 의미합니다. - 이는 매번 새로운 연결을 생성하고 종료하는 대신 이미 생성된 연결을 재사용함으로써 자원을 효율적으로 사용할 수 있게 합니다.
2.2. 커넥션 풀 사용과정
💡 커넥션 풀 사용 과정 - 클라이언트와 서버와 데이터베이스 간의 관계에서 처리과정으로 확인해 봅니다.
1. DB 처리 요청 (Client → Application App) - 클라이언트에서 Application App으로 DB 처리를 요청합니다.
2. Connection 요청 (Application App → Connection Pool) - Application에서 DB 처리를 위한 Connection Pool로 커넥션을 요청합니다.
[ 더 알아보기 ] 💡 JDBC와 다르게 Connection 객체를 매번 생성하는 게 아니라 미리 Connection 객체를 생성하고 사용하면 제공하고 사용을 마치면 반환하는 게 맞는 건가?
- JDBC에서 요청이 있을 때마다 Connection 객체를 생성하여 리소스를 많이 사용하였습니다. - 이에 반해 DBCP 방식에서는 미리 Connection 객체를 여러 개 생성해 놓습니다. 요청이 있을 때마다 이 Connection 객체들 중 하나를 제공하고 사용이 끝나면 반환하여 리소스를 효율적으로 사용할 수 있습니다.
3. 구성과정
3.1. 의존성 주입
💡 의존성 주입
- spring-boot-starter-data-jdbc를 통해 PostgreSQL 데이터베이스와 통신을 합니다. 또한 JDBC 내장으로 DBCP를 포함하고 있습니다. - org.postgresql:postgresql를 통해 실제 데이터베이스를 연결하기 위한 JDBC 드라이버입니다.
- 해당 설정 과정에서는 JDBC 내에 HikariCP를 이용하여 데이터베이스를 연결하는 과정을 구성하였습니다.
- driver-class-name: 사용할 JDBC 드라이버의 클래스 이름을 지정합니다. - jdbc-url: 데이터베이스에 연결하기 위한 JDBC URL을 지정합니다. - username: 데이터베이스 접속을 위한 사용자 이름을 지정합니다. - password: 데이터베이스 접속을 위한 비밀번호를 지정합니다. - pool-name: 데이터베이스 연결 풀의 이름을 지정합니다. - maximum-pool-size: 풀이 유지할 수 있는 최대 연결 수를 지정합니다.
spring:
datasource:
hikari:
driver-class-name: org.postgresql.Driver
jdbc-url: jdbc:log4jdbc:postgresql://localhost:5432/multiflex
username: localmaster
password: 1234
pool-name: Hikari Connection Pool # Alias
maximum-pool-size: 5
3.3. DBConfig
💡 DBConfig
- 해당 파일에서는 서버가 로드되는 시점에 @Configuration Hikari에 환경설정을 해주는 과정입니다. - HikariCP 설정과 최초 데이터베이스 Connection을 수행합니다.
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:/application.properties")
public class DBConfig {
final
ApplicationContext applicationContext;
public DBConfig(ApplicationContext ac) {
this.applicationContext = ac;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariConfig hikariConfig() {
return new HikariConfig();
}
@Bean
public DataSource dataSource() {
return new HikariDataSource(hikariConfig());
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean session = new SqlSessionFactoryBean();
session.setDataSource(dataSource);
session.setMapperLocations(applicationContext.getResources("classpath:mapper/*.xml"));
session.setTypeAliasesPackage("com.test.testApi.model");
session.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/mybatis-config.xml"));
return session.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
3.4. Application Server
💡 Application Server - 아래와 같이 Application Server가 실행될 때 DBCP인 HikariCP가 수행되어 Connection Pool이 생성됨을 확인하였습니다.
💡 [참고] HikariCP에 대한 설정에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
- 자바에서 이름 붙여진 객체를 찾거나 연결하기 위한 API입니다. 이를 통해 개발자들은 다양한 네이밍과 디렉터리 서비스에서 자바 소프트웨어 컴포넌트를 검색하고 사용할 수 있습니다.
- 즉 애플리케이션 내에서가 아닌 외부에서 이를 참조해서 가져오는 형태입니다. 데이터베이스 연결 측면에서 웹 애플리케이션 서버(WAS, Web Application Server)에 데이터베이스 접속 정보를 등록해 놓고, 이를 필요로 하는 웹 애플리케이션들이 WAS로부터 이 정보를 받아 사용할 수 있습니다. - 이는 WAS에서 데이터베이스 접속 정보를 관리하게 되므로, 각 웹 애플리케이션에서 별도로 데이터베이스 접속 정보를 관리할 필요가 없다는 것을 의미합니다.
[ 더 알아보기 ] 💡JNDI를 이용하여 WAS 내에 데이터베이스 접속정보를 구성하면 무엇이 좋을까?
1. 애플리케이션 코드에서 데이터베이스 접속정보를 직접 다루지 않아도 되므로 코드의 가독성과 유지보수성이 향상됩니다. 2. 통일된 방식으로 리소스를 관리할 수 있으므로 개발의 복잡성을 줄일 수 있습니다. 3. WAS가 데이터베이스 접속을 관리하므로 연결 풀링 같은 고급 기능을 이용할 수 있어 성능 향상에도 도움이 됩니다.