Programming/JDBC

JDBC에서 레코드 가져오기, insert문으로 레코드 입력하기 + 트리거 개념+ 시퀀스 개념.+ statement 클래스의 DML에 따른 함수들. 2021-08-27

최동훈1 2021. 8. 27. 17:00

이젠 실질적으로 자바 프로그램으로 DB에 접근해서 데이터를 얻어와 보자.

 

1. 만약 내가 DB에 저장된 모든 데이터를 가지고 오고 싶다면 어떻게 해야 할까?

쉽다. rs.next의 특성을 이용하면 된다. rs.next는 서버의 결과집합을 가리키는 포인터가 EOF에 도달했을때, false를 반환한다. 이걸 이용해서 반복문을 돌리면 된다.

public class program {
static String url="jdbc:oracle:thin:@localhost:1521/xepdb1";
	public static void main(String[] args) 
			throws ClassNotFoundException, SQLException {
		// TODO Auto-generated method stub
		
		String sql ="SELECT * FROM NOTICE ";
		Class.forName("oracle.jdbc.driver.OracleDriver");
		Connection con = 
		DriverManager.getConnection(url,"newlec","vdongv1620");
		Statement st= con.createStatement();
		ResultSet rs= st.executeQuery(sql);
		
		while(rs.next()!=false) {
			int id= rs.getInt("id");
		
		String title = rs.getString("title");
		String writerid=rs.getString("writer_id");
		Date regdate=rs.getDate("regdate");
		String content =rs.getString("content");
		int hit=rs.getInt("hit");
		System.out.printf("id:%d title: %s writer_id: %s regdate: %s "
				+ "content: %s hit: %d",id,title,writerid,regdate,content,hit);
		System.out.println();
		
		}
		
		rs.close();
		st.close();
		con.close();

	}

}

이렇게 말이다. 

 

2.만약에 notice 테이블에서 hit 칼럼의 값이 10 이상인 것만 출력하고 싶다면 어떻게 해야 할까?

두가지 방법이 있다. 바로 DB에서 모든 결과집합을 다 가져온 다음, 레코드 하나하나 조건식 if(hit>10) 이렇게 해서 필터링 하는 것과, 에초에 DB에서 SELECT * from notice where hit>10  이렇게 필터링 한 다음 바로 다 출력 하는 것이다.

 

개발자 입장에서 어떤 코드가 바람직할까?? 바로 후자가 훨~~씬 더 바람직 하다. 왜냐하면, 서버에서 만들어져서 가져올 레코드 수가 1억개가 넘는다면 네트워크 상에 엄청난 부하가 걸릴 것이다. 또한 데이터의 가공에 특화된 DBMS가 있는데 굳이 자바에서 자원을 낭비할 필요가 없다. 그래서 데이터의 필터링,정렬 등은 될수있으면 SQL로 하고, UI 레이아웃 등만 자바코드로 하는 것이 바람직하다.

		String sql ="SELECT * FROM NOTICE where hit>=3";

데이터 가공은 DBMS에서 미리 처리한다음 자바프로그램으로 넘겨주는것이 바람직하다.

그럼 SELECT 를 통해서 DBMS의 레코드를 가져오는 것은 할 수 있게 되었다.이번엔 insert를 통해 프로그램에서 DBMS에 새로운 레코드를 삽입할려면 어떻게 해야될까? 그럴려면 무작정 insert 문을 쓰기 이전에 사용자가 직접 입력해야하는 칼럼들과 그렇지 않은 칼럼등을 구분해야한다.

예를들면 notice의 ID, Title , Writer_id, content, Regdate, hit 의 칼럼들 중에서 Regdate, hit 등은 사용자가 직접 입력할 필요가 없으니 default 값으로 미리 처리해 둔다. 

ALTER table notice modify regdate default systimestamp;
ALTER table notice modify hit default '0';

또 ID 는 primary key 로써 레코드를 식별할수 있는 중복되지않는 값을 넣어야될 필요가 있다. 그래서 이것도 사용자가 입력하지 못하게끔 하고, 시퀀스를 통해 자동으로 입력되게끔 만들어야 한다.

테이블 편집기의 열 시퀀스 편집을 통해 설정.

열 시퀀스란 앞으로 PK 칼럼은 사용자가 레코드를 추가 할 때마다. 시퀀스 정의값에 따라 자동으로 순번이 매겨진다.

그런데 기존의 1부터 차례로 순번이 매겨진다면, 기존의 ID 값과 충돌이 일어날 수 있다. 그렇기에 기존에 존재하는 ID값을 확인하여 시작값을 겹치지 않게 설정해 준다.

이전 ID값의 마지막이 6이므로 7부터 시퀀스가 시작되게끔 함.

그 다음 본격적인 insert문을 작성 해 보려고 했는데, 궁금증이 생겼다. 내가

insert into notice(title, writer_id, content)
values('forever young','모리','최동훈');

이렇게 ID 값을 별도로 넣지 않아도, ID 순번이 자동으로 매겨졌다는 것이였다.

 

그래서 원인을 찾아보니, 내가 열 시퀀스 편집기를 통해서 시퀀스가 자동으로 들어간다고 설정했을때, 간과한 부분이 있다. 바로 트리거가 자동으로 생겼다는 것이다. 그래서 난 분명 NOTICE_SEQ.nextval 을 insert 문에 써주지 않았을테도, 자동으로 순번이 매겨졌던 것이다. !!! 처음에는 트리거의 존재가 몰라서, 테이블 편집기로 열 시퀀스를 설정해주면 이렇게 나오는 건가..?? 라고 생각했다.

아래 코드 참조.

insert into notice(id,title, writer_id, content) 
values(NOTICE_SEQ.nextval,'forever young','모리','최동훈');
-- 이렇게 id의 value 값으로 NOTICE_SEQ.nextval 를 써야 정상적으로
--ID 값에 시퀀스에 따라 순번이 매겨져야 정상인데

insert into notice(title, writer_id, content) 
values('forever young','모리','최동훈');
-- 이렇게 아무 값도 넣지 않았는데도, ID 값에 
--내가 시퀀스로 저장한 7번 부터 차례로 순번이 매겨져서 이상했다.

그런데 테이블 편집기로 위에서 ID 열 항복에 트리거라는 것이 생겨난것을 확인하고 트리거가 무엇인지 찾아보았다.

그냥 난 시퀀스 설정하는줄 알고 넘어갔는데, 트리거가 자동으로 생겨난 것이다.

오라클에서 트리거의 정의 데이는,터베이스 시스템에서 데이터의 입력, 갱신, 삭제 등의 이벤트가 발생할 때 마다

자동적으로 수행되는 사용자 정의 프로시저이다. 즉, 시퀀스를 ~SEQ.nextval 이렇게 일일이 넣어주지 않고도, 자동으로 레코드가 삽입될때마다 적용되는 과정을 만들어 버린 것이다.



내가 테이블 편집기에서 열 시퀀스를 설정하며 자동으로 생성된 트리거.

위의 사진을 보면, NOTICE란 테이블에 레코드를 INSERT 할때마다, 자동으로 ID라고 이름붙여진 칼럼에  다음 시퀀스 값이 추가되는 프로시저가 NEWLEC 스키마에 저장되어 있었던 것이다.

그래서 그냥 어떤 레코드를 insert로 삽입해도, ID값이 자동으로 매겨졌다.

 

그럼 이제 실질적으로 트리거가 적용된 스키마를 통해 JDBC로 insert 문을 넣어보겠다.

또 실수하지 말아야 할 것이 있는데

JDBC에서 쿼리 문장 실행을 할 때, SELECT 는 st.executeQuery() 함수를 사용하고 INSERT, UPDATE, DELETE 등 실제적으로 레코드를 수정하는 DML을 사용할때는, st.executeUpdate() 함수를 사용해야 한다. 

이유는 st.executeQuery()의 반환 타입은 ResultSet 이고(DB에서 select 로 결과집합을 가져오기에 당연하다.) 

st.executeUpdate()의 반환타입은 업데이트한 레코드의 수(삽입, 삭제, 업데이트 로 인한 테이블의 변경수) 를 정수값으로 반환한다.

 

 

각각 반환타입이 다른 모습. SELECT는 executeQuery를 이용해야하고, 다른 DML은 executeUpdate를 이용해야한다.
자바 API 래퍼런스에서 찾은 Statement 클래스의 executeQuery 메서드.

자바 공식 래퍼런스에서 찾아보니 java.sql 패키지에 존재하는 statement 클래스의 executeQuery 함수는이렇게 SELECT 문일때 쓴다고 나와있다.

자바 레퍼런스에서 찾은 executeupdate 메서드

executeupdate함수는 INSERT,UPDATE,DELETE 등의 쿼리에만 적용시킬수 있다.

아래의 자바 래퍼런스 참조.

Statement (Java Platform SE 7 ) (oracle.com)

 

Java Platform SE 7

 

docs.oracle.com

완성된 자바코드.

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class program {
static String url="jdbc:oracle:thin:@localhost:1521/xepdb1";
	public static void main(String[] args) 
			throws ClassNotFoundException, SQLException {
		// TODO Auto-generated method stub
		
		String sql ="insert into notice(title, writer_id, content) values('forever young','모리','최동훈')";
		Class.forName("oracle.jdbc.driver.OracleDriver");
		Connection con = 
		DriverManager.getConnection(url,"newlec","vdongv1620");
		Statement st= con.createStatement();
		int count=st.executeUpdate(sql);
		System.out.println(count);
		
	
		
		
		
		st.close();
		con.close();

	}

}

이렇게 실행해서 count 값을 출력해보니 1이 나온다. 

위 코드를 실행하니 1개의 레코드가 update된 모습.

공부시간 4시간.

순공부시간 3시간.

 

오늘은 집중을 잘 하고, 주변환경의 변화에 상관안쓰고 새로운 환경(섀도우 카페)에서 집중력이 올라간 거 같다.