🔷 Java 프로그램에서 DB에 일관된 방식으로 접근할 수 있도록 API를 제공하는 클래스의 집합
java.lang.Class
클래스의 정적 메소드 forName()
을 이용하여 JVM 안으로 클래스를 메모리에 적재DriverManager
를 통해 접근 가능💡 DB별 Driver Class 이름이 다르다.
- MySQL :
com.mysql.cj.jdbc.Driver
- Oracle :
oracle.jdbc.driver.OracleDriver
- SQL Server :
com.Microsoft.sqlserver.jsdbc.SQLServerDriver
등
DriverManager
클래스의 static 메소드인 getConnection(URL, UserId, UserPassword)
을 통해 연결 요청Connection conn = DriverManager.getConnection(“URL”, “bzeromo”, “1234”);
💡 DB별 URL이 다르다.
- MySQL : jdbc:/mysql://HOST:PORT/DATABASE[?키=값&키=값…]
- Oracle : jdbc:/oracle:thin:@HOST:PORT:DATABASE
- SQL Server : jdbc:sqlserver://[serverName[instanceName][:portNumber]][;property=value…]
🔷 Statement
Statement
객체가 필요하다.Connection
객체를 이용하여 createStatement()
메소드를 통해 생성한다.executeQuery(String sql)
: SELECT 문과 같이 결과값이 여러 개의 레코드로 구해지는 경우 사용executeUpdate(String sql)
: INSERT, UPDATE, DELETE 문과 같이 테이블이 변경만 되고 결과가 없는 경우 사용 반환 값은 int 형🔷 ResultSet
next()
를 통해 현재 행에서 다음행으로 커서 이동previouse()
를 통해 현재 행에서 이전행으로 커서 이동getXXX (Column Name / index)
를 통해 값을 가져올 수 있음.🔷 PreparedStatement
Connection
인터페이스의 prepareStatement(String sql)
메서드를 통해 가져옴.executeQuery()
/ executeUpdate()
사용?
기호를 사용해서 표현 가능 [ “INSERT INTO member VALUES(?, ?, ?, ?)”; ]?
기호에 값을 setXXX(int 순서 / 실제 데이터나 변수)
를 통해 할당해야 함ResultSet
, Statement(or PreparedStatement)
, Connection
의 close()
를 통해 연결을 종료한다. (역순으로 종료)💡 데이터베이스를 열고 닫으며 예외처리를 위해 try-catch-finally를 활용해야한다. throws의 경우 인터페이스 및 메서드를 사용하는 곳까지 전부 건드려야하기 때문에 그닥 추천하지 않는듯 하다.
🖥 mySQL Workbench에서 실습에 사용할 테이블 생성 및 컬럼 삽입
DROP DATABASE IF EXISTS bzeromo_board;
CREATE DATABASE bzeromo_board DEFAULT CHARACTER SET utf8mb4;
USE bzeromo_board;
CREATE TABLE board (
id INT AUTO_INCREMENT,
writer VARCHAR(20) NOT NULL,
title VARCHAR(50) NOT NULL,
content TEXT,
view_cnt INT DEFAULT 0,
reg_date TIMESTAMP DEFAULT now(),
PRIMARY KEY(id)
);
INSERT INTO board(title, writer, content)
VALUES ("BackEnd 너두 마슷허","박씨","너도 할 수 있어"),
("누르지마시오", "따봉맨", "아무내용없음"),
("대답잘하는 법", "bze", "채팅 잘치면됨 네(필쑤)");
SELECT * FROM board;
🖥 Board
package com.bzeromo.board.model.dto;
public class Board {
private int id;
private String title;
private String writer;
private String content;
private String regDate;
private int viewCnt;
public Board() {}
public Board(String title, String writer, String content) {
super();
this.title = title;
this.writer = writer;
this.content = content;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRegDate() {
return regDate;
}
public void setRegDate(String regDate) {
this.regDate = regDate;
}
public int getViewCnt() {
return viewCnt;
}
public void setViewCnt(int viewCnt) {
this.viewCnt = viewCnt;
}
@Override
public String toString() {
return "Board [id=" + id + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
+ regDate + ", viewCnt=" + viewCnt + "]";
}
}
🖥 JDBCTest
package com.bzeromo.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.bzeromo.board.model.dto.Board;
//순서
//1. JDBC 드라이버 로드
//2. 데이터베이스 연결
//3. SQL 준비 및 실행
//4. 데이터베이스 연결 해제
public class JDBCTest {
public JDBCTest() {
//1. JDBC 드라이버 로드
//Class라고 하는 클래스가 가진 정적 메소드 forName을 사용하면 메모리에 미리 올려둔다.
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("Driver Loading Successed.");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JDBCTest db = new JDBCTest();
for(Board b: db.selectAll()) {
System.out.println(b);
}
}
public List<Board> selectAll() {
List<Board> list = new ArrayList<>();
//2. 데이터베이스 연결
try {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bzeromo_board?serverTimezone=UTC", "bzeromo", "1234");
//3. SQL 실행 및 준비
Statement stmt = conn.createStatement();
//SQL (전체 게시글 가져오기)
String sql = "SELECT * FROM board";
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()) {
Board board = new Board();
board.setId(rs.getInt("id"));
board.setTitle(rs.getString("title"));
board.setWriter(rs.getString("writer"));
board.setContent(rs.getString("content"));
board.setRegDate(rs.getString("reg_date"));
board.setViewCnt(rs.getInt("view_cnt"));
//인덱스 값으로 가져올 수도 있다. (1부터 순서대로)
// board.setId(rs.getInt(1));
// board.setWriter(rs.getString(2));
// board.setTitle(rs.getString(3));
// board.setContent(rs.getString(4));
// board.setViewCnt(rs.getInt(5));
// board.setRegDate(rs.getString(6));
list.add(board);
}
//4. 데이터베이스 연결 해제
rs.close();
stmt.close();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
}
❗ id가 4부터 시작하는 이유는 이전에 한번 테이블 내부를 전부 삭제한 적이 있어서 id 1, 2, 3은 스킵되었다.
💡 DTO로 활용할 board는 위에 제시한 그대로이다.
💡 싱글턴 패턴을 이용해 게시글 조회, 등록, 삭제, 수정, 조회수 증가를 구현한다.
🖥 BoardDao (Interface)
package com.bzeromo.board.model.dao;
import java.util.List;
import com.bzeromo.board.model.dto.Board;
public interface BoardDao {
// 전체 게시글 조회
public List<Board> selectAll();
// ID에 해당하는 게시글 하나 가져오기
public Board selectOne(int id);
// 게시글 등록
public void insertBoard(Board board);
// 게시글 삭제
public void deleteBoard(int id);
// 게시글 수정
public void updateBoard(Board board);
// 조회수 증가
public void updateViewCnt(int id);
}
🖥 DBUtil
package com.bzeromo.board.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* Mysql DB 연결 객체를 제공하주고, 사용했던 자원을 해제하는 기능을 제공하는 클래스
*/
public class DBUtil {
/**
* DB 접속에 필요한 url을 작성한다.
* url은 jdbc:mysql://[host][:port]/[database][?propertyName1][=propertyValue1]형태로 작성한다.
* serverTimezone=UTC 설정이 없으면 오류가 발생하므로 주의한다.
*/
// DB와 연결하기위해 필요한 DB의 URL
private final String url = "jdbc:mysql://localhost:3306/bzeromo_board?serverTimezone=UTC";
// DB의 USER 이름
private final String username = "bzeromo";
// 위 USER의 PASSWORD
private final String password = "1234";
// Mysql 드라이버 클래스 이름
private final String driverName = "com.mysql.cj.jdbc.Driver";
/**
* Singleton Design Pattern을 적용해준다.
*/
private static DBUtil instance = new DBUtil();
private DBUtil() {
// JDBC 드라이버를 로딩한다. 드라이버 로딩은 객체 생성 시 한번만 진행하도록 하자.
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static DBUtil getInstance() {
return instance;
}
/**
* DriverManager를 통해 Connection을 생성하고 반환한다.
*
* @return
* @throws SQLException
*/
public Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username, password);
}
// public static void close(Connection conn, PreparedStatement pstmt) {
// try {
// if (pstmt != null)
// pstmt.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
//
// try {
// if (conn != null)
// conn.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
//
// public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
// try {
// if (rs != null)
// rs.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
//
// try {
// if (pstmt != null)
// pstmt.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
//
// try {
// if (conn != null)
// conn.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
/**
* 사용한 리소스들을 정리한다.
* Connection, Statement, ResultSet 모두 AutoCloseable 타입이다.
* ... (전개 연산자)을 이용하므로 필요에 따라서
* select 계열 호출 후는 ResultSet, Statement, Connection
* dml 호출 후는 Statement, Connection 등 다양한 조합으로 사용할 수 있다.
*
* @param closeables
*/
public void close(AutoCloseable... closeables) {
for (AutoCloseable c : closeables) {
if (c != null) {
try {
c.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
🖥 BoardDaoImpl
package com.bzeromo.board.model.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.bzeromo.board.model.dto.Board;
import com.bzeromo.board.util.DBUtil;
//싱글턴
public class BoardDaoImpl implements BoardDao {
//DBUtil 들고오기
private DBUtil util = DBUtil.getInstance();
private static BoardDaoImpl instance = new BoardDaoImpl();
private BoardDaoImpl() {}
public static BoardDaoImpl getInstance() {
return instance;
}
@Override
public List<Board> selectAll() {
List<Board> list = new ArrayList<>();
String sql = "SELECT * FROM board";
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//2. 데이터베이스 연결
try {
conn = util.getConnection();
//3. SQL 실행 및 준비
stmt = conn.createStatement();
//SQL (전체 게시글 가져오기)
rs = stmt.executeQuery(sql);
while(rs.next()) {
Board board = new Board();
board.setId(rs.getInt("id"));
board.setTitle(rs.getString("title"));
board.setWriter(rs.getString("writer"));
board.setContent(rs.getString("content"));
board.setRegDate(rs.getString("reg_date"));
board.setViewCnt(rs.getInt("view_cnt"));
list.add(board);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
util.close(rs, stmt, conn);
}
return list;
}
@Override
public Board selectOne(int id) {
String sql = "SELECT * FROM board WHERE id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Board board = new Board();
try {
conn = util.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
rs = pstmt.executeQuery();
while(rs.next()) {
board.setId(rs.getInt("id"));
board.setTitle(rs.getString("title"));
board.setWriter(rs.getString("writer"));
board.setContent(rs.getString("content"));
board.setRegDate(rs.getString("reg_date"));
board.setViewCnt(rs.getInt("view_cnt"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
util.close(pstmt, rs, conn);
}
return board;
}
@Override
public void insertBoard(Board board) {
String sql = "INSERT INTO board (title, writer, content) VALUES(?,?,?)";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = util.getConnection();
//autocommit 해제시
// conn.setAutoCommit(false);
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, board.getTitle());
pstmt.setString(2, board.getWriter());
pstmt.setString(3, board.getContent());
int result = pstmt.executeUpdate();
if(result == 1)
System.out.println("Update Successed.");
// conn.commit();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// conn.rollback();
} finally {
util.close(pstmt, conn);
}
}
@Override
public void deleteBoard(int id) {
String sql = "DELETE FROM board WHERE id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = util.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
int result = pstmt.executeUpdate();
if(result == 1)
System.out.println("Delete Successed.");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
util.close(pstmt, conn);
}
}
@Override
public void updateBoard(Board board) {
String sql = "UPDATE board SET title=?, content=? WHERE id=?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = util.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, board.getTitle());
pstmt.setString(2, board.getContent());
pstmt.setInt(3, board.getId());
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
util.close(pstmt, conn);
}
}
@Override
public void updateViewCnt(int id) {
String sql = "UPDATE board SET view_cnt = view_cnt + 1 WHERE id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = util.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
util.close(pstmt, conn);
}
}
}
🖥 TEST
package com.bzeromo.test;
import com.bzeromo.board.model.dao.BoardDao;
import com.bzeromo.board.model.dao.BoardDaoImpl;
import com.bzeromo.board.model.dto.Board;
public class Test {
public static void main(String[] args) {
BoardDao dao = BoardDaoImpl.getInstance();
// 1. Board board = new Board("나는 원딜장인", "Bzeromo", "사미라를 잘하지");
// dao.insertBoard(board);
// 2. dao.deleteBoard(4);
// 3. dao.updateViewCnt(5);
for(Board b : dao.selectAll()) {
System.out.println(b);
}
// 4. System.out.println(dao.selectOne(5));
}
}
🖨 모든 게시물 조회 (기본 실행)
🖨 1. 새 게시물 등록
❗ id 7번은 삭제한 전적이 있어서 뜨지 않는다.
🖨 2. 게시물 삭제
🖨 3. 게시물 조회수 증가
🖨 4. 원하는 게시물 조회
연동까지 할 수 있게 되었다.