Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- DP
- 자바
- 브루트포스
- 개발자스터디
- BOJ
- 삼성청년SW아카데미
- 항해
- 구현
- 다이나믹프로그래밍
- 99일지
- 싸피
- 위상정렬
- python
- til
- ssafy
- 파이썬
- 프로그래머스
- BFS
- java
- 99클럽
- 문자열
- dfs
- 알고리즘
- 백트래킹
- 코딩테스트
- 깊이우선탐색
- 백준
- programmers
- SSAFY수료식
- 너비우선탐색
Archives
- Today
- Total
EunJng
[Java] 이것이 자바다 Ch.20 본문
'이것이 자바다' 교재 및 강의를 참고하여 정리한 내용입니다.
Ch.20 데이터베이스 입출력
JDBC 개요
- 자바는 DB와 연결해서 데이터 입출력 작업을 할 수 있도록 JDBC(Java Database Connectivity) 라이브러리 제공
- JDBC Driver : JDBC 인터페이스를 구현한 것. DBMS마다 별도로 다운로드받아 사용
- DriverManager : JDBC Driver를 관리하며 DB와 연결해서 Connection 구현 객체 생성
- Connection : Statement, PreparedStatement, CallableStatement 구현 객체 생성. 트랜잭션 처리 및 DB 연결을 끊을 때 사용
- Statement : SQL의 DDL, DML을 실행할 때 사용. 주로 정적 SQL문을 실행할 때 사용
- PreparedStatement : SQL의 DDL, DML을 실행할 때 사용. 매개변수화된 SQL문을 사용할 수 있어 편리성과 보안성이 좋다.
- CallableStatement : DB에 저장되어 있는 프로시저와 함수를 호출할 때 사용
- ResultSet : DB에서 가져온 데이터를 읽을 때 사용
DB 연결
- 클라이언트 프로그램에서 DB와 연결하려면 DBMS의 JDBC Driver 필요
- DB연결에 필요한 정보
- DBMS가 설치된 컴퓨터의 IP 주소
- DBMS가 허용하는 포트 번호
- 사용자 및 비밀번호
- 사용하고자 하는 DB 이름
DB 연결
- JDBC Diver를 메모리로 로딩
- Class.forName("com.mysql.cj.jdbc.Driver");
- JDBC Driver 클래스의 static 블록이 실행되며 DriverManager에 JDBC Driver 객체 등록
- Build Path에서 JDBC Driver 클래스를 찾지 못하면 ClassNotFoundException 발생 - 예외 처리 필요
- DB 연결
- Connection conn = DriverManager.getConnection("연결 문자열", "사용자", "비밀번호");
- 연결 문자열: jdbc:mysql://{IP 주소}:{포트 번호}/{DB명}
- Connection conn = DriverManager.getConnection("연결 문자열", "사용자", "비밀번호");
package ThisIsJava;
import java.io.FileInputStream;
import java.sql.*;
public class UserInsertExample {
public static void main(String[] args) {
Connection conn = null;
try {
// JDBC Driver 등록
Class.forName("com.mysql.cj.jdbc.Driver");
// 연결
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/thisisjava",
"root",
""
);
System.out.println("연결 성공");
// // 추가
// String sql = "" +
// "INSERT INTO users (userid, username, userpassword, userage, useremail) " +
// "VALUES (?, ?, ?, ?, ?)";
// // PreparedStatement 얻기 및 값 지정 1
// PreparedStatement pstmt = conn.prepareStatement(sql);
// pstmt.setString(1, "winter");
// pstmt.setString(2, "한겨울");
// pstmt.setString(3, "12345");
// pstmt.setString(4, 25);
// pstmt.setString(5, "winter@mycompany.com");
// // 2
// PreparedStatement pstmt = conn.prepareStatement(
// sql, Statement.RETURN_GENERATED_KEYS);
// pstmt.setString(1, "눈 오는 날");
// pstmt.setString(2, "함박눈이 내려요.");
// pstmt.setString(3, "winter");
// pstmt.setString(4, "snow.jpg");
// pstmt.setBlob(5, new FileInputStream("src/ch20/mysql/sec06/snow.jpg"));
// // 게시물 정보 수정
// 매개변수화된 SQL 문 작성
String sql = new StringBuilder()
.append("UPDATE boards SET ")
.append("btitle=?, ")
.append("bcontent=?, ")
.append("bfilename=?, ")
.append("bfiledata=? ")
.append("WHERE bno=?")
.toString();
// 삭제
// String sql = "DELETE FROM boards WHERE bwriter=?";
// PreparedStatement 얻기 및 값 지정
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "눈사람");
pstmt.setString(2, "눈으로 만든 사람");
pstmt.setString(3, "snowman.jpg");
pstmt.setBlob(4, new FileInputStream("src/ch20/mysql/sec06/snowman.jpg"));
pstmt.setInt(5, 3);
// SQL 문 실행
int rows = pstmt.executeUpdate();
System.out.println("수정된 행 수: " + rows);
// bno 값 얻기
if (rows == 1) {
ResultSet rs = pstmt.getGeneratedKeys();
if (rs.next()) {
int bno = rs.getInt(1);
System.out.println("저장된 bno: " + bno);
}
rs.close();
}
// PreparedStatement 닫기
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
// 연결 끊기
conn.close();
System.out.println("연결 끊기");
} catch (SQLException e) {}
}
}
}
}
데이터 읽기
- PreparedStatement 생성 시 INSERT, UPDATE, DELETE일 경우 executerUpdate() 메소드를 호출하지만, 데이터를 가져오는 SELECT 문일 경우 executeQuery() 메소드 호출
- executerQuery() 메소드는 가져온 데이터를 ResultSet에 저장하고 리턴
ResultSet
- SELECT 문에 기술된 컬럼으로 구성된 행의 집합
- 커서가 있는 행의 데이터만 읽을 수 있고, next() 메소드를 통해 커서를 다음 행으로 이동
- if(rs.next()) || while(rs.next())
데이터 행 읽기
- 컬럼의 데이터 타입에 따라 getXxx() 메소드가 사용되고, 매개값으로 컬럼의 이름 또는 순번을 줄 수 있다.
- rs.getString("userid");
- rs.getInt(3);
package ThisIsJava;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.*;
public class SelectExample {
public static void main(String[] args) {
Connection conn = null;
try {
// JDBC Driver 등록
Class.forName("com.mysql.cj.jdbc.Driver");
// 연결
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/thisisjava",
"root",
""
);
// // user Select
// String sql = "" +
// "SELECT userid, username, userpassword, userage, useremail " +
// "FROM users " +
// "WHERE userid=?";
// board Select
String sql = "" +
"SELECT bno, btitle, bcontent, bwriter, bdate, bfilename, bfiledata " +
"FROM boards " +
"WHERE bwriter=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "winter");
// SQL 문 실행 후, ResultSet을 통해 데이터 읽기
ResultSet rs = pstmt.executeQuery();
// // user Select
// if (rs.next()) { // 1개의 데이터 행을 가져왔을 경우
// User user = new User();
// user.setUserId(rs.getString("userid"));
// user.setUserName(rs.getString("username"));
// user.setUserPassword(rs.getString("userpassword"));
// user.setUserAge(rs.getInt(4)); // 컬럼 순번 이용
// user.setUserEmail(rs.getString(5));
// System.out.println(user);
// } else { // 데이터 행을 가져오지 않았을 경우
// System.out.println("사용자 아이디가 존재하지 않음");
// }
// board Select
while (rs.next()) {
// 데이터 행을 읽고 Board 객체 생성
Board board = new Board();
board.setBno(rs.getInt("bno"));
board.setBtitle(rs.getString("btitle"));
board.setBcontent(rs.getString("bcontent"));
board.setBwriter(rs.getString("bwriter"));
board.setBdate(rs.getDate("bdate"));
board.setBfilename(rs.getString("bfilename"));
board.setBfiledata(rs.getBlob("bfiledata"));
System.out.println(board);
// 파일로 저장
Blob blob = board.getBfiledata();
if (blob != null) {
InputStream is = blob.getBinaryStream();
OutputStream os = new FileOutputStream("C:/Temp/" +
board.getBfilename());
is.transferTo(os);
os.flush();
os.close();
is.close();
}
}
rs.close();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
// 연결 끊기
conn.close();
} catch (SQLException e) {}
}
}
}
}
트랜잭션 처리
- transaction: 기능 처리의 최소 단위. 트랜잭션은 소작업들이 모두 성공하거나 모두 실패해야 한다
- DB는 트랜잭션 처리를 위해 커밋, 롤백 제공
- commit : 내부 작업을 모두 성공 처리
- rollback : 실행 전으로 돌아간다 (모두 실패 처리)
- JDBC에서는 INSERT, UPDATE, DELETE 문을 실행할 때마다 자동 커밋이 일어나기 때문에 트랜잭션을 코드로 제어하려면 자동 커밋 기능을 꺼야 한다.
- conn.setAutoCommit(false);
- conn.commit(); // 수동 커밋
- conn.rollback(); // 수동 롤백
- 트랜잭션 처리 후에는 Connection을 계속 사용해야 한다면 setAutoCommit(true)로 자동 커밋 기능을 켜야 한다.
- 커넥션 풀(Connection Pool)
- 일정량의 Connection을 미리 생성시켜놓고, 서버에서 클라이언트의 요청을 처리할 때 Connection을 제공해주고 다시 반환받는 역할 수행
- Connection 재사용을 통해 DB 연결 시간을 줄이고, 전체 Connection 수를 관리할 수 있다.
게시판 구현
package com.example.thisisjava;
import java.sql.*;
import java.util.Scanner;
public class BoardExample2 {
// Field
private Scanner scanner = new Scanner(System.in);
private Connection conn;
// Constructor
public BoardExample2() {
try {
// JDBC Driver 등록
Class.forName("com.mysql.cj.jdbc.Driver");
// 연결하기
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/thisisjava",
"root",
""
);
} catch (Exception e) {
e.printStackTrace();
exit();
}
}
// Method
public void list() {
// 타이틀 및 컬럼명 출력
System.out.println();
System.out.println("[게시물 목록]");
System.out.println("-----------------------------------------------------");
System.out.printf("%-6s%-12s%-16s%-40s\n", "no", "writer", "date", "title");
System.out.println("-----------------------------------------------------");
// boards 테이블에서 게시물 정보를 가져와서 출력
try {
String sql = "" +
"SELECT bno, btitle, bcontent, bwriter, bdate " +
"FROM boards " +
"ORDER BY bno DESC";
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
Board board = new Board();
board.setBno(rs.getInt("bno"));
board.setBtitle(rs.getString("btitle"));
board.setBcontent(rs.getString("bcontent"));
board.setBwriter(rs.getString("bwriter"));
board.setBdate(rs.getDate("bdate"));
System.out.printf("%-6s%-12s%-16s%-40s \n",
board.getBno(),
board.getBwriter(),
board.getBdate(),
board.getBtitle());
}
rs.close();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
exit();
}
// 메인 메뉴 출력
mainMenu();
}
public void mainMenu() {
System.out.println();
System.out.println("-----------------------------------------------------");
System.out.println("메인 메뉴: 1.Create | 2.Read | 3.Clear | 4.Exit");
System.out.printf("메뉴 선택: ");
String menuNo = scanner.nextLine();
System.out.println();
switch (menuNo) {
case "1" -> create();
case "2" -> read();
case "3" -> clear();
case "4" -> exit();
}
}
public void create() {
// 입력 받기
Board board = new Board();
System.out.println("[새 게시물 입력]");
System.out.print("제목: ");
board.setBtitle(scanner.nextLine());
System.out.print("내용: ");
board.setBtitle(scanner.nextLine());
System.out.print("작성자: ");
board.setBtitle(scanner.nextLine());
// 보조 메뉴 출력
System.out.println("-----------------------------------------------------");
System.out.println("보조 메뉴: 1.Ok | 2.Cancel");
System.out.printf("메뉴 선택: ");
String menuNo = scanner.nextLine();
if (menuNo.equals("1")) {
// boards 테이블에 게시물 정보 저장
try {
String sql = "" +
"INSERT INTO boards (btitle, bcontent, bwriter, bdate) " +
"VALUES (?, ?, ?, now())";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, board.getBtitle());
pstmt.setString(2, board.getBcontent());
pstmt.setString(3, board.getBwriter());
pstmt.executeUpdate();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
exit();
}
}
// 게시물 목록 출력
list();
}
public void read() {
// 입력 받기
System.out.println("[게시물 읽기]");
System.out.print("bno: ");
int bno = Integer.parseInt(scanner.nextLine());
// boards 테이블에서 해당 게시물을 가져와 출력
try {
String sql = "" +
"SELECT bno, btitle, bcontent, bwriter, bdate " +
"FROM boards " +
"WHERE bno=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, bno);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
Board board = new Board();
board.setBno(rs.getInt("bno"));
board.setBtitle(rs.getString("btitle"));
board.setBcontent(rs.getString("bcontent"));
board.setBwriter(rs.getString("bwriter"));
board.setBdate(rs.getDate("bdate"));
System.out.println("#############");
System.out.println("번호: " + board.getBno());
System.out.println("제목: " + board.getBtitle());
System.out.println("내용: " + board.getBcontent());
System.out.println("작성자: " + board.getBwriter());
System.out.println("날짜: " + board.getBdate());
System.out.println("-----------------------------------------------------");
System.out.println("보조 메뉴: 1.Ok | 2.Cancel");
System.out.printf("메뉴 선택: ");
String menuNo = scanner.nextLine();
System.out.println();
if (menuNo.equals("1")) {
update(board);
} else if (menuNo.equals("2")) {
delete(board);
}
}
rs.close();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
exit();
}
list();
}
public void update(Board board) {
System.out.println("[수정 내용 입력]");
System.out.print("제목: ");
board.setBtitle(scanner.nextLine());
System.out.print("내용: ");
board.setBtitle(scanner.nextLine());
System.out.print("작성자: ");
board.setBtitle(scanner.nextLine());
// 보조 메뉴 출력
System.out.println("-----------------------------------------------------");
System.out.println("보조 메뉴: 1.Ok | 2.Cancel");
System.out.printf("메뉴 선택: ");
String menuNo = scanner.nextLine();
if (menuNo.equals("1")) {
// boards 테이블에 게시물 정보 수정
try {
String sql = "" +
"UPDATE boards SET btitle=?, bcontent=?, bwriter=? " +
"WHERE bno=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, board.getBtitle());
pstmt.setString(2, board.getBcontent());
pstmt.setString(3, board.getBwriter());
pstmt.setInt(4, board.getBno());
pstmt.executeUpdate();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
exit();
}
}
list();
}
public void delete(Board board) {
// Boards 테이블에 게시물 정보 삭제
try {
String sql = "" +
"DELETE FROM boards WHERE bno=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, board.getBno());
pstmt.executeUpdate();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
exit();
}
list();
}
public void clear() {
System.out.println("[게시물 전체 삭제]");
System.out.println("-----------------------------------------------------");
System.out.println("보조 메뉴: 1.Ok | 2.Cancel");
System.out.printf("메뉴 선택: ");
String menuNo = scanner.nextLine();
if (menuNo.equals("1")) {
// boards 테이블에 게시물 정보 전체 삭제
try {
String sql = "TRUNCATE TABLE boards";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
exit();
}
}
list();
}
public void exit() {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
System.out.println("** 게시판 종료 **");
System.exit(0);
}
public static void main(String[] args) {
BoardExample2 boardExample = new BoardExample2();
boardExample.list();
}
}