[Spring] JDBC API를 이용한 MySQL 연동 (Feat. DriverManager)
❓ JDBC(Java Database Connectivity)란?
프로젝트 진행 중 데이터를 보관하기 위해 DB를 사용하고자 한다.
자바 프로그램과 DB를 연결하기 위해서는 데이터를 주고 받을 수 있도록 도와주는 인터페이스인 JDBC API를 이용한다.
📌 프로젝트에 JDBC 적용해보기
나는 RDBMS 중 MySQL을 사용할 예정이므로 관련 의존성을 추가해주었다.
implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.30'
mysql-connector-java는 MySQL 연결에 필요한 JDBC 드라이버를 제공해준다.
1️⃣ UserDao 생성
[UserDao.java]
UserDao는 다음과 같이 생성하였다.
JDBC API에서 DB연결을 하는 방법으로는 Driver Manager를 이용하는 방법과 DataSource를 이용하는 방법 2가지가 있는데, 조금 더 레거시한 방법으로 구현해보고자 Driver Manager를 이용하였다.
package com.sping.dao;
import com.sping.domain.User;
import java.sql.*;
import java.util.List;
public class UserDao {
public List<User> selectAll() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "root");
PreparedStatement pstmt = conn.prepareStatement("select * from users");
ResultSet rs = pstmt.executeQuery();;
List<User> userList = null;
while(rs.next()) {
User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
userList.add(user);
}
conn.close();
pstmt.close();
rs.close();
return userList;
}
public User selectById(String sId) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "root");
PreparedStatement pstmt = conn.prepareStatement("select id, name, password from users where id = ?");
pstmt.setString(1, sId);
ResultSet rs = pstmt.executeQuery();
User user = null;
if(rs.next()) {
user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
}
conn.close();
pstmt.close();
rs.close();
return user;
}
public void add(User user) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "root");
PreparedStatement pstmt = conn.prepareStatement("insert into users(id, name, password) values (?, ?, ?)");
pstmt.setString(1, "id");
pstmt.setString(2, "name");
pstmt.setString(3, "password");
pstmt.executeUpdate();
conn.close();
pstmt.close();
}
public void deleteAll() throws ClassNotFoundException, SQLException {
lass.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "root");
PreparedStatement pstmt = conn.prepareStatement("delete from users");
pstmt.executeUpdate();
conn.close();
pstmt.close();
}
}
2️⃣ 중복된 로직 분리
위의 로직들은 DB와의 연결 코드와 연결 종료 코드가 매 메서드마다 반복된다는 것을 알 수 있다.
이를 분리하여 더 깔끔하게 작성해보자.
package com.sping.dao;
import com.sping.domain.User;
import java.sql.*;
import java.util.List;
public class UserDao {
public List<User> selectAll() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<User> userList = null;
try {
conn = ConnectionMaker.getConnection();
pstmt = conn.prepareStatement("select * from users");
rs = pstmt.executeQuery();
while(rs.next()) {
User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
userList.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
ConnectionClose.close(conn, pstmt, rs);
}
return userList;
}
public User selectById(String sId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null;
try{
conn = ConnectionMaker.getConnection();
pstmt = conn.prepareStatement("select id, name, password from users where id = ?");
pstmt.setString(1, sId);
rs = pstmt.executeQuery();
if(rs.next()) {
user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
ConnectionClose.close(conn, pstmt, rs);
}
return user;
}
public void add(User user) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = ConnectionMaker.getConnection();
pstmt = conn.prepareStatement("insert into users(id, name, password) values (?, ?, ?)");
pstmt.setString(1, "id");
pstmt.setString(2, "name");
pstmt.setString(3, "password");
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
ConnectionClose.close(conn, pstmt);
}
}
public void deleteAll() {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = ConnectionMaker.getConnection();
pstmt = conn.prepareStatement("delete from users");
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
ConnectionClose.close(conn, pstmt);
}
}
}
[ConnectionMaker.java]
package com.sping.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;
public class ConnectionMaker {
private static final Map<String, String> env = System.getenv();
private static final String dbHost = env.get("DB_HOST");
private static final String dbUser = env.get("DB_USER");
private static final String dbPassword = env.get("DB_PASSWORD");
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(dbHost, dbUser, dbPassword);
}
}
⚠ DB 연결에 필요한 정보들을 입력해줄 때, 해당 내용이 Git에 올라가게 되면 보안이 매우 취약해진다.
그러므로, IntelliJ의 우측 상단에 있는 Edit Configuration > Environment variables에 해당 내용을 설정해주고 위처럼 코드에는 임의의 설정값들을 적어준다.
[ConnectionClose.java]
package com.sping.dao;
public class ConnectionClose {
public static void close(AutoCloseable...autoCloseables) {
for(AutoCloseable ac : autoCloseables) {
try {
if (ac != null) {
ac.close();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
나는 DB 연결을 닫아주는 코드 역시 매번 메서드를 작성할 때마다 반복되기 때문에 별도의 클래스로 생성하였다.
위의 AutoCloseable 객체는 앞서 try{} 문에서 선언된 객체들에 대해 try문이 종료될 때 자동으로 자원을 해제해주는 기능을 가진 객체이다.
또한, AutoCloseable과 함께 쓰인 ... 파라미터는 가변인자라고 불리며, 유동적으로 여러 개의 매개변수를 받을 수 있어 오버로딩 시 편리하게 사용할 수 있다.
[User.java]
package com.sping.domain;
public class User {
private String id;
private String name;
private String password;
public User(String id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
}
마지막으로 해당 클래스는 User 클래스이다.
이렇게 오늘은 MySQL과 연결하기 위한 코드를 작성해보았다.
오늘 구현된 코드는 앞으로 리팩토링을 거쳐 점차 짧아질 예정이다.😊