백엔드/DB
JDBC
코딩 화이팅
2023. 3. 16. 13:39
JDBC(Java Data Connectivity)
- Java 프로그램에서 DB에 일관된 방식으로 접근할 수 있도록 API를 제공하는 클래스의 집합
- 데이터베이스에서 자료를 쿼리(SELECT)하거나 업데이트하는 방법을 제공
- Java에서는 JDBC를 이용하여 SQL을 DMBS와 주고받음
- DBMS의 종류에 관계 없이 사용 가능(약간의 설정만 조금 수정하면 가능)
JDBC 이용하여 DB 연결하는 방법 4단계
- JDBC 드라이버 로드
- 데이터 베이스 연결
- SQL문 실행
- 데이터베이스 연결 끊음
JDBC 드라이버 로드
- DB와 연결하기 위해서는 사용할 JDBC 드라이버를 프로그램 시작할 때 로딩
- 필요한 DBMS의 jar 파일을 프로젝트에 추가한다.
- 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
package com.ssafy.Test;
//1. JDBC 드라이버를 로드
//2. 데이터베이스 연결(Connection)
//3. SQL 준비 및 실행
//4. 데이터베이스 연결 해제
public class JDBCtest {
public JDBCtest() {
//드라이버를 로드한다.
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("드라이버 로딩 성공");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JDBCtest db=new JDBCtest();
}
}
//
드라이버 로딩 성공
데이터베이스 연결
- DriverManager 클래스의 static 메소드인 getConnection(URL, UserId, UserPassword)을 통해 연결 요청
DB별 URL
- MySQL : jdbc:/mysql://HOST:PORT/DATABASE[?키=값&키=값...]
-- SQL
DROP DATABASE IF EXISTS ssafy_board;
CREATE DATABASE ssafy_board DEFAULT CHARACTER SET utf8mb4;
USE ssafy_board;
CREATE TABLE board (
id INT AUTO_INCREMENT,
-- id가 1씩 증가
writer VARCHAR(20) NOT NULL,
title VARCHAR(50) NOT NULL,
-- 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 너두 마슷허","양씨","너도 할 수 있어"),
("누르지마시오", "따봉맨", "아무내용없음"),
("대답잘하는 법", "송소연", "채팅 잘치면됨 네(필쑤)");
SELECT * FROM board;
//Java
package com.ssafy.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import com.ssafy.board.model.dto.Board;
//1. JDBC 드라이버를 로드
//2. 데이터베이스 연결(Connection)
//3. SQL 준비 및 실행
//4. 데이터베이스 연결 해제
public class JDBCtest {
public JDBCtest() {
//드라이버를 로드한다.
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("드라이버 로딩 성공");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JDBCtest db=new JDBCtest();
}
private List<Board>seletAll(){
List<Board> list=new ArrayList<>();
//2. 데이터 베이스를 연결
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost::3306/ssafy_boared?serverTimezone=UTC", "ssafy", "ssafy");
return list;
}
}
SQL 실행
- SQL문을 수행하기 위해서는 Statement 객체가 필요하다.
- Connection 객체를 이용하여 createStatement() 메소드를 통해 생성한다.
- exectueQuery(String sql) : SELECT 문과 같이 결과값이 여러 개의 레코드로 구해지는 경우 사용
- executeUpdate(String sql) : INSERT, UPDATE, DELETE 문과 같이 테이블이 변경만 되고 결과가 없는 경우 사용 반환 값은 int형
ResultSet
- Query에 대한 결과 값 처리
- 반환 값이 여러 개인 경우에 이를 받아서 쉽게 처리할 수 있게 설계됨.
- next()를 통해 현재 행에서 다음 행으로 커서 이동
- getXXX(column Name/index)를 통해 값을 가져올 수 있음.
package com.ssafy.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.ssafy.board.model.dto.Board;
//1. JDBC 드라이버를 로드
//2. 데이터베이스 연결(Connection)
//3. SQL 준비 및 실행
//4. 데이터베이스 연결 해제
public class JDBCtest {
public JDBCtest() {
//드라이버를 로드한다.
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("드라이버 로딩 성공");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JDBCtest db=new JDBCtest();
for(Board b:db.seletAll()) {
System.out.println(b);
}
}
private List<Board>seletAll(){
List<Board> list=new ArrayList<>();
//2. 데이터 베이스를 연결
try {
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/ssafy_board?serverTimezone=UTC", "ssafy", "ssafy");
Statement stmt=conn.createStatement();
//전체 값을 가져오는 SQL문 작성
String sql="SELECT*FROM board";
ResultSet rs= stmt.executeQuery(sql);
//데이터베이스에서 index는 1부터 시작
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);
}
rs.close();
stmt.close();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
}
//
드라이버 로딩 성공
Board [id=1, title=BackEnd 너두 마슷허, writer=양씨, content=너도 할 수 있어, regDate=2023-03-16 10:08:23, viewCnt=0]
Board [id=2, title=누르지마시오, writer=따봉맨, content=아무내용없음, regDate=2023-03-16 10:08:23, viewCnt=0]
Board [id=3, title=대답잘하는 법, writer=송소연, content=채팅 잘치면됨 네(필쑤), regDate=2023-03-16 10:08:23, viewCnt=0]
PreparedStatement
- Statement의 단점을 극복한 인터페이스
- 간단하게 쿼리문을 작성할 수 있도록 도움을 줌
- Connection 인터페이스의 prepareStatement(String sql) 메서드를 통해 가져옴.
- executeQuery() / executeUpdate() 사용
- SQL문은 ?기호를 사용해서 표현 가능 ["INSERT INTO member VALUES(?,?,?,?)";]
- ?기호에 값을 setXXX(int 순서 / 실제 데이터나 변수)를 통해 할당해야 함.
public void insertBoard(Board board) throws SQLException {
// String sql="INSERT INTO board (title, writer, content) VALUES("
String sql="INSERT INTO board (title, writer, content) VALUES(?,?,?)";
Connection conn=null;
PreparedStatement pstmt=null;
conn=util.getConnection();
pstmt=conn.prepareStatement(sql);
pstmt.setString(1, board.getTitle());
pstmt.setString(2, board.getWriter());
pstmt.setString(3, board.getContent());
pstmt.executeUpdate();
util.close(pstmt, conn);
}
============================================================
//try,catch문으로도 구현 가능
public void updateBoard(Board board) throws SQLException {
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();
}
finally {
util.close(pstmt, conn);
}
}
게시판 만들기(실전)
//Board라는 게시판의 객체 생성
package com.ssafy.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 Board(int id, String title, String writer, String content, String regDate, int viewCnt) {
this.id = id;
this.title = title;
this.writer = writer;
this.content = content;
this.regDate = regDate;
this.viewCnt = viewCnt;
}
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 + "]";
}
}
/**
* Mysql DB 연결 객체를 제공하주고, 사용했던 자원을 해제하는 기능을 제공하는 클래스입니다.
*/
package com.ssafy.board.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
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/ssafy_board?serverTimezone=UTC";
// DB의 USER 이름
private final String username = "ssafy";
// 위 USER의 PASSWORD
private final String password = "ssafy";
// 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();
}
}
}
}
}
package com.ssafy.board.model.dao;
import java.sql.SQLException;
import java.util.List;
import com.ssafy.board.model.dto.Board;
public interface BoardDao {
//전체 게시글을 몽땅 들고 오쎄용
public List<Board> selectAll();
//ID에 해당하는 게시글 하나 가져오기
public Board selectOne(int id) throws SQLException;
//게시글 등록
public void insertBoard(Board board) throws SQLException;
//게시글 삭제
public void deleteBoard(int id) throws SQLException;
//게시글 수정
public void updateBoard(Board board) throws SQLException;
//조회수 증가
public void updateViewCnt(int id) throws SQLException;
}
package com.ssafy.board.model.dao;
import java.sql.Connection;
import java.sql.DriverManager;
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.ssafy.board.model.dto.Board;
import com.ssafy.board.util.DBUtil;
//싱글턴으로 만들기
public class BoardDaoImpl implements BoardDao{
//DBUtil 가져와야됨
private final 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<>();
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
//2. 데이터 베이스를 연결
try {
// conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/ssafy_board?serverTimezone=UTC", "ssafy", "ssafy");
conn=util.getConnection();
//위 아래는 같다.
stmt=conn.createStatement();
//전체 값을 가져오는 SQL문 작성
String sql="SELECT*FROM board";
rs= stmt.executeQuery(sql);
//데이터베이스에서 index는 1부터 시작
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) throws SQLException {
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();
rs.next();
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));
}
finally {
util.close(rs, pstmt, conn);
}
return board;
}
@Override
public void insertBoard(Board board) throws SQLException {
// String sql="INSERT INTO board (title, writer, content) VALUES("
String sql="INSERT INTO board (title, writer, content) VALUES(?,?,?)";
Connection conn=null;
PreparedStatement pstmt=null;
conn=util.getConnection();
pstmt=conn.prepareStatement(sql);
pstmt.setString(1, board.getTitle());
pstmt.setString(2, board.getWriter());
pstmt.setString(3, board.getContent());
pstmt.executeUpdate();
util.close(pstmt, conn);
}
@Override
public void deleteBoard(int id) throws SQLException {
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();
System.out.println(result+"개의 데이터가 지웠습니다.");
}
finally {
util.close(pstmt, conn);
}
}
@Override
public void updateBoard(Board board) throws SQLException {
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();
}
finally {
util.close(pstmt, conn);
}
}
@Override
public void updateViewCnt(int id) throws SQLException {
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();
}
finally {
util.close(pstmt, conn);
}
}
}