2023.05.31
* crud의 read를 배울 것
* 파일을 순서대로 읽는다 fileinputstream (=read)
- 자바에서 파일은 1바이트 단위로 읽음
* 바이너리파일 : 바이트 단위를 읽기 위함
* 텍스트파일 : 글자를 읽기 위함
* src : 소스코드가 있는 메인 루트(경로)
* .toString(); 을 마지막에 붙이면 문자로 변경
* uri : 통합 자원 식별자
url(주소 고정)보다 큰 개념 - 포함관계
모든 서버를 기준으로 위치
* 정규화 될 수록 느려짐
중복을 최소화함 => 관련된 테이블 수가 늘어남
이론
1. 데이터 수정
- String sql에 매개변수화된 UPDATE 문을 저장
String sql = new StringBuilder()
.append("UPDATE boards SET ")
.append("btitle= ?, ")
.append("bcontent= ?, ")
.append("bfilename= ?, ")
.append("bfiledata= ? ")
.append("WHERE bno= ?")
.toString();
- 매개변수화된 UPDATE 문을 실행하기 위해 prepareStatement() 메소드로부터 PreparedStatement를 얻고, ?에 해당하는 값을 지정
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "눈사람");
pstmt.setString(2, "눈으로 만든 사람");
pstmt.setString(3, "snowman.jpg");
pstmt.setBlob(4, new FileInputStream("src/ch20/mysql/sec07/snowman.jpg"));
pstmt.setInt(5, 3); // 3번째 게시물 변경
- 값을 모두 지정하였다면 UPDATE 문을 실행하기 위해 executeUpdate() 메소드를 호출
int rows = pstmt.executeUpdate();
2. 데이터 삭제
- 매개변수화된 DELETE 문을 String 타입 변수 sql에 대입
// boards 테이블에서 bwriter가 ?인 모든 게시물을 삭제
String sql = "DELETE FROM boards WHERE bwriter= ?";
- 매개변수화된 DELETE 문을 실행하기 위해 prepareStatement ( ) 메소드로부터 PreparedStatement를 얻고 ?에 값을 지정한 후, executeUpdate로 SQL 문을 실행
String sql = "DELETE FROM boards WHERE bwriter= ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "winter"); // 작성자가 winter인 모든 게시물 삭제
int rows = pstmt.executeUpdate();
3. 데이터 읽기
PreparedStatement를 생성할 때 SQL 문이 INSERT, UPDATE, DELETE일 경우에는 executeUpdate() 메소드를 호출
데이터를 가져오는 SELECT 문일 경우에는 executeQuery() 메소드를 호출
executeQuery() 메소드는 가져온 데이터를 ResultSet에 저장하고 리턴
* ResultSet의 특징
커서cursor(행을 가리키는 포인터)가 있는 행의 데이터만 읽을 수 있다.
ResultSet은 실제 가져온 데이터 행의 앞과 뒤에 beforeFirst(최초 커서) 행과 afterLast 행이 붙어서
첫 번째 데이터 행을 읽으려면 next()를 활용하여 커서를 이동시켜야 한다.
next() : 커서를 다음 행으로 이동
3-1. 데이터 행 읽기
- 커서가 있는 데이터 행에서 각 컬럼의 값은 Getter 메소드로 읽을 수 있다.
- 컬럼의 데이터 타입에 따라서 getXxx() 메소드가 사용되며, 매개값으로 컬럼의 이름 또는 컬럼 순번을 줄 수 있다.
- ResultSet에서 컬럼 순번은 1부터 시작
// 컬럼 이름
String userId = rs.getString("userid");
String userName = rs.getString("username");
int userAge = rs.getInt("userage");
// 컬럼 순번
String userId = rs.getString(1);
String userName = rs.getString(2);
int userAge = rs.getInt(3);
@Data 어노테이션을 사용하기 위해 롬복 설치하기
2023.06.01 - [백엔드/자바] - 자바 - 24 (lombok설치하기)
3-2. 사용자 정보 읽기
▷ users 테이블의 한 개의 행(사용자)을 저장할 User 클래스 작성
- @Data 어노테이션을 이용해서 Getter, Setter, toString() 메소드를 자동 생성
- 어노테이션 : 특수한 기능을 실행하는 자바의 실행주석
package mariadb;
import lombok.Data;
@Data // 롬복 라이브러리에의해 생성자 setter/getter메소드가 만들어짐
public class User {
// 사실상 DTO
private String userId;
private String userName;
private String userPassword;
private int userAge;
private String userEmail;
}
※ 외부에서 접근할 수 있는 속성함수(getter/setter)작성
1. 직접 입력
2. 이클립스의 자동완성(sourse/generate...getter|setter) 메뉴 이용
3. 롬복(lombok)이라는 개발 지원 툴 라이브러리를 이용
- 프로젝트 개발 실행시 자동생성
- @Data : 롬복 라이브러리에의해 Constructor(기본생성자), Getter, Setter, hashCode(), equals(), toString() 자동 생성
- 소스코드에서는 안보이지만 실제로 컴파일된 결과물(.class)에는 코드가 생성되어 있음
▷ 테이블에서 userid가 winter인 사용자의 정보를 가져와 출력하기
package mariadb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserSelectExampleApp {
public static void main(String[] args) {
Connection conn = null;
String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
String JDBC_URL = "jdbc:mariadb://localhost/thisisjava"; // 기본포트번호는 생략 가능
String USER = "root";
String PASSWORD = "mariadb";
try {
// 드라이버 찾기
Class.forName(JDBC_DRIVER);
// 마리아디비와 연결
conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
String sql = "" +
"SELECT userid, username, userpassword, " +
" userage, useremail " +
"FROM users " +
"WHERE userid = ?; ";
// preparedStatement 얻기 및 값인수 지정
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "winter");
// SQL 실행 후 결과 얻어오기(ResultSet 반환)
ResultSet rs = pstmt.executeQuery();
// 한개의 레코드만 가져왔다고 가정
if (rs.next()) { // 결과 데이터(사용자 아이디) 있음
// DTO 클래스 사용
User user = new User();
user.setUserId( rs.getString("userid") ); // 컬럼 이름으로 읽기
user.setUserName( rs.getString("username") );
user.setUserPassword( rs.getString("userpassword") );
user.setUserAge( rs.getInt(4) ); // 컬럼순번 이용
user.setUserEmail( rs.getString(5) );
System.out.println( user );
} else { // 결과 데이터가 없음
System.out.println("사용자 아이디가 존재하지 않습니다.");
}
} catch(ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
if ( conn != null ) {
// 연결 끊기
conn.close();
}
} catch(Exception e) {
}
}
}
}
- Date는 import util로 하기
3-3 게시물정보읽기
▷ boards 테이블에 bwriter를 winter로 하는 게시물을 1개 저장할 Board 클래스(DTO)
- 컬럼 개수와 타입에 맞게 필드 선언
- Data 어노테이션 활용
package mariadb;
import java.sql.Blob;
import java.util.Date; // 유틸임
import lombok.Data;
// DTO / VO 클래스
// Data 클래스
@Data
public class Board {
private int bno;
private String btitle;
private String bcontent;
private String bwriter;
private Date bdate;
private String bfilename;
private Blob bfiledata;
}
▷ boards 테이블에서 bwriter가 winter인 게시물 정보를 가져오는 전체 코드
package mariadb;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class BoardSelectExampleApp {
public static void main(String[] args) {
Connection conn = null;
String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
String JDBC_URL = "jdbc:mariadb://localhost/thisisjava";
String USER = "root";
String PASSWORD = "mariadb";
try {
Class.forName(JDBC_DRIVER);
conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
// 마리아디비서버에 질의할 SELECT SQL 정의
String sql = "" +
"SELECT bno, btitle, bcontent, bwriter, bdate, bfilename, bfiledata " +
"FROM boards " +
"WHERE bwriter = ?; ";
// 명령객체 생성 및 쿼리
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "winter");
// 쿼리 실행 및 결과 얻기
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
// 데이터행을 읽고 각 컬럼값을 대입하여 DTO board 객체 생성
Board board = new Board();
board.setBno(rs.getInt("bno")); // set : 데이터 넣기
board.setBtitle(rs.getString("btitle"));
board.setBcontent(rs.getString("bcontent"));
board.setBwriter(rs.getString("bwriter"));
board.setBdate(rs.getDate("bdate"));
board.setBfilename(rs.getString("bfilename"));
board.setBfiledata(rs.getBlob("bfiledata")); // 파일은 2진데이터
// 콘솔 출력
System.out.println(board);
// board 객체의 bfiledata를 파일로 저장
// board DTO객체의 Blob타입 bfiledata의 참조주소를 blob변수에 저장
// 참조라는 것은 원본데이터를 직접 접근할 수 있다
// 새로운 변수를 만들면 접근경로가 짧아 접근이 쉬워짐(안하면 .appends와 같이 활용해야함)
Blob blob = board.getBfiledata();
if(blob != null) {
// getBinaryStream()은 blob객체로부터 읽어온다(=read) = 입력한다(=input)
InputStream is = blob.getBinaryStream(); // 운영체제(JVM)에 도움을 받아 파일로 저장됨
OutputStream os = new FileOutputStream( // 업캐스팅, 1바이트씩 넘겨주지만 buffer됨
"c:/Temp/" + board.getBfilename()); // 저장될 경로
is.transferTo(os); // 인풋에서 아웃풋으로 흘러나간다
os.flush(); // 저장공간이 남아있어도 강제저장
os.close();
is.close();
}
rs.close();
pstmt.close();
}
} catch(Exception e) { // 일일히 다 적어주지 않고 Exception로 일괄해도 됨
e.printStackTrace();
} finally {
if( conn != null ) {
try {
conn.close();
} catch (Exception e) {
}
}
}
}
}
* Blob 바이너리 타입
메모리에 2~4기가 정도 확보
Blob blob = board.getBfiledata();
-> board의 blob 객체를 다이렉트로 가르키기 위함
blob 힙(메모리)의 주소가 저장됨
* FileOutputStream : 이미지의 데이터사이즈만큼 읽어서 파일로 저장
들어오는 입구가 메모리, 나가는 출력 흐름이 파일임
* FileInputStream
들어오는 입구가 파일, 나가는 출력 흐름이 이미지임
* 메모리와 아웃풋 연결
is.transferTo(os);
▷ boards 테이블에 bwriter를 winter로 하는 게시물을 2개 이상 저장
- Borad : Data 클래스, boards 테이블의 1개 행(게시물)을 저장할 클래스
- BoardSelectExampleApp : boards 테이블에서 bwriter가 winter인 게시물 정보를 가져오는 전체 코드
1. BoardInsertExampleApp 여러번 실행
- sql 편집기 열어서 확인
2. sql 편집기 열기 > 원하는 테이블 위에서 오른쪽 마우스 > SQL 생성 > INSERT문 복사 > 자바(sts)에서 sql명령문 작성하기
● Stream
- 시작(입력방향)과 끝(출력방향)이 있는 순차적 흐름
- 스트림은 다른스트림의 입력(출력)스트림이 될 수 있다
* InputStream
바이트단위로 처리(느림)
나를 중심으로 내 쪽으로 화살표
Buffer에 담아 처리
Buffer : 일정공간을 채워 한번에 처리
파일로 데이터를 읽는 작업은 오래걸리므로 Buffer 활용
* OutputStream
바이트단위로 처리(느림)
나를 중심으로 내 바깥 쪽으로 화살표
인풋 : 읽어라
아웃풋 : 저장해라
* DB에 있는 메모리를 영구저장을 하기 위해서는 파일로 만들어야함
* Blob (Binary Large Object)
- 바이너리 형태로 큰 객체를 저장할 것이라는 것
- 큰 객체라는 것은 주로 이미지, 비디오, 사운드 등과 같은 멀티미디어 객체
- Blob 객체는 파일류의 불변하는 미가공 데이터.
- 텍스트와 이진 데이터의 형태로 읽을 수 있으며, ReadableStream으로 변환한 후 스트림 메서드를 사용해 데이터를 처리할 수도 있다
- blob의 경우 4GB의 이진 데이터를 저장할 수 있다. 하지만 이건 DB에 직접 저장하는 것이 아니라 DB에는 Large Object의 위치 포인터만 저장
▶ 폴더 없을 시 폴더 생성
String folderPath = "c:/temp/";
String filepath = folderPath + board.getBfilename();
File folder = new File(folderPath);
if(!folder.exists()) {
try {
folder.mkdir(); // 폴더를 생성
System.out.println("폴더를 생성했습니다.");
} catch(Exception e) {
e.printStackTrace();
}
}
'백엔드 > 자바' 카테고리의 다른 글
자바 - 26 (Stream) (0) | 2023.06.02 |
---|---|
자바 - 25 (트랜잭션) (0) | 2023.06.02 |
자바 - 24 (lombok설치하기) (0) | 2023.06.01 |
자바 - 23 (0) | 2023.06.01 |
자바 - 22 (List - 리스트) (0) | 2023.05.31 |