데이터베이스(트랜잭션, statement, preparedStatement, ResultSet, 8859_1와 EUC_KR)

제이·2023년 5월 24일
0

데이터베이스

목록 보기
2/2
post-thumbnail

트랜잭션

트랜잭션 : 더이상 쪼갤 수 없는 단위
con.setAutoCommit()

start --> A-> B -> C -> D ----> end
A :ok
B :ok
C : failed
그럼 작업을 시작하기 전 단계로 돌아간다. (rollback)

A~D : 다 ok
그럼 A부터 D까지 반영해라! 하는게 commit

트랜잭션 속성 : 원자성
A~D까지를 하나로 묶어서 원자로 본다.

데이터베이스에 연결(Connection객체 얻어온거)을 하고 나서,

싱글톤객체 - 공유되는 객체, 상태를 가지면 안된다.
상태는 멤버변수다.

커낵션 기능중에 바로 반영하지말고 내가 반영하라고 할 때 해라.
만약 insert를 했다가 취소했을 때 데이터는 없지만, 자동적으로 이미 번호를 할당 받았어.
오토커밋끄고 일이 다 완성되었을 때 본 다음에 처리하는 게 트랜잭션이다!
3개의 일이 따로지만, 하나로 보고 3개 다 성공해야 성공한 것으로 본다.
일이 성공하면 한꺼번에 반영한다. 3개중에 하나라도 성공못하면 rollback한다.

마이그레이션 검색하기.

Statement

⭐⭐connection을 통해서 데이터베이스에 질의를 보내는 기능.

메서드

  • executeUpdate(sql문) : sql실행 -> return int
    int인 이유 : 결과에 따라 몇줄이 바뀌었는 지 알려줌
    insert, delete, update할 때 사용.

  • executeQuery(sql문) : sql실행 -> return ResultSet
    Select에 사용

  • String sql = "INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment) VALUES ('%s', '%s', '%s', '%d', '%s', '%s')"; 라고 가정할 때 아래처럼 사용한다.

  • sql = String.format(sql, book.getTitle(), book.getWriter(), book.getPassword(), book.getPrice(), book.getPublisher(), book.getComment());

PreparedStatement

statement가 쿼리를 중간에 String.format()한 것을 얘가 가지고 있다. 날때부터 준비가 되어 있는 애다. 뭘 할지 알고 있다. 쿼리구문을 해석하면서 태어난다.

메서드

  • stmt = con.createStatement()
    :statement 처음 만들 때 이거 사용하는 듯.

  • pStmt = con.prepareStatement(sql) : statement준비해라라는 뜻.

  • pStmt.setString(1, toEn(book.getTitle()));
    :해석 - 첫번째 '?'를 뜻하는 게 1이다. 그리고 그 뒤에는 바꿀 값을 넣는다.

  • result = pStmt.executeUpdate()
    :statement랑 다르게 파라미터로 쿼리문을 안넣는다.

  • executeQuery()

  • pStmt.clearParameters()
    여러개 수정, 삽입, 삭제할 때. 아이디가 여러개 들어올거고 여러개 삭제 될건데 그때 이걸 꼭 해줘야 한다. 값들을 전부 다 초기화해준다.
    지금 하나씩 해서 필요없지만 나중에 여러개 할 때 필요함.

String sql = "SELECT * FROM a_book WHERE b_title LIKE ?";

pStmt = con.prepareStatement(sql);
와일드 카드를 위의 LIKE 뒤에 적는 게 아니다. 쿼리문에 적는 거 아니다!
-> pStmt.setString(1, "%" + toEn(b_title) + "%")
뜻 : 첫번째 물음표에 title을 넣어라. 인덱스인데 1부터 시작한다.
그리고 여기에 와이드카드(%)를 넣어줘야 한다. 그래야 오류가 나지 않는다.

ResultSet

결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.

8859_1언어와 EUC_KR

DB 언어 : 8859_1
java 언어 : EUC_KR

  • insert, update 일 때, (euc_kr -> 8859_1)로 바꿔야 한다 :이거는 static toEnd(kor:String) : String 메소드로 만든다.

  • select 일 때, (8859_1 -> euc-kr)
    이거는 static toKor(en:String) : String

팁) EUC_KR에서 'euc-kr' 이걸로 적으면 안된다. euc_kr 이렇게 '언더바'로 적어야 한다!!


JUint

JUnit 4로 가기!!


코드

book.sql

CREATE TABLE a_book (
	b_num			INT				PRIMARY KEY 	AUTO_INCREMENT,
	b_title			CHAR(200)		NOT NULL,
	b_writer		CHAR(30)		NOT NULL,
	b_password		VARCHAR(10)		NOT NULL,
	b_price			INT				NOT NULL,
	b_publisher 	VARCHAR(30)		NOT NULL,
	b_comment		CHAR(200)		NOT NULL
);

-- 전체보기SELECT * FROM a_book
 SELECT * FROM a_book ORDER BY b_num DESC;

--책하나보기
SELECT * FROM a_book WHERE b_num = ? ;

-- 책 추가하기
INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment)
VALUES (?,?,?,?,?,?);

-- 책 삭제하기
DELETE FROM a_book WHERE b_num = ? ;

--책 수정
--다 바꾼다고 전제.
UPDATE a_book SET b_title=?, b_writer=?, b_price=?, b_publisher=?, b_comment=? WHERE b_num=? ;


DROP TABLE a_book;

bookDao.java(statement 버전)

package kr.ac.green.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import kr.ac.green.Book;

/*
 * JDBC(java database connectivity) 
 * 
 * 1. 드라이버 로드(1번만 수행하면 된다.)
 * 2. 연결
 * 3. 질의
 * 4. 자원해제
 */

public class BookDao {
	private static final BookDao INSTANCE = new BookDao();
	
	private BookDao() {
		// 1. 드라이버 로드
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static BookDao getInstance() {
		return INSTANCE;
	}
	
	public Connection connect() {	// import java.sql.Connection... 데이터 베이스에 연결이 되는거야
		Connection con = null;
		try {
			con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
		} catch(SQLException e) {
			e.printStackTrace();
		}
		return con;
	}
	
	public void disconnect(Connection con) {
		try {
			con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	
	public int insertBook(Connection con, Book book) {
		int result = 0;
		// statement? 
		// 동시에 접근할 수 있는 세션 수 정해져 있는데 자원해제 안하면 그 세션이 물려서 accessDeny가 발생함..
		// 그럼 프로그램이 동작을 안해 그래서 자원해제 분명히 해야한다.
		Statement stmt = null;	// sql.Statement
		
		try {
			// executeUpdate :  INSERT, UPDATE, DELETE  => int 리턴
			// executeQUERY : SELECT 
			stmt = con.createStatement();
			//String sql = "(b_title, b_writer, b_password, b_price, b_publisher, b_comment) "
			//		+ "VALUES ('" + book.getTitle() +"','" + book.getWriter() + "','"+ book.getPassword() + "',"+book.getPrice() + ",'" + book.getPublisher() + "','" + book.getComment() + "')";
			
			String sql = "INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment) VALUES ('%s', '%s', '%s', '%d', '%s', '%s')";
			sql = String.format(sql, book.getTitle(), book.getWriter(), book.getPassword(), book.getPrice(), book.getPublisher(), book.getComment());
			result = stmt.executeUpdate(sql);
		
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(stmt);
		}
		return result;
	}
	
	public int deleteBook(Connection con, int num) {
		//몇개가 지워졌는가.
		int result = 0;
		Statement stmt = null;//데이터베이스에 질의를 날리는 거.
		
		try {
			stmt = con.createStatement();
			String sql = "DELETE FROM a_book WHERE b_num =" + num;
			result = stmt.executeUpdate(sql);	//cud에 해당.
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(stmt);
		}
		
		return result;
	}

	
	public int updateBook(Connection con, Book book) {
		int result = 0;
		Statement stmt = null;
		
		try {
			stmt = con.createStatement();
		
			String sql = "UPDATE a_book SET b_title='%s', b_writer='%s', b_price=%d, b_publisher='%s', b_comment='%s' WHERE b_num= %d";
			sql = String.format(sql, book.getTitle(), book.getWriter(), book.getPrice(), book.getPublisher(), book.getComment(), book.getNum());
			
			result = stmt.executeUpdate(sql); //일시켜야한다. 
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(stmt);
		}
		
		return result;
	}
	
	public Vector<Book> getAll(Connection con) {
		Vector<Book> list = new Vector<Book>();
		//책이 없을 때 빈 벡터를 던질 것이다.
		Statement stmt = null;
		ResultSet rs = null; //결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.
		
		try {
			stmt = con.createStatement();
			String sql = "SELECT * FROM a_book ORDER BY b_num DESC";
			rs = stmt.executeQuery(sql);
			while(rs.next()) {	//다음 라인이 있습니까? true이면, 위치를 다음 레코드로 이동한다. 
				list.add(rowMapping(rs));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			close(rs);
			close(stmt);
		}
		
		return list;
		
	}
	//SELECT * FROM a_book WHERE b_num = ? ;
	public Book getBookByNum(Connection con, int num) {
		Book book = null;
		Statement stmt = null;
		ResultSet rs = null;
		
		try {
			stmt = con.createStatement();
			String sql = "SELECT * FROM a_book WHERE b_num =" + num ;
			rs = stmt.executeQuery(sql);
			if(rs.next()) {
				book = rowMapping(rs);
			}
			
		}catch(SQLException e) {
			
		}finally {
			close(rs);
			close(stmt);
		}
		return book;
	}
	
	private Book rowMapping(ResultSet rs) throws SQLException { //spring에 나온다.
		int b_num = rs.getInt("b_num");
		String b_title = rs.getString("b_title");
		String b_writer = rs.getString("b_writer");
		String b_password = rs.getString("b_password");
		int b_price = rs.getInt("b_price");
		String b_publisher = rs.getString("b_publisher");
		String b_comment = rs.getString("b_comment");
		
		return new Book(b_num, b_title, b_writer, b_password, b_price, b_publisher, b_comment);
	}
	
	private void close(ResultSet rs) {
		try {
			rs.close();
		}catch(Exception e) {}
	}
	
	private void close(Statement stmt) {
		try {
			stmt.close();
		}catch (Exception e) {
		}
	}
}

TestBookDao.java

package kr.ac.green.test;

import java.sql.Connection;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import kr.ac.green.Book;
import kr.ac.green.dao.BookDao;

public class TestBookDao {
	// 어떤 상태냐에 따라 골라 사용하면 된다.
	private static BookDao dao;
	private Connection con;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {	// 전체 테스트 전 1번 호출
		dao = BookDao.getInstance();
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {	// 전체 테스트 후 1번 호출
	}

	@Before
	public void setUp() throws Exception {	// 매번 테스트할 때마다
		con = dao.connect();
		//con.setAutoCommit(false); //바로 값이 들어가지 말라고 해놓는 거라서 insert만 시행해도 값이 들어가지 않는다. 
	}

	@After
	public void tearDown() throws Exception {	// 매번 테스트할 때마다
		dao.disconnect(con);
	}

	@Test
	public void testInsertBook() {// 실제 테스트를 담당, 복수개 생성 가능
		//insertBook 테스트
		
		//가짜개체 만듦
		Book book = new Book("testTitle", "testWriter", "password", 100, "testPublisher", "testComment");
		int result = dao.insertBook(con, book); // 몇개가 인서트 되었는지를 리턴해준다.
		Assert.assertEquals(1, result);	// 예상하는 값(1)대로 나오면 실제 값은 result이니까 둘이 비교를 한다.
		
		//문제점이 있어 테스트를 하는데 실제로 값이 들어가버림
	}
	
	@Test
	public void testDeleteBook() {
		int result = dao.deleteBook(con, 9);
		Assert.assertEquals(1, result);
	}
	
	@Test
	public void testupdateBook() {
		Book book = new Book(16, "title1", "writer1", "password1", 200, "publisher1", "comment1");
		int result = dao.updateBook(con, book);
		Assert.assertEquals(1, result);
	}
	
	@Test
	public void testGetAll() {
		Assert.assertEquals(2, dao.getAll(con).size());
	}
	
	@Test
	public void testGetBookByNum() {
		int num = 1;
		Book book = dao.getBookByNum(con, num);
		Assert.assertEquals(1, book.getNum());
		Assert.assertEquals("testComment", book.getComment());
	}
}

BookDao.java(preparedStatement 버전, 인코딩)

package kr.ac.green.dao;

import java.io.UnsupportedEncodingException;
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.Vector;

import kr.ac.green.Book;

/*
 * JDBC(java database connectivity) 
 * 
 * 1. 드라이버 로드(1번만 수행하면 된다.)
 * 2. 연결
 * 3. 질의
 * 4. 자원해제
 */

public class BookDao {
	
	private static final BookDao INSTANCE = new BookDao();
	
	private BookDao() {
		// 1. 드라이버 로드
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static BookDao getInstance() {
		return INSTANCE;
	}
	
	public Connection connect() {	// import java.sql.Connection... 데이터 베이스에 연결이 되는거야
		Connection con = null;
		try {
			con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
		} catch(SQLException e) {
			e.printStackTrace();
		}
		return con;
	}
	
	public void disconnect(Connection con) {
		try {
			con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public int insertBook(Connection con, Book book) {
		int result = 0;
		// statement? 
		// 동시에 접근할 수 있는 세션 수 정해져 있는데 자원해제 안하면 그 세션이 물려서 accessDeny가 발생함..
		// 그럼 프로그램이 동작을 안해 그래서 자원해제 분명히 해야한다.
		//Statement stmt = null;	// sql.Statement
		PreparedStatement pStmt = null;
		String sql = "INSERT INTO a_book (b_title, b_writer, b_password, b_price, b_publisher, b_comment)\r\n" + "VALUES (?, ?, ?, ?, ?, ?)";
		
		
		try {
			// executeUpdate :  INSERT, UPDATE, DELETE  => int 리턴
			// executeQUERY : SELECT 
			pStmt = con.prepareStatement(sql);
			pStmt.setString(1, toEn(book.getTitle()));
			pStmt.setString(2, toEn(book.getWriter()));
			pStmt.setString(3, book.getPassword());
			pStmt.setInt(4, book.getPrice());
			pStmt.setString(5, toEn(book.getPublisher()));
			pStmt.setString(6, toEn(book.getComment()));
		
			result = pStmt.executeUpdate();
			pStmt.clearParameters();
		
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pStmt);
		}
		return result;
	}
	
	public int deleteBook(Connection con, int num) {
		//몇개가 지워졌는가.
		int result = 0;
		//Statement stmt = null;//데이터베이스에 질의를 날리는 거.
		PreparedStatement pStmt = null;
		String sql = "DELETE FROM a_book WHERE b_num = ?" ;
		
		try {
			pStmt = con.prepareStatement(sql);
			pStmt.setInt(1, num);
			result = pStmt.executeUpdate();	
			pStmt.clearParameters();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pStmt);
		}
		
		return result;
	}
	
	public int updateBook(Connection con, Book book) {
		int result = 0;
		//Statement stmt = null;
		PreparedStatement pStmt = null;
		String sql = "UPDATE a_book SET b_title=?, b_writer=?, b_price=?, b_publisher=?, b_comment=? WHERE b_num = ?";
		try {
			pStmt = con.prepareStatement(sql); //statement준비해라라는 뜻.
			pStmt.setString(1, toEn(book.getTitle()));
			pStmt.setString(2, toEn(book.getWriter()));
			pStmt.setInt(3, book.getPrice());
			pStmt.setString(4, toEn(book.getPublisher()));
			pStmt.setString(5, toEn(book.getComment()));
			pStmt.setInt(6, book.getNum());
			
			result = pStmt.executeUpdate();
			pStmt.clearParameters(); //여러개 수정, 삽입, 삭제할 때. 아이디가 여러개 들어올거고 여러개 삭제 될건데 그때 이걸 꼭 해줘야 한다. 값들을 전부 다 초기화해준다. 지금 하나씩 해서 필요없지만 나중에 여러개 할 때 필요함. 
			//stmt = con.createStatement();
		
			//String sql = "UPDATE a_book SET b_title='%s', b_writer='%s', b_price=%d, b_publisher='%s', b_comment='%s' WHERE b_num= %d";
//			sql = String.format(
//					sql, 
//					toEn(book.getTitle()),
//					toEn(book.getWriter()), 
//					book.getPrice(), 
//					toEn(book.getPublisher()), 
//					toEn(book.getComment()),
//					book.getNum());
			
			//result = stmt.executeUpdate(sql); //일시켜야한다. 
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//close(stmt);
			close(pStmt);
		}
		
		return result;
	}
	
	public Vector<Book> getAll(Connection con) {
		Vector<Book> list = new Vector<Book>();
		//책이 없을 때 빈 벡터를 던질 것이다.
		PreparedStatement pStmt = null;
		ResultSet rs = null; //결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.
		String sql = "SELECT * FROM a_book ORDER BY b_num DESC";
		
		try {
			pStmt = con.prepareStatement(sql);
			rs = pStmt.executeQuery();
			while(rs.next()) {	//다음 라인이 있습니까? true이면, 위치를 다음 레코드로 이동한다. 
				list.add(rowMapping(rs));
			}
					
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			close(rs);
			close(pStmt);
		}
		
		return list;
		
	}
	//SELECT * FROM a_book WHERE b_num = ? ;
	public Book getBookByNum(Connection con, int num) {
		Book book = null;
		PreparedStatement pStmt = null;
		ResultSet rs = null;
	
		String sql = "SELECT * FROM a_book WHERE b_num = ?";
		
		try {
			pStmt = con.prepareStatement(sql);
			pStmt.setInt(1, num);
			rs = pStmt.executeQuery();
			pStmt.clearParameters();
			if(rs.next()) {
				book = rowMapping(rs);
			}
			
		}catch(SQLException e) {
			
		}finally {
			close(rs);
			close(pStmt);
		}
		return book;
	}
	
	public Vector<Book> getBooksByTitle(Connection con, String b_title) {
		Vector<Book> list = new Vector<Book>();
		PreparedStatement pStmt = null;	// 쿼리를 중간에 포멧한 것을 얘가 가지고 있다. 날때부터 준비가 되어 있는 애다. 뭘 할지 알고 있다. 쿼리구문을 해석하면서 태어난다. 
		ResultSet rs = null;
		
		String sql = "SELECT * FROM a_book WHERE b_title LIKE ?";
		try {
			pStmt = con.prepareStatement(sql);
			//와일드 카드 여기 적기 쿼리문에 적는 게 아니라. 위의 LIKE 뒤에 적는 게 아니라. 
			pStmt.setString(1, "%" + toEn(b_title) + "%");//첫번째 물음표에 title을 넣어라. 인덱스인데 1부터 시작한다. 여기에 와이드카드를 넣어줘야 한다. 그래야 오류가 나지 않는다. 
			rs = pStmt.executeQuery(); //파라미터 없다. 
			while(rs.next()) {
				list.add(rowMapping(rs));
			}
			
		}catch(SQLException e) {
			e.printStackTrace();
		}
		
		return list;
	
	}
	
	private Book rowMapping(ResultSet rs) throws SQLException { //spring에 나온다.
		int b_num = rs.getInt("b_num");
		String b_title = toKor(rs.getString("b_title"));
		String b_writer = toKor(rs.getString("b_writer"));
		String b_password = rs.getString("b_password");
		int b_price = rs.getInt("b_price");
		String b_publisher = toKor(rs.getString("b_publisher"));
		String b_comment = toKor(rs.getString("b_comment"));
		
		return new Book(b_num, b_title, b_writer, b_password, b_price, b_publisher, b_comment);
	}
	
	private void close(ResultSet rs) {
		try {
			rs.close();
		}catch(Exception e) {}
	}
	
	private void close(Statement stmt) {
		try {
			stmt.close();
		}catch (Exception e) {
		}
	}
	
	/*
	 * DB : 8859_1
	 * java : EUC-KR
	 * insert, update ->  euc_kr -> 8859_1  :이거는 static toEnd(kor:String) : String 메소드로 만든다.
	 * select : 8859_1 -> euc-kr : static toKor(en:String) : String
	 * */
	
	public String toEn(String kor) {
		String en = null;
		try {
			en = new String(kor.getBytes("EUC_KR"), "8859_1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return en;
	}
	
	public String toKor(String en) {
		String kor = null;
		try {
			kor = new String(en.getBytes("8859_1"), "EUC_KR");
		}catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return kor;
	}
}
profile
Hello :)

0개의 댓글