Study Web Development

12월 2일

이전으로

자바

17. JDBC

17-2 JDBC(Java Database Connectivity)

PreparedStatement
  1. 새 클래스 UpdateMemo 생성
package kr.s04.preparedstatement;

import kr.s03.preparedstatement.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class UpdateMemo {
	public static void main(String[] args) {
		BufferedReader br = null;
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		
		try {
			br = new BufferedReader(new InputStreamReader(System.in));
			System.out.println("메모를 수정합니다.");
			System.out.print("메모 번호 > ");
			int num = Integer.parseInt(br.readLine());
			System.out.print("제목 > ");
			String title = br.readLine();
			System.out.print("작성자 > ");
			String name = br.readLine();
			System.out.print("내용 > ");
			String memo = br.readLine();
			System.out.print("이메일 > ");
			String email = br.readLine();
			
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			
			// 빈 행을 입력받은 경우, 기존 정보를 유지할 수 있게끔 SELECT문 실행
			if(title.equals("")|name.equals("")|memo.equals("")|email.equals("")) {
				sql = "SELECT * FROM test2 WHERE num=?";
				PreparedStatement pstmt2 = conn.prepareStatement(sql);
				pstmt2.setInt(1, num);
				rs = pstmt2.executeQuery();
				if(rs.next()) {
					if (title.equals("")) title = rs.getString("title");
					if (name.equals("")) name = rs.getString("name");
					if (memo.equals("")) memo = rs.getString("memo");
					if (email.equals("")) email = rs.getString("email");
				}
				DBUtil.executeClose(rs, pstmt2, null); // 자원 정리를 하기 위해서는 SQL문장마다 별도의 PreparedStatement 객체를 생성해야 함
			}
			
			// SQL문 작성
			sql = "UPDATE test2 SET title=?, name=?, memo=?, email=? WHERE num=?";
			
			// JDBC 수행 3단계
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setString(1, title);
			pstmt.setString(2, name);
			pstmt.setString(3, memo);
			pstmt.setString(4, email);
			pstmt.setInt(5, num);
			
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블의 행 변경
			int count = pstmt.executeUpdate();
			System.out.println(count + "개 행의 정보를 수정했습니다.");
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt, conn);
			if(br!=null) try {br.close();} catch(Exception e) {}
		}
	}
}
  1. 새 클래스 DeleteMemo 생성
package kr.s04.preparedstatement;

import kr.s03.preparedstatement.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class DeleteMemo {
	public static void main(String[] args) {
		BufferedReader br = null;
		Connection conn = null;
		PreparedStatement pstmt = null;
		String sql = null;
		
		try {
			br = new BufferedReader(new InputStreamReader(System.in));
			System.out.println("메모를 삭제합니다.");
			System.out.print("메모 번호 > ");
			int num = Integer.parseInt(br.readLine());
			
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			
			// SQL문 작성
			sql = "DELETE FROM test2 WHERE num=?";
			
			// JDBC 수행 3단계 : PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(1, num);
			
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블의 행을 삭제
			int count = pstmt.executeUpdate();
			System.out.println(count + "개 행이 삭제되었습니다.");
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			if(br!=null) try {br.close();} catch(Exception e) {}
			DBUtil.executeClose(null, pstmt, conn);
		}
	}
}
CallableStatement
  1. 새 패키지 kr.s05.callablestatement 생성하고 table.sql 생성
CREATE TABLE salary(
	name VARCHAR2(30) PRIMARY KEY,
	pay NUMBER
);

INSERT INTO salary VALUES('TEST', 1000);
INSERT INTO salary VALUES('CALL', 2000);
INSERT INTO salary VALUES('PROCEDURE', 3000);
COMMIT;

CREATE OR REPLACE PROCEDURE adjust(name_i IN VARCHAR2, rate IN FLOAT)
AS
	newpay FLOAT;
BEGIN
	SELECT pay INTO newpay FROM salary WHERE name = name_i;
	newpay := newpay * (1 + rate);
	UPDATE salary SET pay=newpay WHERE name = name_i;
	COMMIT;
EXCEPTION WHEN OTHERS THEN
	DBMS_OUTPUT.PUT_LINE(name_i || ' 갱신 실패');
	ROLLBACK;
END;
  1. kr.s03.preparedstatement 패키지의 DBUtil 클래스에 executeClose() 메서드 오버로딩을 추가
package kr.s03.preparedstatement;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBUtil {
	private static final String DB_DRIVER = "oracle.jdbc.OracleDriver";
	private static final String DB_URL = "jdbc:oracle:thin:@localhost:1521:xe";
	private static final String DB_ID = "scott";
	private static final String DB_PASSWORD = "tiger";

	// Connection 객체 생성 및 반환
	public static Connection getConnection() throws ClassNotFoundException, SQLException {
		// JDBC 수행 1단계 : 드라이버 로드
		Class.forName(DB_DRIVER);
		// JDBC 수행 2단계 : Connection 객체 생성
		return DriverManager.getConnection(DB_URL, DB_ID, DB_PASSWORD);
	}
	
	// 자원 정리
	public static void executeClose(ResultSet rs, PreparedStatement pstmt, Connection conn) {
		if(rs!=null) try {rs.close();} catch(SQLException e) {}
		if(pstmt!=null) try {pstmt.close();} catch(SQLException e) {}
		if(conn!=null) try {conn.close();} catch(SQLException e) {}
	}
	public static void executeClose(CallableStatement cstmt, Connection conn) {
		if(cstmt!=null) try {cstmt.close();} catch(SQLException e) {}
		if(conn!=null) try {conn.close();} catch(SQLException e) {}	
	}
}
  1. 새 클래스 CallableStatementMain 생성
package kr.s05.callablestatement;

import kr.s03.preparedstatement.DBUtil;
import java.sql.CallableStatement;
import java.sql.Connection;

public class CallableStatementMain {
	public static void main(String[] args) {
		Connection conn = null;
		CallableStatement cstmt = null;
		String sql = null;
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			
			// 프로시저 호출 문장 작성
			sql = "{call adjust(?, ?)}"; // 인자에 직접 값을 입력하지 않고 바인드 변수 ? 사용
			
			// JDBC 수행 3단계 : CallableStatement 객체 생성
			cstmt = conn.prepareCall(sql);
			// ?에 데이터를 바인딩
			cstmt.setString(1, "CALL");
			cstmt.setFloat(2, 0.06f);
			
			// JDBC 수행 단계 : 프로시저 실행
			cstmt.executeUpdate();
			System.out.println("급여 정보가 수정되었습니다.");
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(cstmt, conn);
		}
	}
}
Connection
  1. 새 패키지 kr.s06.transaction 생성하고 새 클래스 TransactionMain 생성
package kr.s06.transaction;

import kr.s03.preparedstatement.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TransactionMain {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement pstmt1 = null;
		PreparedStatement pstmt2 = null;
		PreparedStatement pstmt3 = null;
		String sql = null;
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			
			// 트랜잭션 처리를 위해서 자동 COMMIT 해제
			conn.setAutoCommit(false);
			
			// SQL문 작성
			sql = "INSERT INTO test1 VALUES (?, ?)";
			
			// JDBC 수행 3단계;
			pstmt1 = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt1.setString(1, "test");
			pstmt1.setInt(2, 10);
			
			// JDBC 수행 4단계 : SQL문 실행
			pstmt1.executeUpdate();
			
			sql = "INSERT INTO test1 VALUES (?, ?)";
			pstmt2 = conn.prepareStatement(sql);
			pstmt2.setString(1, "COMMIT");
			pstmt2.setInt(2, 20);
			pstmt2.executeUpdate();
			
			// 트랜잭션 테스트를 위해 오류가 있는 SQL문 작성
			sql = "INSERT INTO test1 VALUES (?, ?";
			pstmt3 = conn.prepareStatement(sql);
			pstmt3.setString(1, "ROLLBACK");
			pstmt3.setInt(2, 30);
			pstmt3.executeUpdate();
			
			// 모든 SQL문이 정상적으로 처리되면 COMMIT
			conn.commit();
			System.out.println("작업 완료");	
		}
		catch(Exception e) {
			e.printStackTrace();
			
			// 예외 발생하면 ROLLBACK
			try {
				conn.rollback();
			}
			catch(SQLException se) {
				se.printStackTrace();
			}
		}
		finally {
			DBUtil.executeClose(null, pstmt1, null);
			DBUtil.executeClose(null, pstmt3, null);
			DBUtil.executeClose(null, pstmt2, conn); // PreparedStatement 객체가 여러 개일 때, 각각의 정리 순서는 중요하지 않고 Connection 객체만 마지막에 정리하면 됨
		}
	}
}
DAO(Data Access Object)
  1. 새 패키지 kr.s07.note 생성하고 table.sql 생성
CREATE TABLE note(
	num NUMBER PRIMARY KEY,
	name VARCHAR2(30) NOT NULL,
	passwd VARCHAR2(10)	NOT NULL,
	subject VARCHAR2(60) NOT NULL,
	content VARCHAR2(4000) NOT NULL,
	email VARCHAR2(60),
	reg_date DATE NOT NULL
);
CREATE SEQUENCE note_seq;
  1. 새 클래스 NoteMain 생성
package kr.s07.note;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class NoteMain {
	private BufferedReader br;
	private NoteDAO note; // callMenu() 메서드에서 사용하기 때문에 멤버 변수로 선언
	
	public NoteMain() {
		note = new NoteDAO();
		try {
			br = new BufferedReader(new InputStreamReader(System.in));
			callMenu();
		}
		catch(IOException e) {
			e.printStackTrace();
		}
		finally {
			if(br!=null) try {br.close();} catch(IOException e) {}
		}
	}
	
	// 메뉴
	public void callMenu() throws IOException {
		while(true) {
			System.out.println("메뉴 : 1. 글 쓰기, 2. 목록 보기, 3. 상세 글 보기, 4. 글 수정, 5. 글 삭제, 6.종료");
			System.out.print("메뉴 > ");
			try {
				int menu = Integer.parseInt(br.readLine());
				if(menu==1) { // 글 쓰기
					System.out.print("이름 > ");
					String name = br.readLine();
					System.out.print("비밀번호 > ");
					String passwd = br.readLine();
					System.out.print("제목 > ");
					String subject = br.readLine();
					System.out.print("내용 > ");
					String content = br.readLine();
					System.out.print("이메일 > ");
					String email = br.readLine();
					// NoteDAO의 insertInfo() 메서드를 호출해서 데이터를 전달
					note.insertInfo(name, passwd, subject, content, email);
				}
				else if(menu==2) { // 목록 보기
					note.selectListInfo();
				}
				else if(menu==3) { // 상세 글 보기
					note.selectListInfo(); // 글 번호 확인용 목록 보기
					System.out.println("상세 정보를 확인하려는 글 번호를 입력하세요!");
					System.out.print("글 번호 > ");
					int num = Integer.parseInt(br.readLine());
					note.selectDetailInfo(num);
				}
				else if(menu==4) { // 글 수정
					note.selectListInfo(); // 글 번호 확인용 목록 보기
					System.out.println("수정하려는 글 번호를 입력하세요!");
					int num;
					while(true) { // 올바른 글 번호 입력 강제
						System.out.print("글 번호 > ");
						num = Integer.parseInt(br.readLine());
						if(note.selectDetailInfo(num)==1) break;
					}
					System.out.println("수정할 내용을 입력하세요!");
					System.out.print("이름 > ");
					String name = br.readLine();
					System.out.print("비밀번호 > ");
					String passwd = br.readLine();
					System.out.print("제목 > ");
					String subject = br.readLine();
					System.out.print("내용 > ");
					String content = br.readLine();
					System.out.print("이메일 > ");
					String email = br.readLine();
					note.updateInfo(num, name, passwd, subject, content, email);
				}
				else if(menu==5) { // 글 삭제
					note.selectListInfo(); // 글 번호 확인용 목록 보기
					System.out.println("삭제하려는 글 번호를 입력하세요!");
					System.out.print("글 번호 > ");
					int num = Integer.parseInt(br.readLine());
					note.deleteInfo(num);
					note.selectListInfo(); // 삭제가 정상적으로 수행되었는지 확인하기 위해 목록 호출
				}
				else if(menu==6) { // 종료
					System.out.println("프로그램을 종료합니다.");
					break;
				}
				else {
					System.out.println("잘못 입력했습니다!");
				}
			}
			catch(NumberFormatException e) {
				System.out.println("숫자만 입력 가능합니다!");
			}
		}
	}
	
	public static void main(String[] args) {
		new NoteMain();
	}
}
  1. 새 클래스 NoteDAO 생성
package kr.s07.note;

import kr.s03.preparedstatement.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/*
 * DAO(Data Access Object): 데이터베이스의 데이터를 전문적으로 호출하고 제어하는 객체
 */
public class NoteDAO {
	// 글 쓰기
	public void insertInfo(String name, String passwd, String subject, String content, String email) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		String sql = null;
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			// SQL문 작성
			sql = "INSERT INTO note VALUES (note_seq.NEXTVAL, ?, ?, ?, ?, ?, SYSDATE)";
			// JDBC 수행 3단계 : PreparedStatment 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setString(1, name);
			pstmt.setString(2, passwd);
			pstmt.setString(3, subject);
			pstmt.setString(4, content);
			pstmt.setString(5, email);
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블에 행을 추가
			int count = pstmt.executeUpdate();
			if(count==1) System.out.println("글 작성이 완료되었습니다.");
			else System.out.println("글 작성에 실패하였습니다.");
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt, conn);
		}
	}
	
	// 목록 보기
	public void selectListInfo() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			// SQL문 작성
			sql = "SELECT * FROM note ORDER BY num DESC";
			// JDBC 수행 3단계 : PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블로부터 결과 집합을 얻고 ResultSet에 담아서 반환
			rs = pstmt.executeQuery();
			System.out.println("글 번호\t제목\t\t이름\t작성일");
			String subject, name;
			while(rs.next()) {
				System.out.print(rs.getInt("num")+"\t");
				subject = rs.getString("subject");
				if(subject.length()>6+6) { // 제목이 길면 말줄임표 처리; 길이 상한이 60byte(=국문 20자)이므로, 정렬용으로 \t 2개 사용
					System.out.print(subject.substring(0,4+6)+"..\t");
				}
				else if(subject.length()>7) {
					System.out.print(subject+"\t");
				}
				else {
					System.out.print(subject+"\t\t");
				}
				name = rs.getString("name");
				if(name.length()>6) { // 이름이 길면 말줄임표 처리
					System.out.print(name.substring(0,4)+"..\t");
				}
				else {
					System.out.print(name+"\t");
				}
				System.out.println(rs.getDate("reg_date")); // 목록 보기에서는 연, 월, 일만 표시
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(rs, pstmt, conn);
		}
	}
	
	// 상세 글 보기
	public int selectDetailInfo(int num) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs= null;
		String sql = null;
		int exitcode = 0; // if~else 도달 전에 예외 발생할 수 있으므로 기본값 지정
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			// SQL문 작성
			sql = "SELECT * FROM note WHERE num=?";
			// JDBC 수행 3단계 : PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(1, num);
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블로부터 한 개의 레코드를 얻고 ResultSet에 담아서 반환
			rs = pstmt.executeQuery();
			if(rs.next()) {
				System.out.println("글 번호 : " + rs.getInt("num"));
				System.out.println("이름 : " + rs.getString("name"));
				System.out.println("비밀번호 : " + rs.getString("passwd"));
				System.out.println("제목 : " + rs.getString("subject"));
				System.out.println("내용 : " + rs.getString("content"));
				System.out.println("이메일 : " + rs.getString("email"));
				System.out.println("작성일 : " + rs.getString("reg_date")); // 상세 글 보기에서는 연, 월, 일, 시, 분, 초까지 표시
				exitcode = 1; // 있는 글 번호를 입력하여 정상적으로 상세 글 보기 작업을 완료한 경우
			}
			else {
				System.out.println(num + "번 글이 존재하지 않습니다!");
				exitcode = -1; // 없는 글 번호를 입력하여 상세 글 보기 작업에 실패한 경우
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(rs, pstmt, conn);
		}
		return exitcode;
	}
	
	// 글 수정
	public void updateInfo(int num, String name, String passwd, String subject, String content, String email) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		String sql = null;
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			// 빈 문자열을 입력받은 경우 기존 정보를 유지하기
			if((name+passwd+subject+content+email).equals("")) {
				System.out.println("수정할 내용이 없습니다!");
				return;
			}
			if(name.equals("")|passwd.equals("")|subject.equals("")|content.equals("")|email.equals("")) {
				sql = "SELECT * FROM note WHERE num=?";
				PreparedStatement pstmt2 = conn.prepareStatement(sql);
				pstmt2.setInt(1, num);
				ResultSet rs = pstmt2.executeQuery();
				if(rs.next()) {
					if (name.equals("")) name = rs.getString("name");
					if (passwd.equals("")) passwd = rs.getString("passwd");
					if (subject.equals("")) subject = rs.getString("subject");
					if (content.equals("")) content = rs.getString("content");
					if (email.equals("")) email = rs.getString("email");
				}
				DBUtil.executeClose(rs, pstmt2, null);
			}
			// SQL문 작성
			sql = "UPDATE note SET name=?, passwd=?, subject=?, content=?, email=? WHERE num=?";
			// JDBC 수행 3단계 : PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setString(1, name);
			pstmt.setString(2, passwd);
			pstmt.setString(3, subject);
			pstmt.setString(4, content);
			pstmt.setString(5, email);
			pstmt.setInt(6, num);
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블의 행 정보 갱신
			int count = pstmt.executeUpdate();
			if(count==1) System.out.println(num + "번 글을 수정하였습니다.");
			else System.out.println(num + "번 글을 수정하지 못했습니다!");
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt, conn);
		}
	}
	
	// 글 삭제
	public void deleteInfo(int num) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		String sql = null;
		
		try {
			// JDBC 수행 1,2단계
			conn = DBUtil.getConnection();
			// SQL문 작성
			sql = "DELETE FROM note WHERE num=?";
			// JDBC 수행 3단계 : PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(1, num);
			// JDBC 수행 4단계 : SQL문을 실행해서 테이블의 행을 삭제
			int count = pstmt.executeUpdate();
			if(count==1) System.out.println(num + "번 글을 삭제하였습니다.");
			else System.out.println(num + "번 글을 삭제하지 못했습니다!");
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt, conn);
		}
	}
}

다음으로