Backend/Spring & SpringBoot

[Spring] JDBC API를 이용한 MySQL 연동 (Feat. DriverManager)

th42500 2022. 10. 18. 21:28

❓ 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과 연결하기 위한 코드를 작성해보았다.

오늘 구현된 코드는 앞으로 리팩토링을 거쳐 점차 짧아질 예정이다.😊