
본 시리즈에서는 데이터베이스의 개념을 정리하고 필요시 실습을 진행합니다.
Ubuntu-24.04)10.11.8-MariaDB)2024.2.2)correto-1.8)Ultimate Edition)JDBC (Java Database Connectivity)는 Java 애플리케이션이 데이터베이스와 상호작용할 수 있도록 지원하는 표준 API입니다.
데이터베이스에 SQL 문장을 전달하고, 그 결과를 Java 애플리케이션에서 처리할 수 있도록 설계되었습니다.JDBC를 통해 Java 프로그램은 다양한 관계형 데이터베이스와 연결할 수 있습니다.SQL 문장을 데이터베이스에 전달하고, 결과를 받아 처리합니다.데이터베이스로부터 데이터를 조회하거나, 수정/삭제와 같은 작업을 수행할 수 있습니다.데이터베이스 벤더에 상관없이 동일한 API를 사용하여 데이터베이스와 상호작용할 수 있습니다.DriverManager
DriverManager.getConnection(url, user, password).connector와 유사합니다.Connection
SQL 쿼리를 실행하기 위해 필요한 객체를 생성.connection.createStatement()Statement
Statement: 정적인 SQL 실행 (해킹 위험으로 거의 쓰이지 않음)PreparedStatement: 파라미터화된 SQL 실행 (주로 쓰이는 형태)CallableStatement: DB에 저장된 Procedure를 호출cursor와 유사합니다.ResultSet
JDBC 드라이버 로드
JDBC 드라이버를 로드하여 DriverManager에 등록합니다.데이터베이스 연결
DriverManager.getConnection() 메서드를 사용해 데이터베이스와 연결합니다.SQL 실행
Statement 또는 PreparedStatement 객체를 통해 SQL 문장을 실행합니다.결과 처리
SELECT 쿼리의 경우 ResultSet 객체를 사용하여 결과를 처리합니다.INSERT, UPDATE, DELETE의 경우 실행 결과로 영향을 받은 행 수를 반환합니다.연결 종료
Connection, Statement, ResultSet)를 명시적으로 종료합니다.try-with-resources로 자동으로 닫히도록 할 수도 있습니다.DB 벤더별로 다른 API를 사용할 필요 없이 JDBC를 통해 통합된 방법으로 접근 가능.MySQL, MariaDB, PostgreSQL, Oracle 등 다양한 관계형 데이터베이스와 호환.JDBC를 통해 Java 애플리케이션과 MariaDB를 연결하기 위해 필요한 설정을 단계별로 다뤄보겠습니다.
MariaDB Connector는 MariaDB와 Java 애플리케이션 간의 통신을 가능하게 하는 JDBC 드라이버입니다.
MariaDB Connector/J 다운로드 페이지로 이동합니다.
.jar 파일을 다운로드합니다.편의상 진행하는 Project의 디렉토리에 위치하도록 파일을 옮깁니다.

lib이라는 디렉토리를 만들어서 넣어주었습니다.IntelliJ에서 프로젝트를 열고 File > Project Structure로 이동합니다.

Modules > Dependencies 탭을 선택한 뒤, + > JARs or Directories를 클릭합니다.

다운로드한 MariaDB Connector .jar 파일을 추가합니다.

프로젝트가 새로 빌드되며, MariaDB 드라이버가 프로젝트에 포함됩니다.
Maven을 사용하는 경우, 의존성을 추가하면 다운로드 및 설정이 자동으로 완료됩니다.
pom.xml 파일 수정
프로젝트의 pom.xml 파일에 아래 의존성을 추가합니다:
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.1.2</version>
</dependency>
Maven 프로젝트 동기화
IntelliJ IDEA 상단에서 "Reload Maven Project" 아이콘을 클릭하여 의존성을 다운로드합니다.
Gradle을 사용하는 경우, MariaDB Connector 의존성을 build.gradle 파일에 추가합니다.
build.gradle 파일 수정
dependencies {
implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.2'
}
프로젝트 동기화
IntelliJ IDEA 상단의 "Sync Now"를 클릭하여 Gradle 의존성을 다운로드합니다.
참고 (Maria DB 데이터베이스 생성)
- 서버 컴퓨터에서 MySQL/MariaDB의
root유저로 접속 (이 내용까진 자세히 다루진 않습니다)- DB & User 생성 및 권한 부여
-- 새 데이터베이스 생성 CREATE DATABASE blog_jdbc; -- 새 사용자 생성 및 권한 부여 CREATE USER 'jdbc_user'@'%' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON blog_jdbc.* TO 'jdbc_user'@'%'; FLUSH PRIVILEGES;
MariaDB와 Java 애플리케이션을 연결했다고 가정하고, SQL을 실행하기 위해 사용되는 주요 JDBC 객체와 그 동작 방식을 다뤄보겠습니다.
JDBC를 통해 MariaDB에 연결하기 위해서는 데이터베이스 URL(String)이 필요합니다.
URL은 연결 정보를 포함하며 아래와 같은 형식을 따릅니다:
jdbc:mariadb://<host>:<port>/<database>?<options>
jdbc:mariadb://: MariaDB JDBC 드라이버를 사용한다는 의미.<host>: 데이터베이스 서버의 IP 주소 또는 호스트 이름. (예: localhost, 127.0.0.1)<port>: MariaDB가 사용하는 포트 번호(기본값: 3306).<database>: 연결하려는 데이터베이스 이름.<options>: 추가적인 옵션(예: 인증이나 SSL 설정).String url = "jdbc:mariadb://localhost:3306/blog_jdbc?useSSL=false";
String user = "jdbc_user";
String password = "your_password";
JDBC 드라이버를 로드하여 DriverManager가 MariaDB와 연결할 수 있도록 준비합니다.
Class.forName() 메서드를 사용하여 MariaDB 드라이버 클래스를 로드합니다.Class.forName("org.mariadb.jdbc.Driver");META-INF/services/java.sql.Driver에 정의된 정보를 통해 자동으로 로드됩니다. try {
Class.forName("org.mariadb.jdbc.Driver");
System.out.println("MariaDB JDBC Driver 로드 성공!");
} catch (ClassNotFoundException e) {
System.out.println("JDBC Driver 로드 실패: " + e.getMessage());
}
Connection 객체는 데이터베이스와의 연결을 나타냅니다.
DriverManager.getConnection() 메서드를 호출하여 Connection 객체를 생성합니다.
URL, 사용자명, 비밀번호를 인자로 전달합니다.String url = "jdbc:mariadb://localhost:3306/blog_jdbc?useSSL=false";
String user = "jdbc_user";
String password = "your_password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
System.out.println("데이터베이스 연결 성공!");
} catch (SQLException e) {
e.printStackTrace();
}
createStatement()
Statement 객체를 생성.Statement stmt = conn.createStatement();prepareStatement()
PreparedStatement 객체를 생성. PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users VALUES (?, ?)");prepareCall()
CallableStatement 객체를 생성. CallableStatement cstmt = conn.prepareCall("{CALL procedure_name(?)}");close()
try-with-resources를 사용하여 자동으로 닫음.JDBC에서는 SQL 문장을 실행하기 위해 Statement, PreparedStatement, CallableStatement 세 가지 형태의 객체를 제공합니다.
특징
사용법
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement()) {
// SELECT 문 실행
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println("ID: " + rs.getInt("id") + ", Name: " + rs.getString("name"));
}
// INSERT 문 실행
int rowsInserted = stmt.executeUpdate("INSERT INTO users (name, email) VALUES ('John', 'john@example.com')");
System.out.println(rowsInserted + " rows inserted.");
} catch (SQLException e) {
e.printStackTrace();
}
Statement가 잘 쓰이지 않는 이유
- SQL Injection 취약
- 동적 쿼리에서 사용자 입력값을 직접 포함하면
SQL Injection 공격에 노출됩니다.String userInput = "john@example.com'; DROP TABLE users; --"; String query = "SELECT * FROM users WHERE email = '" + userInput + "'"; // 의도치 않은 SQL 실행 가능- 성능 저하
- 매번 쿼리를 새로 컴파일하기 때문에 동일한 SQL 문장을 반복 실행할 경우 성능이 떨어집니다.
특징
SQL Injection 방지.사용법
String query = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = conn.prepareStatement(query)) {
// 파라미터 설정
pstmt.setString(1, "Jane");
pstmt.setString(2, "jane@example.com");
// SQL 실행
int rowsInserted = pstmt.executeUpdate();
System.out.println(rowsInserted + " rows inserted.");
} catch (SQLException e) {
e.printStackTrace();
}
특징
사용법
try (Connection conn = DriverManager.getConnection(url, user, password);
CallableStatement cstmt = conn.prepareCall("{CALL insert_user(?, ?)}")) {
// 입력 매개변수 설정
cstmt.setString(1, "Alice");
cstmt.setString(2, "alice@example.com");
// 저장 프로시저 실행
cstmt.execute();
System.out.println("Stored procedure executed successfully.");
} catch (SQLException e) {
e.printStackTrace();
}
CallableStatement 주요 메서드
setXxx(int parameterIndex, Xxx value): 입력 매개변수 설정.registerOutParameter(int parameterIndex, int sqlType): 출력 매개변수 등록.| 객체 유형 | 주요 용도 | 특징 | 장점 및 단점 |
|---|---|---|---|
| Statement | 정적 SQL | 매번 SQL을 문자열로 전달 | SQL Injection 취약, 성능 저하 |
| PreparedStatement | 파라미터화된 SQL | 파라미터 지원, 컴파일된 쿼리 구조 재사용 | 안전하고 성능 우수 |
| CallableStatement | 저장 프로시저 호출 | IN/OUT/INOUT 매개변수 지원 | 복잡한 로직을 DB 내에서 처리 가능 |
| 메서드 | 용도 | 반환값 | 설명 |
|---|---|---|---|
| executeQuery() | SELECT 문 실행 | ResultSet | 조회 결과를 행(Row) 단위로 처리 |
| executeUpdate() | INSERT, UPDATE, DELETE 실행 | 영향을 받은 행(row) 개수 반환 | DML(데이터 수정) 작업에서 사용 |
| execute() | 모든 SQL 문 실행 가능 | true 또는 false | SELECT일 경우 true, 아니면 false |
SELECT 쿼리 실행 시 결과를 ResultSet 객체로 반환합니다.
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println("ID: " + rs.getInt("id") + ", Name: " + rs.getString("name"));
}
INSERT, UPDATE, DELETE 쿼리 실행 시 영향을 받은 행(row) 개수를 반환합니다.
int rowsAffected = stmt.executeUpdate("DELETE FROM users WHERE id = 1");
System.out.println(rowsAffected + " rows deleted.");
SELECT 쿼리인지 여부에 따라 반환값이 달라집니다.
boolean hasResultSet = stmt.execute("DROP TABLE test_table");
if (!hasResultSet) {
System.out.println("테이블 삭제 성공.");
}
이제 JDBC를 사용하여 실제로 CREATE, READ, UPDATE, DELETE(CRUD) 작업을 구현해 보겠습니다.
velog_jdbc_kj이며, IP 주소는 보안을 위해 예제 코드에서 가리고 설명하겠습니다.jdbc:mariadb://<DB_IP>:3306/velog_jdbc_kj
<DB_IP>는 보안상 표시하지 않았습니다. "jdbc:mariadb://127.0.0.1:3306/velog_jdbc_kj" (localhost)MariaDB 테이블 생성: users실습을 위한 테이블을 준비합니다.
USE velog_jdbc_kj; # 필요시 사용
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
);

Java에서 데이터를 효율적으로 다루기 위해 User 클래스를 정의합니다.
public class User {
private Integer id; // id는 null 가능 (삽입 시 DB에서 자동 생성)
private String userName;
private String email;
public User(Integer id, String userName, String email) {
this.id = id;
this.userName = userName;
this.email = email;
}
public User(String userName, String email) {
this.userName = userName;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{id=" + id +
", name='" + userName + '\'' +
", email='" + email + '\'' + '}';
}
}
INSERT 문을 사용해 데이터베이스에 새로운 사용자 정보를 삽입합니다.
id는 AUTO_INCREMENT로 자동 생성됩니다.User 객체를 생성하여 데이터를 전달합니다.INSERT INTO users (user_name, email) VALUES (?, ?)
?는 SQL 파라미터로, PreparedStatement를 사용해 동적으로 값을 설정합니다.import java.sql.*;
public class CreateUser {
public static void main(String[] args) {
String url = "jdbc:mariadb://<DB_IP>:3306/velog_jdbc_kj";
String user = "jdbc_user";
String password = "your_password";
String query = "INSERT INTO users (user_name, email) VALUES (?, ?)";
User newUser = new User("John Doe", "john.doe@example.com");
// 연결 (try-with-resource)
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement cursor = conn.prepareStatement(query)) {
// SQL 파라미터 설정
cursor.setString(1, newUser.getUserName());
cursor.setString(2, newUser.getEmail());
// SQL 실행
int rowsInserted = cursor.executeUpdate();
System.out.println(rowsInserted + " row(s) inserted.");
} catch (SQLException e) {
e.printStackTrace(System.err);
}
}
}
- 데이터베이스 연결
DriverManager.getConnection()메서드를 사용해 데이터베이스와 연결합니다.- 연결 정보는
url, user, password로 지정합니다.- PreparedStatement 생성
PreparedStatement는 SQL 문을 사전에 컴파일하고, 동적으로 값을 설정할 수 있는 객체입니다.- 이 예제에서는
query문자열을 기반으로PreparedStatement객체를 생성합니다.- SQL 파라미터 설정
cursor.setString(1, newUser.getUserName()): SQL의 첫 번째?에 사용자 이름 설정.cursor.setString(2, newUser.getEmail()): SQL의 두 번째?에 이메일 주소 설정.- 참고로, SQL에선
0부터 숫자를 세지 않고1부터 셉니다.- SQL 실행
executeUpdate(): INSERT, UPDATE, DELETE 문을 실행하고 영향을 받은 행의 개수를 반환합니다.- 반환된
rowsInserted는 삽입된 행의 개수입니다.- 예외 처리
SQLException발생 시 에러 메시지를 출력합니다.try-with-resource를 사용하여Connection및PreparedStatement객체를 자동으로 닫습니다.
1 row(s) inserted.SELECT * FROM users;
SELECT 문을 사용해 데이터베이스에서 사용자 정보를 조회합니다.
SELECT * FROM users
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class ReadUsers {
public static void main(String[] args) {
String url = "jdbc:mariadb://<DB_IP>:3306/velog_jdbc_kj";
String user = "jdbc_user";
String password = "your_password";
String query = "SELECT * FROM users";
List<User> users = new ArrayList<>();
// 연결 (try-with-resource)
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
// ResultSet을 User 객체로 변환하여 리스트에 추가
while (rs.next()) {
User userObj = new User(
rs.getInt("id"),
rs.getString("user_name"),
rs.getString("email")
);
users.add(userObj);
}
// 조회된 데이터 출력
users.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace(System.err);
}
}
}
- 데이터베이스 연결
DriverManager.getConnection()메서드를 사용하여 데이터베이스에 연결합니다.- Statement 생성
Statement는 정적 SQL 쿼리를 실행하는 객체로, 이 예제에서는SELECT문에 사용됩니다.- SQL 실행 및 ResultSet 처리
stmt.executeQuery(query)는SELECT문을 실행하고 결과 집합(ResultSet)을 반환합니다.ResultSet의 각 행(row)을 읽어User객체로 변환하고, 이를 리스트(List<User>)에 추가합니다.- 데이터 출력
- 리스트의 모든
User객체를 출력합니다.
User{id=1, name='John Doe', email='john.doe@example.com'}
User{id=2, name='Cheong Kyung Jae', email='dankool@naver.com'}
UPDATE 문을 사용해 데이터베이스에서 특정 사용자의 정보를 수정합니다.
id 값을 조건으로 사용해 특정 사용자를 선택하여 수정합니다.PRIMARY KEY를 이용하지만 필요에 따라 조건을 추가할 수 있습니다.UPDATE users SET email = ? WHERE id = ?
import java.sql.*;
public class UpdateUser {
public static void main(String[] args) {
String url = "jdbc:mariadb://<DB_IP>:3306/velog_jdbc_kj";
String user = "jdbc_user";
String password = "your_password";
String query = "UPDATE users SET email = ? WHERE id = ?";
User updatedUser = new User(1, "John Doe", "john.new@example.com");
// 연결 (try-with-resource)
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement cursor = conn.prepareStatement(query)) {
// SQL 파라미터 설정
cursor.setString(1, updatedUser.getEmail());
cursor.setInt(2, updatedUser.getId());
// SQL 실행
int rowsUpdated = cursor.executeUpdate();
System.out.println(rowsUpdated + " row(s) updated.");
} catch (SQLException e) {
e.printStackTrace(System.err);
}
}
}
- 데이터베이스 연결
DriverManager.getConnection()메서드로 데이터베이스와 연결합니다.- PreparedStatement 생성
PreparedStatement를 사용하여 SQL 파라미터를 동적으로 설정합니다.- SQL 파라미터 설정
cursor.setString(1, updatedUser.getEmail()): SQL의 첫 번째?에 새로운 이메일 설정.cursor.setInt(2, updatedUser.getId()): SQL의 두 번째?에 수정할 대상의id설정.- SQL 실행
executeUpdate()는 수정된 행의 개수를 반환합니다.
1 row(s) updated.SELECT * FROM users;
DELETE 문을 사용해 데이터베이스에서 특정 사용자의 정보를 삭제합니다.
id 값을 조건으로 사용해 특정 사용자를 삭제합니다.DELETE FROM users WHERE id = ?
import java.sql.*;
public class DeleteUser {
public static void main(String[] args) {
String url = "jdbc:mariadb://<DB_IP>:3306/velog_jdbc_kj";
String user = "jdbc_user";
String password = "your_password";
String query = "DELETE FROM users WHERE id = ?";
User userToDelete = new User(1, null, null);
// 연결 (try-with-resource)
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement cursor = conn.prepareStatement(query)) {
// SQL 파라미터 설정
cursor.setInt(1, userToDelete.getId());
// SQL 실행
int rowsDeleted = cursor.executeUpdate();
System.out.println(rowsDeleted + " row(s) deleted.");
} catch (SQLException e) {
e.printStackTrace(System.err);
}
}
}
- 데이터베이스 연결
DriverManager.getConnection()으로 데이터베이스에 연결합니다.- PreparedStatement 생성
PreparedStatement를 사용하여 SQL 파라미터를 설정합니다.- SQL 파라미터 설정
cursor.setInt(1, userToDelete.getId()): 삭제할 대상의id를 SQL의 첫 번째?에 설정합니다.- SQL 실행
executeUpdate()는 삭제된 행의 개수를 반환합니다.
1 row(s) updated.SELECT * FROM users;
이번 포스팅에서는 JDBC(Java Database Connectivity)를 사용하여 Java 애플리케이션과 MariaDB 데이터베이스를 연결하고, 이를 통해 CRUD 작업을 수행하는 방법을 다뤘습니다.
Java 애플리케이션에서 데이터베이스와 상호작용하기 위한 표준 API입니다.DriverManager, Connection, Statement, PreparedStatement, CallableStatement, ResultSet 등 JDBC의 주요 객체를 이해하고 활용하는 것이 중요합니다.PreparedStatement는 Statement에서 일어날 수 있는 SQL Injection 방지와 성능 최적화를 가능하게 하기 때문에 일반적으로 가장 많이 활용됩니다.이번 포스팅에서는 기본 JDBC 활용법에 초점을 맞췄습니다.
Java 개발자에게 가장 기본적이면서도 중요한 기술 중 하나이며, SQL을 직접 제어할 수 있다는 점에서 강력합니다. 이후의 포스팅은 데이터베이스 및 JDBC의 내용 중 필요에 따라서 추가적으로 정리가 필요한 부분들이 생기면 작성해볼 예정입니다. (Spring이나 JPA는 추후에 따로 시리즈를 만들게 될 것 같습니다..)