부스트코스 강의를 듣고 정리한 내용.
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를 이용한 프로그래밍 방법
- import java.sql.*;
- 드라이버를 load한다.
- Connection 객체를 생성한다. 커넥션 객체란 DB가 접속됐을 때 얻어내줄 수 있는 객체를 말한다.
- Statement 객체를 생성 및 질의 수행
- SQL문에 결과물이 있다면 ResultSet 객체를 생성한다.
- 모든 객체를 닫는다.
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”);
- any SQL
- 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이라는 테이블은 컬럼 두 개를 담을 공간을 마련하면 되니까 컬럼에 해당하는 변수 두 roleId
와 description
을 데이터 타입에 맞춰 선언해준다.
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이라는 테이블은 컬럼 두 개를 담을 공간을 마련하면 되니까 컬럼에 해당하는 변수 두 roleId
와 description
을 데이터 타입에 맞춰 선언해준다.
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);
}
}