20장 데이터베이스 입출력
공유데이터는 데이터베이스에 저장하고 읽고 저장 수정 등을 한다.
db연결방법 저장방법등 배움
20.1 JDBC개요
자바는 데이터베이스와 연결해서 데이터 입출력 작업을 할 수 있도록 JDBC라이브러리를 제공한다.
이는 하나의 프레임 워크 라고 할 수 있다.
이 프레임워크서 제공하는 클래스 인터페이스를 통해서 사용하는 것이다.

이들은 데이터베이스 관리 '소프트웨어'이다.
어떠한 것을 사용하더라도 JDBC 라이브러리를 사용할 수 있다.
이걸 사용하기 위해 외부에서 가져와서 사용해야하는데 이것이 JDBC드라이버이다.
작성할때는 JDBC인터페이스를 사용하고 이걸 구현하는 객체를 JDBC드라이버이다.
이걸 누가만드냐? DBMS만드는 회사가 만든다.
우리는 JDBC인터페이스를 가지고 JDBC드라이버를 가져와서 설계하는 것이다.
java.sql javax.sql패키지가 있다. 여기에 속한 인터페이스 클래스를 통해서 데이터베이스 작업을 한다.
대표적으로 사용하는 것은 다음과 같다.

여기서 화살표는 상속의 관계가 아닌 생성의 관계이다.
Connection 연결 인터페이스: 이런 데이터베이스 관리시스템도 서버여서 TCP를 사용한다. 연결하고 데이터를 주고받는다.
Statement종류들은 인터페이스는 SQL을 DBMS에보내고 이 기술된 내용대로 데이터를 처리한다.
DBMS는 SQL만으로 부를 수 있는 것은 아님. CallableStatment는 프로시저, 펑션등을 사용해서 데이터베이스 실행가능한 프로그램
ResultSext 결과가 담긴 Set 결과를 얻어 DB에서 가져온 데이터를 읽을 때 사용
20.2 DBMS설치
데이터 보안 등 안정적인 것을 위해 상용 DB를 사용하는데 가장 Oracle을 많이 사용한다.
oracle.com에서 설치
매우중요!
디렉토리를 생성한다음 압축파일명이 디렉토리 명이되도록 압축을 해제한다.
일부는 설치용 일부는 dbms제공하는게 필요 잘못건들면 오류발생하니 잘 해라
윈도우 서비스형태로 함
보통설치할대 운영체제와 다른곳에 설치함
문제발생시 운영체제 포맷하는데 db가 날아가기때문
운영체제가 일하고 db가 일하고 그러면 드라이버가 할일이 너무많아짐
컨테이너 없이 생성하도록 공부시에는 사용하기 귀찮아짐
나중에 데이터베이스 로컬 에서만 사용하면 상관없는데 프로젝트를 할때는 원격연결을 해야한다.
외부에서 1521포트로 접속가능하게 만들기
20.3 Cleint Tool설치
Oracle DB개발 및 관리를 간편하게 해주는 무료 툴이다.
개발자도 관리자도 쓰는 툴임.
명령어 기반 SQL Plus를 이용해도 되지만 Ui기반을 이용하면 모델링에서부터 상태확인 스크립트 개발 등을 편리하게 할 수 있다.


20.4 DB구성
한글깨짐? 기본이 인코딩 UTF-8로 바꿔주기
테이블이란 행과열로 행과 컬럼만들어져있음.
테이블의 data을 들어가면 각테입르에 어떤 데이터가 있는지 알수있다.

가로가 행 세로가 컬럼
열부분을 보면 값의 타입을 알수있다.
데이터를 추가한다는 것은 한 행을 추가하는 것임.
데이터가 없을때? 프로그램으로 추가하는 것임.
BORADS는 게시물이 들어가는 테이블
시퀸스 BNO가 순차적으로 제공되는 객체
번호를 하나씩 받아서 게시판의 BNO의 값으로 사용한다.

20.5 DB연결
클라이언트 프로그램에서 DBMS가 서버라면 사용하는쪽은 다 클라이언트 프로그램임

20.5.1 DB Driver설치
DB와 연결하려면 DBMS의 JDBC Driver가 필요하다. 도한연결에 필요한 4가지 정보가 잇어야한다.
네트워크 어댑터까지 1.IP번호 2.DBMS별로 필요한 포트번호 오라클안의 3.사용자계정,비밀번호 4.DB이름
Oracle 같은 경우 설치할때 \Oracle\WINDOWS.X64_193000_db_home\jdbc\lib
설치경로 파일에 jar파일이 있다.
여기에는 JDBC를 구현한 인터페이스가 있다.
원격에서할경우 별도로 다운을 받아야한다.
롬복, json을 추가하듯이 lib파일에 넣고 빌드패스를 해주면 된다.
환경변수에 추가해서 cmd에서 사용할 수 있게도 할 수 있다.
20.5.2 DB연결
JDBC Driver를 메모리로 로딩하는 것이다.
Class.forName("oracle.jdbc.OracleDriver");
클래스객체를 얻을 때 사용할 수 있다고 배운 것.
이 클래스를 메모리에 로딩해야해
왜? 하나 JDBC Driver클래스에 static블록 실행되면서 DriverManager에 자동적으로 등록이 됨.
class OracleDricer{
static {
Driver driver = new OracleDriver();
DrverManager.registerDriver(driver);}
이것은 메모리 블록에 로딩이되면 실행이 됨. 그래서 등록되며 사용이 가능한 것임.
만약 드라이버가 없다면 ClassNotFoundException 예외가 발생하므로 예외처리를 해야한다.
예외가 발생하면 빌드패스를 제대로 했는지 안했는지를 볼 필요가 있다.
DriverManager.getConnection(null, "java", "oracle");
여기서 3가지 정보 IP번호 포트번호 DB이름 이것들이 들어가는 문자열 형식은 DBMS마다 다르다. api도큐먼트를 보고 알기
jdbc:orcale:thin:@ip주소:포트번호/DB이름
jdbc:orcale:thin:@localhost:1521/orcl
여기서 thin은? tcp프로토콜을 사용하면 이걸 가지고 있음. thin이 들어가있으면 tcp를 사용하는 것이다.
SQLException발생가능해서 예외처리 해줘야함.
package ch20.sec05;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionExample {
public static void main(String[] args) {
Connection conn = null;
try {
// JDBC Driver를 메모리로 로딩하고 DriverMangaer에 등록
Class.forName("oracle.jdbc.OracleDriver");
//DB와 연결
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl", "java", "oracle");
System.out.println("연결 성공");
//DB작업
//~~
//DB 연결 끊기
//안전하게 끊기 위해 finally가서 끊기
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(conn != null) {
//연결이 null인경우? 연결이 안됫을 경우임. 이땐 close할필요가 없으니 이것을 넣어줘도됨
try {
//DB 연결 끊기
conn.close();
System.out.println("연결 끊김");
} catch (SQLException e) {}
}
}
/*
try {
// JDBC Driver를 메모리로 로딩하고 DriverMangaer에 등록
Class.forName("oracle.jdbc.OracleDriver");
try(conn = DriverManager.getConnection("jdbc:orcale:thin:@localhost:1521/orcl", "java", "oracle")) {
System.out.println("연결 성공");
//DB작업
//~~
} catch ..
이런식으로 자동 리소스닫기 구현되어서 이렇게 사용해도 된다.
} catch ...
*/
}}20.6 데이터 저장
JDBC를 이용해서 INSERT 문을 실행하는 방법을 알아보자.
users테이블에 새로 운 사용자 정볼르 저장하는 INSERT문은 다음과 같다.
테이블의 컬럼과 똑같은 순서를 할 필요는 없지만 가독성을 위해 가급적이면 같게 쓰자.
INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES ('winter', '한겨울', '12345', 25, 'winter@mycompany.com');
이 문을 작성하면 클라이언트가 아닌 데이터베이스 내부에서 실행을 해야한다.
이클립스가 아닌 데이터베이스 내부 Oracle SQL Developer에 쓰기
SQL문에서 문자열은 ' '로 감싸줌
이것을 실행한다고 바로 실행되는것은 아님
Oracle SQL Developer도 하나의 클라이언트일뿐임
이 SQL을 데이터베이스로 보내서 (서버로 보내서)안에서 실행한다는 것
영구적으로 들어가는게 아니라 데이터베이스 메모리에만 저장된거고 물리적인 파일에 저장된 것이아님.
commit;을 작성해줘야 서버에 영구적으로 저장이 되는 것임.
새로운 데이터를 저장했다 이를 서버의 파일 시스템에 영구적으로 저장해줘라는 것
위 메뉴에 커밋 버튼을 눌러도 된다.
INSERT DELETE UPDATE등 SQL문을 사용한 후에는 무조건 커밋을 해줘야한다.
SQL에서 실행한걸 되돌리려면 rollback;을 치거나 롤백버튼을 누르면된다.
20.6.1 user테이블에 추가하기
값을 ?(물음표)로 대체한 매개변수화된 INSERT문으로 변경하면 다음과 같다.
INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES (?, ?, ?, ?, ?);
그리고 INSERT문을 String 타입 변수 sql에 문자열로 대입한다.
String sql = new StringBuilder()
.append("INSERT INTO users (userid, username, userpassword, userage, useremail)")
.append("VALUES (?, ?, ?, ?, ?)")
.toString();
또는
String sql = "INSERT INTO users (userid, username, userpassword, userage, useremail)" + "VALUES (?, ?, ?, ?, ?)";
매개변수화 된 SQL문을 실행하려면 PreparedStatement가 필요하다. 다음과같이 Connection의 PreparedStatement()메소드로부터 PreparedStatement를 얻는다.
PreparedStatement pstmt = conn.PreparedStatement(sql);
그리고 ?에 들어갈 값을 지정해주는데 ?는 순서에 따라 1번부터 번호가 부여된다. 값의 타입에따라 Setter메소드를 선택한후 첫번째에는 ?순번, 두번째에는 값을 지정한다.
pstmt.setString(1, "winter");
pstmt.setString(2, "한겨울");
pstmt.setString(3, "12345");
pstmt.setInt(4, 25);
pstmt.setString(5, "winter@mycompany.com");
값을 저장한 후 executeUpdate()메소드를 호출하면 SQL문이 실행되면서 users테이블에 1개의 행이 저장된다.
executeUpdate()가 리턴하는 값은 저장된 행 수 인데 정상적으로 실행되었을 경우 1을 리턴한다.
int rows = pstmt.executeUpdate();
PreparedStatement가 더 이상 사용하지 않을 경우에는 close()메소드를 호출해서 PreparedStatement가 사용했던 메모리를 해제한다.
pstmt.close();
예제는 이전의 ConnectionExample을 가져와서 db작업 부분을 수정하였음.
java.sql안에 들어있는데 이것이 JDBC 프레임 워크다.
앞의 예제에 DB작업을 했다.
//DB작업
//매개변수화 된 SQL문 작성
String sql = "" + "INSERT INTO users (userid, username, userpassword, userage, useremail)" + "VALUES (?, ?, ?, ?, ?)";
//PreparedStatement 얻기 밑 값 지정 인터페이스임
PreparedStatement pstmt = conn.prepareStatement(sql);
//문자열 타입은 실제 테이블의 타입을 봐야함.
//데이터베이스 제약조건 보면 ID에 Primary_Key(기본키)가 되어있어서 같은 값을 못넣음.
//DB를 통해 배우는 것임 여기에선 주의하기
pstmt.setString(1, "fall");
pstmt.setString(2, "김단풍");
pstmt.setString(3, "12345");
pstmt.setInt(4, 27);
pstmt.setString(5, "fall@mycompany.com");
int rows = pstmt.executeUpdate();
System.out.println("저장된 행 수: " + rows);
pstmt.close();java에서 넣으면 오토 커밋되고 성공적으로 추가된것을 알 수 있다.
같은 것을 한번더 실행하면 무결성조건위배된다는 예외가 발생하는데
userid가 primary_key이기때문에 중복되면 안되는 것임.

20.6.2 boards테이블에 추가하기
SQL Developer에서 사용
INSERT INTO boards (bno, btitle, bcontent, bwriter, bdate, bfilename, bfiledata)
VALUES (SEQ_BNO.NEXTVAL, '눈오는 날', '함박눈이 내려요', 'winter', SYSDATE, 'snow.jpg', binaryData)";
SEQ_BNO.NEXTVAL 숫자를 줘도 되는데 시퀀스 SEQ_BNO에서 숫자 알아서 넣어줌 시퀀스는 oracle의 객체인데 순차적으로 번호를 넣어줌
일단 아무거나 넣었는데 왠만해선 userid를 넣음. 원래대로면 user테이블과 연결했을듯 일단은 아무값이나 넣음.
데이터베이스 NULLABLE 데이터가 없어도 된다. NULLABLE이 NO인 것은 모두 언급해줘야한다.
여기서는 bno, btitle, bcontent, bwriter컬럼들이 필수적으로 값이 있어야한다.
이를 매개변수화하면 밑에처럼 할 수 있다.
String sql = "insert into boards (bno, btitle, bcontent, bwriter, bdate, bfilename, bfiledata)" + "values (SEQ_BNO.NEXTVAL, ?, ?, ?, SYSDATE, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql, new String[]{"bno"});
이전 예제에서는 sql만 줬는데 두번째 매개변수로 String배열을 주었음.
이것은 결과값으로 가져올 컬럼을 가져와라는 뜻 new String[] colunmNames)
실제 열에 저장된 값을 알고 싶을 때 가져오는 것이고 colunmNames에는 {"열이름", "열이름"} 식으로 열이름의 배열이 들어갈 수 있다.
SEQ_BNO.NEXTVAL의 다음 번호가 뭔지 알려면 실제 저장된 값을 알아야한다.
시퀸스에서 자동으로 값을 가져올 경우 값을 알아야 다음 작업을 할 수 있는 일이 많다.
//SEQ_BNO.NEXTVAL,SYSDATE를 무시하고 ?만 순서를 보면된다.
pstmt.setString(1, "눈오는 날");
pstmt.setString(2, "함박눈이 내려요.");
pstmt.setString(3, "winter");
pstmt.setString(4, "snow.jpg");
pstmt.setBlob(5, new FileInputStream("src/ch20/oracle/sec06/snow.jpg"));
3번 데이터 타입은 CLOB인데 이것은 4기가까지의 문자열을 저장할수 있는 것이다.
VARCHAR2는 알파벳 4000자 한글 4000/3자
4번자리에는 파일명
5번자리에는 FileInputStream으로 파일의 내용을 읽고 넣기
5번은 데이터타입이 BLOB 바이너리 데이터를 넣을 수 있다는 것
SQL 문 실행
int rows = pstmt.executeUpdate();
if(rows == 1) {
ResultSet rs = pstmt.getGeneratedKeys(); //new String[]{"bno"}에 기술된 컬럼값을 가져옴
if(rs.next()) { //값이 있다면
int bno = rs.getInt(1); //new String[]{"bno"}에 첫번째 항목 bno 컬럼값을 읽음
System.out.println("저장된 bno: " + bno);
}
rs.close(); //ResultSet이 사용했던 메모리 해제}
정상적으로 저장되었으면 1이 리턴되니 정상적으로 저장되었을경우 실행된다.
ResultSet을 얻으면 커서가 테이블의 맨위에 있게됨 (필드일듯) 이부분은 데이터의 값이 없으니 일단대기함.
여기서 이 커서를 한칸 내리는 것을 next()메소드이다. 그러면 다음 행으로 내려가서 지정했던 bno값을 읽어줌
setBlob에서 그냥 null값주면 컴파일 에러뜸
setBlob(인덱스, InputStream)
setBlob(인덱스, Blob b)
두개가 오버라이딩되서 뭔지 몰라서 컴파일에러뜨는것
그래서 Blob blob = null; 블롭타입 변수 선언하고 넣어주면 정확하게 됨
//DB작업
//매개변수화 된 SQL문 작성
//,컬럼 구분자 꼭 넣어주기 마지막이랑 where에는 넣지 않는다. 실수를 방지하기 위해 한칸 띄어주기
String sql = "INSERT INTO boards (bno, btitle, bcontent, bwriter, bdate, bfilename, bfiledata) " + "VALUES (SEQ_BNO.NEXTVAL, ?, ?, ?, SYSDATE, ?, ?)";
//PreparedStatement 얻기 및 값 지정
PreparedStatement pstmt = conn.prepareStatement(sql, new String[] {"bno"});
pstmt.setString(1, "눈 오는 날3");
pstmt.setString(2, "함박눈이 내려요3");
pstmt.setString(3, "winter");
pstmt.setString(4, "photo2.png");
pstmt.setBlob(5, new FileInputStream("src/ch20/sec07/photo2.png"));
int rows = pstmt.executeUpdate();
System.out.println("저장한행 수: " + rows);
if(rows == 1) {
ResultSet rs = pstmt.getGeneratedKeys();
if(rs.next()){
int bno = rs.getInt(1);
System.out.println("저장된 bno: " + bno);
}
rs.close();
}
pstmt.close();
20.7 데이터 수정
JDBC를 이용해서 UPDATE문을 실행하는 방법을 알아보자.
책에서 파란색으로 된것 update set where 는 예약어임.
update boards set
btitle = '눈사람',
bcontent = '눈으로 만든 사람',
bfilename = 'snowman.jpg',
bfiledata=binaryData
where bno = 3
동시에 여려 행을 바꾸고 싶을때는 ,로 넣으면됨 ,는 컬럼 구분자이기때문에 맨마지막엔 넣지 않으니 주의하자
where는 조건문이고 만약 where가 없으면 값이 전부다 바뀌므로 주의하자
자바코드로 업데이트하기
바꾸고자 하는 값들을 매개변수로 ?를 주면됨
update boards set
btitle = ?,
bcontent = ?,
bfilename = ?,
bfiledata= ?
where bno = ?
String sql에 매개변수화된 UPDATE 문을 저장한다.
String sql = new StringBuilder()
.append("update boards set")
.append("btitle = ?, ")
.append("bcontent = ?, ")
.append("bfilename = ?, ")
.append("bfiledata= ? ")
.append("where bno = ? ")
.toString();
스트링빌더를 만들고 스트링타입에 대입하면 컴파일에러
.toString()을해서 문자열로 바꿔야함
String sql = "update boards set" + "bitile = ?, " + "bcontent = ?, " + "bfilename = ?, " + "bfiledata= ? "+ "where bno = ? "
//매개변수화된 UPDATE문을 실행하기 위해 preparStatment()메소드로부터 preparedStatement를 얻고 ?에 해당하는 값을 지정한다.
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "눈사람");
pstmt.setString(2, "눈으로 만든 사람");
pstmt.setString(3, "snowman.jpg'");
pstmt.setBlob(4, new FileInputStgream("파일경로 및 위치"));
pstmt.setInt(5, 3);
값을 모두 지정하였다면 UPDATE문을 실행하기 위해 executeUpdate()메소드를 호출한다.
성공적으로 실행되면 수정된 행의 수가 리턴된다. 0이 리턴되면 조건에 맞는 행이 없어 수정된 내용이 없음을 의미한다.
where의 조건에 따라 달라짐
즉 행이 없을 경우? 0이 리턴됨
>=7이런식으로 여러개를 고치면 2이상의 값이 나올수도 있음
//DB작업
//매개변수화 된 SQL문 작성
String sql = new StringBuilder()
.append("update boards set")
.append("btitle = ?, ")
.append("bcontent = ?, ")
.append("bfilename = ?, ")
.append("bfiledata= ? ")
.append("where bno = ? ")
.toString();
//PreparedStatement 얻기 및 값 지정
//PreparedStatement pstmt = conn.prepareStatement(sql);
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "비가 내려요");
pstmt.setString(2, "겨울비는 추워요");
pstmt.setString(3, "photo2.png");
pstmt.setBlob(4, new FileInputStream("src/ch20/sec06/photo2.png"));
pstmt.setInt(5, 6);
int rows = pstmt.executeUpdate();
System.out.println("업데이트된 행 수: " + rows);
pstmt.close();20.8 데이터 삭제
DELETE문을 실행해서 삭제하자
delete from boards where bwriter = 'winter'
삭제대상은 행임 컬럼하나의 값만 지울 수는 없다.
where가 빠지면 전체 행이 날아가버리니 주의하자.
자바에서 실행하려면 매개변수를 ?로 하면됨
delete from boards where bwriter = ?
또 String타입 sql변수에 대입
String sql = "delete from boards where bwriter = ?";
매개변수화된 UPDATE문을 실행하기 위해 preparStatment()메소드로부터 preparedStatement를 얻고 ?에 해당하는 값을 지정
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "winter");
int rows = pstmt.executeUpdate();
실제 삭제된 행수
//DB작업
//매개변수화 된 SQL문 작성
String sql = "delete from boards where bwriter = ?";
//PreparedStatement 얻기 및 값 지정
//PreparedStatement pstmt = conn.prepareStatement(sql);
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "winter");
int rows = pstmt.executeUpdate();
System.out.println("삭제된 행 수: " + rows);
pstmt.close();2022.12.12
실행하다가 연결성공만하고 수정이안됨.
데이터베이스에서 수정하던거 커밋 안눌러놔서 수정중이어서 lock이 걸림
데이터베이스에서 커밋이나 롤백을 해야 다른곳에서 lock이 풀려서 수정가능함.
'기초단계 > JAVA' 카테고리의 다른 글
| 2022.12.13-2 JAVA 데이터베이스 입출력 (0) | 2022.12.13 |
|---|---|
| 2022.12.13 JAVA 데이터베이스 입출력 (0) | 2022.12.13 |
| 2022.12.10 JAVA 네트워크 입출력 (0) | 2022.12.10 |
| 2022.12.09 JAVA 네트워크 입출력 (0) | 2022.12.10 |
| 2022.12.07 JAVA 데이터 입출력 (1) | 2022.12.07 |