[DB] JDBC

young-gue Park·2023년 10월 12일
0

DB

목록 보기
3/6
post-thumbnail

⚡ JDBC


📌JDBC(Java DataBase Connectivity)

🔷 Java 프로그램에서 DB에 일관된 방식으로 접근할 수 있도록 API를 제공하는 클래스의 집합

  • 데이터베이스에서 자료를 쿼리(SELECT)하거나 업데이트(INSERT, UPDATE, DELETE)하는 방법을 제공
  • Java 에서는 JDBC를 이용하여 SQL을 DMBS와 주고받음.
  • DBMS의 종류에 관계 없이 사용 가능. (약간의 설정만 조금 수정하면 가능)

JDBC 이용하여 DB 연결하는 방법 4단계

  1. JDBC 드라이버 로드
  • DB와 연결하기 위해서는 사용할 JDBC 드라이버를 프로그램 시작할 때 로딩
  • 필요한 DMBS의 jar 파일을 프로젝트에 추가한다. (jar 파일 직접 추가 or Maven(pom.xml))
  • 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
  1. 데이터베이스 연결
  • DriverManager 클래스의 static 메소드인 getConnection(URL, UserId, UserPassword)을 통해 연결 요청
Connection conn = DriverManager.getConnection(“URL”, “bzeromo”,1234);
  • Connection은 인터페이스 이므로 new 연산자를 통해 인스턴스를 생성하지 않고 만들어진
    인스턴스를 얻어와 저장한다. (default 는 AutoCommit)

💡 DB별 URL이 다르다.

  • MySQL : jdbc:/mysql://HOST:PORT/DATABASE[?키=값&키=값…]
  • Oracle : jdbc:/oracle:thin:@HOST:PORT:DATABASE
  • SQL Server : jdbc:sqlserver://[serverName[instanceName][:portNumber]][;property=value…]
  1. SQL 실행

🔷 Statement

  • SQL문을 수행하기 위해서는 Statement 객체가 필요하다.
  • Connection 객체를 이용하여 createStatement() 메소드를 통해 생성한다.
  • executeQuery(String sql) : SELECT 문과 같이 결과값이 여러 개의 레코드로 구해지는 경우 사용
  • executeUpdate(String sql) : INSERT, UPDATE, DELETE 문과 같이 테이블이 변경만 되고 결과가 없는 경우 사용 반환 값은 int 형

🔷 ResultSet

  • Query에 대한 결과 값 처리
  • 반환 값이 여러 개인 경우에 이를 받아서 쉽게 처리할 수 있게 설계됨
  • next() 를 통해 현재 행에서 다음행으로 커서 이동
  • previouse() 를 통해 현재 행에서 이전행으로 커서 이동
  • getXXX (Column Name / index)를 통해 값을 가져올 수 있음.

🔷 PreparedStatement

  • Statement의 단점을 극복한 인터페이스
  • 간단하게 쿼리문을 작성할 수 있도록 도움을 줌
  • Connection 인터페이스의 prepareStatement(String sql) 메서드를 통해 가져옴.
  • executeQuery() / executeUpdate() 사용
  • SQL문은 ? 기호를 사용해서 표현 가능 [ “INSERT INTO member VALUES(?, ?, ?, ?)”; ]
  • ? 기호에 값을 setXXX(int 순서 / 실제 데이터나 변수)를 통해 할당해야 함
  1. 데이터베이스 연결 끊음
  • 모든 작업이 끝이 나면 ResultSet, Statement(or PreparedStatement), Connectionclose()를 통해 연결을 종료한다. (역순으로 종료)

💡 데이터베이스를 열고 닫으며 예외처리를 위해 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은 스킵되었다.


📌 mySQL 연동 게시판 만들기

💡 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. 원하는 게시물 조회


연동까지 할 수 있게 되었다.

profile
Hodie mihi, Cras tibi

0개의 댓글