[JDBC] MySQL에서 JDBC 사용

2019. 3. 9. 01:46JAVA Back-End


자 이번에는 실제 JDBC를 통해서 자바코드로 MySQL에 쿼리를 날려보겠습니다. MySQL 드라이버를 사용하니 다른 DBMS(MongoDB, MariaDB.. 기타)를 사용하고자 하는 분들은 다른 포스팅을 참고해주시면 좋겠습니다.

또한 try-with-resource(줄여서 TWR)를 ResultSet를 사용해 결과값을 반환해 보여주는 SELECT문 경우에 TWR을 사용해서도 구현해보겠습니다.

try-catch-finally

try{

// Exception 발생가능한 코드

}catch(ClassNotFoundException e){

// 예외 처리

}catch(SQLException e){

// 예외 처리2 (2개, 3개 등등 multiple하게 catch문을 여러개 사용해서 예외처리할 수도 있습니다)

}

finally{

// 항상 실행되야 하는 부분

}

try-with-resource (Java7부터 추가되었습니다)

예외 발생 여부와 상관없이 finally에서 처리해줬던 Resource 객체(I/O 스트림, Socket, SQL )들의 close()메소드를 호출해서 안전하게 Resource를 종료해줍니다. try() 문에서  자원을 밖에서 사용하지 못하도록 설계되어 있습니다! 따라서 외부의 변수를 사용할 수 없습니다. 

Connection conn = null; 

이런 식으로 Resource객체를 try() 외부의 변수를 대입해 사용할 수 없습니다. (아래 코드는 예외를 발생시킵니다.)

try( conn = DriverManager.getConnection( dbUrl, dbUser, dbPassword ); ){

}

try(


// 명시적으로 close()하지 않아도 자동으로 호출해줍니다.

Connection conn = DriverManager.getConnection("db의 url","db 사용자","db 패스워드");

FileInputStream fis = new FileInputStream("exam.txt")

){

// Exception 발생가능한 코드

}catch(Exception e){

// 예외 처리

}    


자 이제 실제 실습을 해보겠습니다 :>

JDBC 실습


우선 Maven을 사용해 JDBC에서 제공해주는 MySQL 드라이버를 depencency를 통해 추가하고 여러 설정을 pom.xml에서 작업해줍니다.

 pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>kr.or.connect</groupId>
    <artifactId>jdbcexam</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>jdbcexam</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

  <!-- 의존성 관리 (외부 라이브러리) -->

   <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

<!-- buil도구 (jdk 1.5 -> 1.8로 빌드 EL태그 사용가능) -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


 Role.java


package com.jini.jdbcexam.dto;

//role이라는 테이블 정보
public class Role {
    private int roleId;
    private String description;
    
    public Role(int roleId, String description) {
        super();
        this.roleId = roleId;
        this.description = description;
    }
    //데이터를 꺼내고 넣고 이런 부분들이 수행되어야 하니 getter() , setter()를 만들어 줍니다.
    
    public Integer getRoleId() {
        return roleId;
    }
    public void setRoleId(int roleId) {
        this.roleId = roleId;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    @Override
    public String toString() {
        return "Role [roleId=" + roleId + ", description=" + description + "]";
    }
}

( 그냥 멤버변수를public으로 선언하면되지 private으로 선언하고 public으로 getter/setter 메소드를 만들어 접근하는 이유는 DB는 민감한 정보를 담을 수 있습니다.  중요한 정보는 이렇게 내부의 정보를 직접 접근, 조작할 수 없고 외부에서 접근할 수 있도록 설정한 메소드를 통해서만 접근하도록 캡슐화를 해줍니다.)


1) INSERT 문

RoleDao.java

package com.jini.jdbcexam.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.jini.jdbcexam.dto.Role;

public class RoleDao {
// 다른 메소드에서도 계속 사용되고 변하지 않으니 아예 상수처럼 사용하도록 static 멤버변수로 선언했습니다
    private static String dbUrl = "jdbc:mysql://localhost:3306/lol";
    private static String dbUser = "root";
    private static String dbPassword = "123456";

    public int addRole(Role role) {

        // select문은 조회 결과를 ResultSet으로 보여주지만 Insert는 테이블 결과를 반환하진 않으므로 ResultSet은 사용하지 x
        int insertCount = 0; // sql 실행하고 결과를 주듯 int값으로 변수 하나 선언.

        Connection connection = null;
//PreparedStatement는 쿼리문의 ?를 바인딩해서 사용할 수 있습니다.
        PreparedStatement statement = null;

        try {
//1. JDBC의 MySQL 드라이버를 로드해줍니다.        
            Class.forName("com.mysql.jdbc.Driver");

// 2. connection객체로 MySQL과 연결해줍니다
            connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword);

            String sql = "INSERT INTO role (role_id, description) VALUES (?,?);"; // 실행할 query문

            // 3. Connection의 인스턴스를 통한 preparedStatemet메소드를 사용하여 query문을 실행합니다.
statement = connection.prepareStatement(sql);
 // 4. 위의 query의 ?,?에 순서대로! 바인딩을 해줍니다.
// role_id는 int형인 setInt() / description은 string형인 setString()
            statement.setInt(1, role.getRoleId());
            statement.setString(2, role.getDescription());

            insertCount = statement.executeUpdate(); // 해당 쿼리가 실행되면 int값을 반환하는데 insertStatement 변수에 담는다.

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (statement != null) {
                try {
// 5. 사용이 끝난 후 해당 resource 객체(statement)를 close해줍니다.
                    statement.close();  
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
// 5. 사용이 끝난 후 해당 resource 객체(connection)를 close해줍니다.
                    connection.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }
        return insertCount;
    }
}

JDBCInsert.java

package com.jini.jdbcexam;

import com.jini.jdbcexam.dao.RoleDao;
import com.jini.jdbcexam.dto.Role;

public class JDBCInsert {
    public static void main(String[] args) {
        int roleId = 600;
        String description = "PM";

        Role role = new Role(roleId, description);
        RoleDao dao = new RoleDao();
        int insertCount = dao.addRole(role);

        System.out.println(insertCount);

    }
}

결과)

mysql> select * from role;

+---------+-------------+

| role_id | description |

+---------+-------------+

|     100 | developer   |

|     200 | publisher   |

|     300 | designer    |

|     400 | Marketer    |

|     600 | PM          |

+---------+-------------+

5 rows in set (0.00 sec)



2) SELECT문

SELECT는 쿼리가 실행되면 검색 결과가 반환됩니다. 따라서 다른 query와 다르게 ResultSet을 사용해 통보해줍니다.

RoleDao.java

 
  // ...편의상 위의 코드는 생략...
    private static String dbUrl = "jdbc:mysql://localhost:3306/lol";
    private static String dbUser = "root";
    private static String dbPassword = "123456"

// 객체 role을 리턴하는 메소드
public Role getRole(int roleId) {
Role role = null;
Connection connection = null; // db 연결 객체
PreparedStatement statement = null; // 쿼리 실행 객체
ResultSet rs = null; // 결과값

try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword); // mysql 드라이버로 연결
// PreparedStatement 쿼리의 값이 매번 수행할 때 마다 달라지는데 인자로 들어온 값(role_id)이 ?를 바인딩 되는 부분만
// 바뀝니다.
String sql = "SELECT role_id,description FROM role WHERE role_id=?";
statement = connection.prepareStatement(sql);
statement.setInt(1, roleId); // 첫번째는 parameterIndex 물음표 순서.
rs = statement.executeQuery();

if (rs.next()) {
int id = rs.getInt("role_id");
String desc = rs.getString("description");
role = new Role(id, desc);
}
} catch (Exception e) {
e.printStackTrace();

} finally { // finally는 반드시 수행되는 구절 -DB를 닫아주는 코드
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return role;
}

JDBCSelect.java - SELECT실행시키는 main함수.

package com.jini.jdbcexam;

import com.jini.jdbcexam.dao.RoleDao;
import com.jini.jdbcexam.dto.Role;

public class JDBCSelect {
    public static void main(String[] args) {
        RoleDao dao = new RoleDao();
        Role role = dao.getRole(100);
        
        System.out.println(role);
    }
}

결과)

mysql> SELECT role_id, description FROM role WHERE role_id=100;

+---------+-------------+

| role_id | description |

+---------+-------------+

|     100 | developer   |

+---------+-------------+

1 row in set (0.00 sec)


ArrayList를 이용해 여러 줄의 쿼리를 정렬해서 검색할 수도 있습니다. 이 과정에서 위에서 try-with-resource를 이용해서 코드를 작성하여 더 간결하게코드를 작성할 수 있습니다. 

RoleDao.java

 
// ...편의상 위의 코드는 생략...
 public List<Role> getRoles(){
        List<Role> list = new ArrayList<>();
        
        try{
            Class.forName("com.mysql.jdbc.Driver");
        }catch(Exception e) {
            e.printStackTrace();
        }
        
        String sql = "SELECT role_id, description FROM role ORDER BY role_id desc";
        try(
            Connection connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
            PreparedStatement statement = connection.prepareStatement(sql)){
            try(ResultSet rs = statement.executeQuery()){
                
                while(rs.next()) {
//role_id에 이어 2번 째로 찾으므로. 아니면 "description" 컬럼으로 얻어올 수도 있습니다.
                    String desc = rs.getString(2);
                    int id = rs.getInt("role_id");
                    Role role = new Role(id,desc);
                    //list를 반복할 떄마다 인스턴스 생성해 list에 추가합니다. 
list.add(role);
                }
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
        return list;
    }

JDBCSelectOrder.java

package com.jini.jdbcexam;

import java.util.List;

import com.jini.jdbcexam.dao.RoleDao;
import com.jini.jdbcexam.dto.Role;

public class JDBCExam3 {
     public static void main(String[] args) {
         RoleDao dao = new RoleDao();
         List<Role> list = dao.getRoles();
        
// 방법1) new for loop. :의 사용 
/*       for(Role role: list) {
           System.out.println(role);
*/       }

      // 방법2) forEach문을 사용 
/*       list.forEach(action -> {
           System.out.println(action);
*/       });

// 방법3) 기본적인 for문을 이용해 출력        
         for(int i=0; i< list.size(); i++ ) {
             System.out.println(list.get(i));
         }
     }
}

INSERT와 SELECT를 정리했습니다. UPDATE와 DELET에서도 INSERT와 비슷한 방식(ReseltSet 없이)으로 로직을 구성할 수 있습니다.

[참고] https://nyhooni.tistory.com/71


[ edwith - 웹프로그래밍 부스트코스 ] 를 개인적으로 공부하고 정리한 공간입니다. 잘못된 부분은 피드백 주시면 감사하겠습니다

'JAVA Back-End' 카테고리의 다른 글

[Web API] Web API 정리  (0) 2019.03.09
[REST API] REST API인가? Web API인가?  (0) 2019.03.09
[JDBC] JDBC (Java Database Connectivity)  (1) 2019.03.06
[Servlet]Maven이란?  (0) 2019.03.05
[JSP/Servlet] JSTL (JSP Standard Tag Library)  (0) 2019.03.02