트랜잭션 : 더이상 쪼갤 수 없는 단위
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한다.
마이그레이션 검색하기.
⭐⭐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());
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부터 시작한다.
그리고 여기에 와이드카드(%)를 넣어줘야 한다. 그래야 오류가 나지 않는다.
결과 집합. select문 결과를 담은 객체이다. 표같은 거 나타내는 거.
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 이렇게 '언더바'로 적어야 한다!!
JUnit 4로 가기!!
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;
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) {
}
}
}
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());
}
}
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;
}
}