자 이번에는 실제 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 - 웹프로그래밍 부스트코스 ] 를 개인적으로 공부하고 정리한 공간입니다. 잘못된 부분은 피드백 주시면 감사하겠습니다