부스트코스 강의를 듣고 정리한 내용.

Java DataBase Connectivity

JDBC 개요

JDBC란?

  • 자바를 이용한 DB 접속과 SQL문의 실행, 그리고 실행 결과로 얻어진 데이터의 핸들링을 제공하는 방법과 절차에 대한 규약
  • 자바 프로그램 내에서 SQL문을 실행하기 위한 자바 API
  • SQL과 프로그래밍 언어의 통합 접근 중 한 형태

자바는 표준 인터페이스인 JDBC API를 제공한다. 데이터베이스 벤더, 또는 기타 third party에서는 JDBC 인터페이스를 구현한 드라이버를 제공한다.

환경 구성

  • JDK 설치
  • JDBC 드라이브 설치
    • 메이븐에 다음과 같은 dependency를 추가한다. MySQL에서 다운로드 한다. ```xml
    mysql mysql-connector-java 5.1.45

    ```

  • Java API Reference
  • JDBC 튜토리얼

JDBC를 이용한 프로그래밍 방법

  1. import java.sql.*;
  2. 드라이버를 load한다.
  3. Connection 객체를 생성한다. 커넥션 객체란 DB가 접속됐을 때 얻어내줄 수 있는 객체를 말한다.
  4. Statement 객체를 생성 및 질의 수행
  5. SQL문에 결과물이 있다면 ResultSet 객체를 생성한다.
  6. 모든 객체를 닫는다.

JDBC 클래스의 생성 관계

JDBC 클래스의 생성 관계

JDBC 사용

  • import
      import java.sql.*;
    
  • driver load
      Class.forName("com.mysql.jdbc.Driver"); // "com.mysql.jdbc.Driver" 객체를 메모리에 올림
    
  • Connection 얻기
      String dburl = "jdbc:mysql://localhost/dbName";
      Connection con = DriverManager.getConnection(dburl, ID, PWD);
    
    public static Connection getConnection() throws Exception {
        String url = "jdbc:oracle:thin:@117.16.46.111:1521.xe";
        String user = "smu";
        String password = "smu";
        Connection conn = null;	Class.forName("oracle.jdbc.driver.OracleDriver");
        conn = DriverManager.getConnection(url, user, password);
        return conn;
    }
    
  • statement 객체 생성
      Statement stmt = con.createStatement();
    
  • 쿼리문 실행
      ResultSet rs = stmt.executeQuery('select no from user');
    

    실행할 쿼리문에 따라 메소드가 달라진다.

    • any SQL
        `stmt.execute(query); //any SQL
      
    • SELECT
        stmt.executeQuery(query);
      
    • INSERT, UPDATE, DELETE
        stmt.executeUpdate(query);
      
  • ResultSet으로 결과 받기
      ResultSet rs = stmt.executeQuery("select no from user");
      while(rs.next()) 
          // 하나씩 실행하며 col 이름이 no인 값을 꺼내옴
          System.out.println(rs.getInt("no"));
    
  • Close 늦게 열린 순서대로 닫아주면 된다.
      rs.close();
      stmt.close();
      con.close();
    

JDBC 실습

지난번에 만들어뒀던 connectdb 데이터베이스로 실습을 해볼 것이다. 먼저 jdbcexam이라는 이름의 메이븐 프로젝트를 작성하고 pom.xml을 수정한다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<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>

  <name>jdbcexam</name>
  <url>http://www.example.com</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>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

그 다음 무엇을 할 것인지 생각해보자. 우선 데이터들을 가져올 수 있는 객체가 필요하다.

connectdb의 ROLE이라는 테이블에 대한 정보부터 담아보자. DESC ROLE; 명령어를 통해 살펴본 ROLE 테이블의 정보는 아래 표와 같다.

Field Type Null Key Default Extra
role_id int(11) NO PRI NULL  
description varchar(100) YES   NULL  

각 테이블의 정보를 담을 객체를 만들기 위해 먼저 클래스부터 작성할 것이다. src/main/java에서 Role 클래스를 만든다. 그리고 관련 있는 클래스들끼리 모아두기 위해 패키지를 만들어 작업해주자.

ROLE이라는 테이블은 컬럼 두 개를 담을 공간을 마련하면 되니까 컬럼에 해당하는 변수 두 roleIddescription을 데이터 타입에 맞춰 선언해준다.

private Integer roleId;
private String description;

출력을 편하게 하기 위해 toString()메소드 오버라이딩이랑 getter/setter 등등 까지 작성해준다.

package kr.or.connect.jdbcexam.dto;

public class Role {
	private Integer roleId; // int
	private String description; // varchar
	
	public Role() {
	}
	
	public Role(Integer roleId, String description) {
		super();
		this.roleId = roleId;
		this.description = description;
	}
	
	public Integer getRoleId() {
		return roleId;
	}
	public void setRoleId(Integer 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 + "]";
	}
}

다음에는 ROLE 테이블에 있는 정보를 CRUD하는 기능을 갖는 클래스 RoleDao를 작성한다.

package kr.or.connect.jdbcexam.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import kr.or.connect.jdbcexam.dto.Role;

public class RoleDao {
	// DB와 연결할 때마다 필요하므로 상수로 선언해버리기
	private static String dbUrl = "jdbc:mysql://localhost:3306/connectdb?serverTimezone=Asia/Seoul&useSSL=false";
	private static String dbUser = "connectuser";
	private static String dbpasswd = "connect123!@#";
	
	// Role 하나를 가져오는 메소드
	public Role getRole(Integer roleId) {
		Role role = null;
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			Class.forName("com.mysql.jdbc.Driver"); // driver 로드
			conn = DriverManager.getConnection(dbUrl, dbUser, dbpasswd); // connection 얻기
			String sql = "SELECT description, role_id FROM role WHERE role_id = ?";
			ps = conn.prepareStatement(sql);
			ps.setInt(1, roleId); // 첫 번째 '?' 자리에 roleId를 바인딩 해주세요! 라는 의미
			rs = ps.executeQuery(); // 쿼리문 실행

			// 결괏값이 있다면 첫 번째 레코드로 코드를 이동시키고 true 리턴
			if(rs.next() ) {
				// SELECT 문에서 나열한 컬럼 순서대로 꺼내와야 함
				String description = rs.getString(1); 
				int id = rs.getInt("role_id");
				role = new Role(id, description);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 닫아주기
		// 닫아줄 때도 에러가 발생할 수 있으므로 예외처리가 필요하다.
			if(rs != null) { // null인 경우에는 닫을 수 없으므로 null이 아닐 때만 닫아줘야 함
				try {
					rs.close();
				} catch(SQLException e) {
					e.printStackTrace();
				}
			}
			if(ps != null) {
				try {
					ps.close();
				} catch(SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch(SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return role;
	}
}

JDBC 실습

지난번에 만들어뒀던 connectdb 데이터베이스로 실습을 해볼 것이다. 먼저 jdbcexam이라는 이름의 메이븐 프로젝트를 작성하고 pom.xml을 수정한다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<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>

  <name>jdbcexam</name>
  <url>http://www.example.com</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>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

그 다음 무엇을 할 것인지 생각해보자. 우선 데이터들을 가져올 수 있는 객체가 필요하다.

connectdb의 ROLE이라는 테이블에 대한 정보부터 담아보자. DESC ROLE; 명령어를 통해 살펴본 ROLE 테이블의 정보는 아래 표와 같다.

Field Type Null Key Default Extra
role_id int(11) NO PRI NULL  
description varchar(100) YES   NULL  

각 테이블의 정보를 담을 객체를 만들기 위해 먼저 클래스부터 작성할 것이다. src/main/java에서 Role 클래스를 만든다. 그리고 관련 있는 클래스들끼리 모아두기 위해 패키지를 만들어 작업해주자.

ROLE이라는 테이블은 컬럼 두 개를 담을 공간을 마련하면 되니까 컬럼에 해당하는 변수 두 roleIddescription을 데이터 타입에 맞춰 선언해준다.

private Integer roleId;
private String description;

출력을 편하게 하기 위해 toString()메소드 오버라이딩이랑 getter/setter 등등 까지 작성해준다.

package kr.or.connect.jdbcexam.dto;

public class Role {
	private Integer roleId; // int
	private String description; // varchar
	
	public Role() {
	}
	
	public Role(Integer roleId, String description) {
		super();
		this.roleId = roleId;
		this.description = description;
	}
	
	public Integer getRoleId() {
		return roleId;
	}
	public void setRoleId(Integer 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 + "]";
	}
}

다음에는 ROLE 테이블에 있는 정보를 CRUD하는 기능을 갖는 클래스 RoleDao를 작성한다.

package kr.or.connect.jdbcexam.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import kr.or.connect.jdbcexam.dto.Role;

public class RoleDao {
	// DB와 연결할 때마다 필요하므로 상수로 선언해버리기
	private static String dbUrl = "jdbc:mysql://localhost:3306/connectdb?serverTimezone=Asia/Seoul&useSSL=false";
	private static String dbUser = "connectuser";
	private static String dbpasswd = "connect123!@#";
	
	// Role 하나를 가져오는 메소드
	public Role getRole(Integer roleId) {
		Role role = null;
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			Class.forName("com.mysql.jdbc.Driver"); // driver 로드
			conn = DriverManager.getConnection(dbUrl, dbUser, dbpasswd); // connection 얻기
			String sql = "SELECT description, role_id FROM role WHERE role_id = ?";
			ps = conn.prepareStatement(sql);
			ps.setInt(1, roleId); // 첫 번째 '?' 자리에 roleId를 바인딩 해주세요! 라는 의미
			rs = ps.executeQuery(); // 쿼리문 실행

			// 결괏값이 있다면 첫 번째 레코드로 코드를 이동시키고 true 리턴
			if(rs.next() ) {
				// SELECT 문에서 나열한 컬럼 순서대로 꺼내와야 함
				String description = rs.getString(1); 
				int id = rs.getInt("role_id");
				role = new Role(id, description);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 닫아주기
		// 닫아줄 때도 에러가 발생할 수 있으므로 예외처리가 필요하다.
			if(rs != null) { // null인 경우에는 닫을 수 없으므로 null이 아닐 때만 닫아줘야 함
				try {
					rs.close();
				} catch(SQLException e) {
					e.printStackTrace();
				}
			}
			if(ps != null) {
				try {
					ps.close();
				} catch(SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch(SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return role;
	}
}

Role와 RoleDao가 제대로 작성되었는지 테스트하는 코드를 작성해 실행시켜본다.

package kr.or.connect.jdbcexam;

import kr.or.connect.jdbcexam.dao.RoleDao;
import kr.or.connect.jdbcexam.dto.Role;

public class JDBCExam1 {

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

실행 결과 Role [roleId=100, description=Developer]이라고 제대로 출력되는 것을 확인할 수 있다.

그럼 이제는 DB에 데이터를 넣어보자. 앞서 작성한 getRole()과 거의 비슷한 addRole() 메소드를 RoleDao에 작성하면 된다.

public int addRole(Role role) {
	int insertCount = 0;
	
	try {
		Class.forName("com.mysql.jdbc.Driver");
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	}
	String sql = "INSERT INTO role (role_id, description) VALUES ( ?, ? )";

	try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbpasswd);
			PreparedStatement ps = conn.prepareStatement(sql)) {

		ps.setInt(1, role.getRoleId());
		ps.setString(2, role.getDescription());

		insertCount = ps.executeUpdate();

	} catch (Exception ex) {
		ex.printStackTrace();
	}
	return insertCount;
}

마찬가지로 테스트코드도 작성해 실행시켜본다.

package kr.or.connect.jdbcexam;

import kr.or.connect.jdbcexam.dao.RoleDao;
import kr.or.connect.jdbcexam.dto.Role;

public class JDBCExam2 {

	public static void main(String[] args) {
		int roleId = 600;
		String description = "CTO";
		
		Role role = new Role(roleId, description);
		RoleDao dao = new RoleDao();
		int insertCount = dao.addRole(role);

		System.out.println(insertCount);
	}
}

리스트로 갖고오기

public List<Role> getRoles() {
	List<Role> list = new ArrayList<>();
	
	try {
		Class.forName("com.mysql.jdbc.Driver");
	} catch(ClassNotFoundException e) {
		e.printStackTrace();
	}
	
	String sql = "SELECT description, role_id FROM role ORDER BY role_id DESC";
	
	try(Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbpasswd);
			PreparedStatement ps = conn.prepareStatement(sql)) {
		
		try(ResultSet rs = ps.executeQuery()) {
			while(rs.next()) {
				String description = rs.getString(1);
				int id= rs.getInt("role_id");
				Role role = new Role(id, description);
				list.add(role);
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	} catch (SQLException e) {
		e.printStackTrace();
	} 
	return list;
}

테스트코드

package kr.or.connect.jdbcexam;

import java.util.List;

import kr.or.connect.jdbcexam.dao.RoleDao;
import kr.or.connect.jdbcexam.dto.Role;

public class JDBCExam3 {

	public static void main(String[] args) {
		RoleDao dao = new RoleDao();
		
		List<Role> list = dao.getRoles();
		
		for(Role role: list) System.out.println(role);
	}
}

삭제

public int deleteRole(Integer roleId) {
	int deleteCount = 0;
	Connection conn = null;
	PreparedStatement ps = null;
	
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(dbUrl, dbUser, dbpasswd);
		String sql = "DELETE FROM role WHERE role_id = ?";
		ps = conn.prepareStatement(sql);
		ps.setInt(1, roleId);
		deleteCount = ps.executeUpdate();
	} catch(Exception e) {
		e.printStackTrace();
	} finally {
		if(ps != null) {
			try {
				ps.close();
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch(Exception e) {
				e.printStackTrace();
			}			
		}
	}
	return deleteCount;
}

테스트코드

package kr.or.connect.jdbcexam;

import kr.or.connect.jdbcexam.dao.RoleDao;

public class JDBCExam4 {
	public static void main(String[] args) {
		int roleId = 500;

		RoleDao dao = new RoleDao();
		int deleteCount = dao.deleteRole(roleId);

		System.out.println(deleteCount);
	}
}

업데이트

public int updateRole(Role role) {
	int updateCount = 0;
	
	Connection conn = null;
	PreparedStatement ps = null;
	
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(dbUrl, dbUser, dbpasswd);
		String sql = "UPDATE role SET description = ? WHERE role_id = ?";
		ps = conn.prepareStatement(sql);
		ps.setString(1, role.getDescription());
		ps.setInt(2, role.getRoleId());
		updateCount = ps.executeUpdate();
	} catch(Exception e) {
		e.printStackTrace();
	} finally {
		if(ps != null) {
			try {
				ps.close();
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
	return updateCount;
}

테스트코드

package kr.or.connect.jdbcexam;

import kr.or.connect.jdbcexam.dao.RoleDao;
import kr.or.connect.jdbcexam.dto.Role;

public class JDBCExam5 {
	public static void main(String[] args) {
		int roleId = 500;
		String description = "CEO";
		
		Role role = new Role(roleId, description);
		
		RoleDao dao = new RoleDao();
		int updateCount = dao.updateRole(role);

		System.out.println(updateCount);
	} 
}