CREATE TABLE test1 (
id VARCHAR2(30) PRIMARY KEY,
name VARCHAR2(30) NOT NULL
);
CREATE TABLE test2 (
id VARCHAR2(30) PRIMARY KEY,
birth VARCHAR2(30) NOT NULL,
FOREIGN KEY(id) REFERENCES test1(id)
);
CREATE TABLE test3 (
id VARCHAR2(30) PRIMARY KEY,
tel VARCHAR2(30) NOT NULL,
FOREIGN KEY(id) REFERENCES test1(id)
);
SELECT * FROM test1;
SELECT * FROM test2;
SELECT * FROM test3;
package transEx;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import db_util.DBConn;
public class Ex_Transaction {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Connection conn = DBConn.getConnection();
PreparedStatement pstmt = null;
String sql;
String id, name, birth, tel;
try {
System.out.print("아이디 ? ");
id = br.readLine();
System.out.print("이름 ? ");
name = br.readLine();
System.out.print("생년월일 ? ");
birth = br.readLine();
// 전화번호를 입력하지 않고 그냥 엔터를 누를경우와 전화번호를 입력했을 때와 비교
System.out.print("전화번호 ? ");
tel = br.readLine();
// 트랜잭션이 필요한 부분은 반드시 하나의 try 블록에서 모두 코드를 작성해야 한다.
// 자동으로 COMMIT 되지 않도록 설정
conn.setAutoCommit(false);
sql = "INSERT INTO test1(id, name) VALUES (?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
pstmt.setString(2, name);
pstmt.executeUpdate();
pstmt.close();
sql = "INSERT INTO test2(id, birth) VALUES (?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
pstmt.setString(2, birth);
pstmt.executeUpdate();
pstmt.close();
sql = "INSERT INTO test3(id, tel) VALUES (?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
pstmt.setString(2, tel);
pstmt.executeUpdate();
// 커밋
conn.commit();
System.out.println("데이터 추가 성공...");
} catch (SQLException e) {
try {
// 세 개의 테이블 중 하나의 테이블이라도 추가하는 도중에 에러가 발생하면 모두 롤백
conn.rollback();
} catch (Exception e2) {
}
System.out.println(e.toString());
System.out.println("데이터 추가 실패...");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (Exception e2) {
}
}
try {
conn.setAutoCommit(true);
} catch (Exception e2) {
}
DBConn.close(); // 프로그램 종료 시
}
}
}
conn.setAutoCommit(false)를 통해 트랜잭션이 다 이뤄지고 나서 3개의 테이블에 모두 COMMIT되지 않는 경우에는 데이터가 들어가지 않도록 한다. 또 트랜잭션이 필요한 부분은 반드시 하나의 try 블록에서 모두 코드를 작성해야한다. 예외가 발생했을때는 ROLLBACK을 통해 데이터가 들어가지 않도록 해야한다.
package score3;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public interface ScoreDAO {
public int insertScore(ScoreDTO dto) throws SQLException;
public int updateScore(ScoreDTO dto) throws SQLException;
public int deleteScore(String hak) throws SQLException;
public ScoreDTO readScore(String hak);
public List<ScoreDTO> listScore();
public List<ScoreDTO> listScore(String name);
public Map<String, Integer> averageScore();
}
-- 프로시저에서 DML(INSERT, UPDATE, DELETE)작업 후 반드시 COMMIT
CREATE OR REPLACE PROCEDURE insertScore
(
pHak IN score.hak%TYPE,
pName IN score.name%TYPE,
pBirth IN score.birth%TYPE,
pKor IN score.kor%TYPE,
pEng IN score.eng%TYPE,
pMat IN score.mat%TYPE
)
IS
BEGIN
INSERT INTO score(hak,name,birth,kor,eng,mat) VALUES
(pHak, pName, pBirth, pKor, pEng, pMat);
COMMIT;
END;
/
SELECT * FROM user_procedures;
EXEC insertScore('5555', '마마마', '2003-03-03', 80, 90, 70);
SELECT * FROM score;
-- 수정 프로시저
CREATE OR REPLACE PROCEDURE updateScore
(
pName IN score.name%TYPE,
pBirth IN score.birth%TYPE,
pKor IN score.kor%TYPE,
pEng IN score.eng%TYPE,
pMat IN score.mat%TYPE,
pHak IN score.hak%TYPE
)
IS
BEGIN
UPDATE score SET name=pName, birth=pBirth, kor=pKor, eng=pEng,
mat=pMat WHERE hak=pHak;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20100, '존재하지 않는 자료입니다.');
END IF;
COMMIT;
END;
/
EXEC updateScore('마마마', '2000-10-10', 100, 100, 100, '5555');
SELECT * FROM score;
-- 삭제 프로시저
CREATE OR REPLACE PROCEDURE deleteScore
(
pHak IN score.hak%TYPE
)
IS
BEGIN
DELETE FROM score WHERE hak = pHak;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20100, '존재하지 않는 자료 입니다.');
END IF;
COMMIT;
END;
/
EXEC deleteScore('5555');
SELECT * FROM score;
-- 학번 검색 프로시저
CREATE OR REPLACE PROCEDURE readScore
(
pResult OUT SYS_REFCURSOR,
pHak IN VARCHAR2
)
IS
BEGIN
OPEN pResult FOR
SELECT hak, name, TO_CHAR(birth, 'YYYY-MM-DD') birth, kor, eng, mat,
(kor+eng+mat) tot, (kor+eng+mat)/3 ave
FROM score where hak = pHak;
END;
/
-- 전체 리스트 프로시저
CREATE OR REPLACE PROCEDURE listScore
(
pResult OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN pResult FOR
SELECT hak, name, birth, kor, eng, mat,
(kor+eng+mat) tot, (kor+eng+mat)/3 ave,
RANK() OVER( ORDER BY (kor+eng+mat) DESC ) rank
FROM score;
END;
/
-- 이름 검색 프로시저
CREATE OR REPLACE PROCEDURE searchNameScore
(
pResult OUT SYS_REFCURSOR,
pName IN VARCHAR2
)
IS
BEGIN
OPEN pResult FOR
SELECT hak, name, birth, kor, eng, mat,
(kor+eng+mat) tot, (kor+eng+mat)/3 ave
FROM score where INSTR(name, pName) >= 1;
END;
/
-- 각 과목 평균프로시저
CREATE OR REPLACE PROCEDURE averageScore
(
pKor OUT NUMBER,
pEng OUT NUMBER,
pMat OUT NUMBER
)
IS
BEGIN
SELECT NVL(AVG(kor), 0), NVL(AVG(eng), 0), NVL(AVG(mat), 0)
INTO pKor, pEng, pMat
FROM score;
END;
/
- 직렬화란 자바 시스템 내부에서 사용되는 객체 또는 데이터를 자바 외부에서도 사용할 수 있도록 바이트(byte)단위로 변환하는 것을 말한다.
- 직렬화가 가능한 자료형 및 객체
기본형 타입 (boolean, char, byte, short, int, long, float, double)
Serializable 인터페이스를 구현한 클래스의 객체
transient가 사용된 멤버변수(null 전송), static 멤버변수, 메소드는 직렬화 대상에서 제외된다.
역 직렬화(Deserialization)
- 직렬화된 바이트(byte)단위의 데이터를 다시 원래 데이터로 복원하는 것을 말한다.
java.io.Serializable 인터페이스
- 객체를 직렬화하기 위해서는 먼저, 객체를 직렬화가 가능하도록 Serializable 인터페이스를 구현해야 한다.
- 이 인터페이스는 객체 직렬화가 제공되어야 함을 자바 가상 머신에 알려주는 역할을 한다.
- Serializable 인터페이스는 다른 인터페이스와는 달리 구현해야 할 메소드가 없으므로 단지 Serializable 인터페이스만 implements 하면 된다.
- 객체의 직렬화 대상에서 제외되는 것은 멤버 변수가 static으로 선언된 경우, 멤버 변수가 transient로 선언된 경우와 메소드이다.
형식
public class 클래스명 implements Serializable {
// 클래스 정의 부분
}
transient
- 직렬화 대상에서 제외할 멤버 변수는 transient 키워드를 사용하여 제외한다.
- transient 키워드가 붙은 멤버 변수가 복원되면, 숫자변수는 0, 객체는 null로 설정된다.
public class 클래스명 implementsSerializable{
접근제어자 자료형 변수1;
접근제어자 transient 자료형 변수2;
// 클래스 구현
}
SerialVersionUID 필드
- serialVersionUID 는 직렬화에 사용되는 고유 아이디로, 선언하지 않으면 JVM에서 디폴트로 자동생성된다.
serialVersionUID는 역 직렬화 해서 읽어 들일 때 캐스팅한 클래스의 serialVersionUID와 역직렬화 한 serialVersionUID가 맞는지 확인하기 위해 사용된다.
- 선언하지 않아도 동작하는데 문제는 없지만, JVM에 의한 디폴트 serialVersionUID 계산은 클래스의 세부 사항을 매우 민감하게 반영하기 때문에 컴파일러 구현체에 따라서 달라질 수 있어 역 직렬화(deserialization) 과정에서 예상하지 못한 InvalidClassException을 유발할 수 있다.
- Java에서는 명시적으로 serialVersionUID를 선언할 것을 적극 권장하고 있다.