JDBC 개요
- JDBC ( Java Database Connectivity ) 라이브러리 ( java.sql.패키지 )를 제공 → 데이터베이스 ( DB )와 연결해서 데이터 입출력 작업
· JDBC는 데이터베이스 관리시스템 ( DBMS )의 종류와 상관없이 동일하게 사용할 수 있는 클래스와 인터페이스로 구성
- JDBC에 포함되어 있는 클래스와 인터페이스들의 연관관계
· JDBC Driver
: JDBC 인터페이스를 구현한 것으로, JDBC 인터페이스를 통해 실제로 DB와의 작업을 함
DBMS마다 별도로 다운로드 받아 사용해야 함 (이전 게시물에 자세히 작성함)
① DriverManager
: DriverManager 클래스는 JDBC Driver를 관리하며 DB와 연결해서 Connection 구현 객체 생성
② Connection
: Connection 인터페이스는 Statement, PreparedStatement, CallableStatement 구현 객체를 생성
트랜잭션( transation ) 처리 및 DB 연결을 끊을 때 사용
③ Statement
: Statement 인터페이스는 SQL의 DDL과 DML을 실행할 때 사용
→ 주로 변경되지 않는 정적 SQL문을 실행할 때 사용
④ PreparedStatement
: PreparedStatement는 Statement와 동일하게 SQL의 DDL, DML문을 실행할 때 사용
→ Statement과의 차이점 : 매개변수화된 SQL문을 사용할 수 있으므로 편리성과 보안성이 좋음
⑤ CallableStatement
: CallableStatement는 DB에 저장되어 있는 프로시저와 함수를 호출할 때 사용
DB 구성 - 학습용 DB 및 Table 생성
▶ DB 생성
① Database - 오른쪽 클릭 - [ Create New Database ] - Name 입력란에 데이스베이스명 입력 후 OK
② 성공적으로 DB가 생성되면 해당 데이터베이스를 선택 후 오른쪽 클릭 - [ Set as default ] 클릭 → localhost 계정의 기본 DB로 설정
▶ Table 생성
- 새 SQL 편집기를 열어 테이블 생성 코드를 작성
① 사용자 정보가 저장될 users 테이블 생성
② 게시물 정보가 저장될 boards 테이블 생성 - UI 방식
- 해당 데이터베이스에서 오른쪽 클릭 - [ Create ] - [ Table ] → 테이블 생성 창 뜸 ( 테이블명 작성 및 하단 컬럼모양 아이콘 클릭하여 컬럼명 및 속성 설정 )
· Engine : InnoDB ( Transaction-safe 한 Storage Engine을 의미 )
· Auto Increment : 0 ( 자동 숫자 증가 : 0부터 시작하라는 의미 )
- 아래 사진과 같이 컬럼을 다 추가시킨 후, 기본키 설정하기
기본키로 정할 컬럼명에서 오른쪽 클릭 - [ New Constraint from Selection ] - [ Create constraint for table "boards" ] 창이 뜸
→ 여기서 기본키로 설정할 컬럼명을 선택한 후 OK버튼 클릭
- 하단의 디스크 모양의 저장버튼 클릭 시, [ Persist Changes ] 창이 뜸 → UI로 작성한 테이블의 컬럼이 SQL문으로 작성된 것을 확인 가능 → 확인 후, 하단의 [ Persist ] 클릭
③ 계좌 정보가 저장될 accounts 테이블 생성
- 테이블 생성 후, Insert문을 통해 데이터 추가
④ 해당 데이터베이스에서 오른쪽 클릭 - [ Refresh ] → Database Navigator 에서 생성한 테이블 확인 가능
DB 연결
- 클라이언트 프로그램에서 DB와 연결하려면 해당 DBMS의 JDBC Driver 필요 및 다음 4가지의 정보가 필요
① DBMS가 설치된 컴퓨터의 IP 주소 → 해당 컴퓨터를 찾아가기 위해
② DBMS가 허용하는 포트(Port) 번호 → DBMS로 연결하기 위해
③ 사용자(DB 계정) 및 비밀번호 → 어떤 사용자인지 인증받기 위한 계정 및 비밀번호 필요
④ 사용하고자 하는 DB 이름 → DBMS는 여러 개의 DB를 관리하므로 실제로 사용할 DB 이름 필요
▶ JDBC Driver 설치
(이전 게시물에 자세히 작성)
▶ DB 연결
① 클라이언트 프로그램을 DB와 연결하기 위해 가장 먼저 해야할 작업 : JDBC Driver를 메모리에 로딩하기
Class.forName( ) 메서드 : 문자열로 주어진 JDBC Driver 클래스를 Build Path에서 찾은 후, 메모리에 로딩함
String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
Class.forName(JDBC_DRIVER);
→ JDBC Driver 클래스의 static 블록이 실행되면서 DriverManager에 JDBC Driver 객체를 등록하게 됨
만약 Build Path에서 JDBC Driver 클래스를 찾지 못한다면, ClassNotFoundException이 발생하므로 예외처리 필수!
② DriverManager에 JDBC Driver가 등록되면 getConnection( ) 메서드로 DB와 연결 가능
String JDBC_URL = "jdbc:mariadb://localhost:3306/(사용할 DB명)";
IP주소 포트(기본 포트일 경우, 생략 가능)
// localhost는 로컬에 설치된 MariaDB에 연결하겠다는 의미
원격 MariaDB에 연결하려면 IP주소로 기술해야 함
String USER = "root";
String PASSWORD = "mariadb";
Connection conn = DriverManager.getConnection("연결 문자열", "사용자", "비밀번호");
→ 연결에 성공하면 getConnection( ) 메서드는 Connection 객체를 리턴함
만약 연결이 실패하면 SQLException이 발생하므로 예외처리 필수!
▶ ConnectionExample 클래스
package mariadb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionExampleApp2 {
public static void main(String[] args) {
// 멤버변수
Connection conn = null; // 로컬변수는 반드시 초기화해주어야 함
// 멤버상수
String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
String JDBC_URL = "jdbc:mariadb://localhost:3306/thisisjava";
String USER = "root";
String PASSWORD = "mariadb";
// JDBC_DRIVER 가져오기
try {
// JDBC_DRIVER 등록
Class.forName(JDBC_DRIVER);
System.out.println("JDBC_DRIVER 가져오기 성공!"); // 로그
// 연결
conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
System.out.println("MariaDB에 연결 성공!");
} catch(ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
} finally {
// 연결 끊기 -> Connection 객체 닫기
try {
if( conn != null)
conn.close();
} catch (SQLException e) { }
}
}
}
→ 멤버 변수 ( 클래스 변수와 인스턴스 변수 )와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적!
→ 메서드 선언부에 키워드 throws를 통해 예외를 선언함으로써 메서드를 사용하려는 사람이 메서드의 선언부를 보았을 때, 이 메서드를 사용하기 위해서는 어떠한 예외들이 처리되어져야 하는지 쉽게 알 수 있음
∴ 이 메서드 사용 시, 해당 예외에 대한 처리를 해주어야 함 → try - catch 구문 이용
데이터 저장
데이터를 저장하기 전에, Statement와 PreparedStatement 객체의 차이점에 대해 알아보자.
① Statement
: Statement 인터페이스는 SQL의 DDL과 DML을 실행할 때 사용
→ 주로 변경되지 않는 정적 SQL문을 실행할 때 사용
INSERT INTO users
( userid, username, userpassword, userage, useremail )
VALUES
( 'winter', '한겨울', '12345', 25, 'winter@company.com' );
② PreparedStatement
: PreparedStatement는 Statement와 동일하게 SQL의 DDL, DML문을 실행할 때 사용
→ Statement과의 차이점 : 매개변수화된 SQL문을 사용할 수 있으므로 편리성과 보안성이 좋음
INSERT INTO users
( userid, username, userpassword, userage, useremail )
VALUES
( ?, ?, ?, ?, ? );
// 값을 ?( 물음표 )로 대체한 매개변수화된 INSERT문으로 변경하면 됨
▶ users 테이블 : JDBC를 이용해서 INSERT문을 통해 새로운 사용자 정보를 저장해보자.
package mariadb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UserInsertExampleApp {
public static void main(String[] args) {
// 멤버변수
Connection conn = null;
// 멤버상수
String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
String JDBC_URL ="jdbc:mariadb://localhost:3306/thisisjava";
String USER = "root";
String PASSWORD = "mariadb";
try {
// JDBC_DRIVER 등록
Class.forName(JDBC_DRIVER);
System.out.println("DRIVER 로딩 성공!");
// 연결
conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
System.out.println("MariaDB 연결 성공!");
// 실제 JDBC를 사용하여 쿼리를 처리할 때는 PreparedStatement를 사용
String sql = """
INSERT INTO users
( userid, username, userpassword, userage, useremail )
VALUES
( ?, ?, ?, ?, ? ); """;
// PreparedStatement 명령 객체 생성 및 값 지정
// ① sql문을 미리 준비해서 객체 생성 시, sql문을 매개변수로 지정
PreparedStatement pstmt = conn.prepareStatement(sql);
// ② 실행할 쿼리에 필요한 값 지정
pstmt.setString(1, "winter");
pstmt.setString(2, "한겨울");
pstmt.setString(3, "12345");
pstmt.setInt(4, 25);
pstmt.setString(5, "winter@mycompany.com");
// SQL문 실행 (명령객체 생성 시 sql를 매개변수로 지정했으므로 executeUpdate()에는 지정 안함)
int insertCount = pstmt.executeUpdate();
System.out.println("저장된 행 수 : " + insertCount);
// 반드시 명령객체(실행객체)를 닫아야 함
// -> pstmt가 try블럭에서 선언되었으므로 finally 블럭에서 닫을 수 없음
pstmt.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
// 연결끊기
if(conn != null)
conn.close();
} catch (SQLException e) { }
}
}
}
① PreparedStatement의 경우, Statement와 달리 Connection의 prepareStatement( ) 메서드에 매개변수로 미리 준비한 sql문이 들어감 → SQL문 실행 즉, executeUpdate( ) 메서드에는 매개변수로 sql를 지정하지 않음
② 매개변수화된 sql문의 ?에 들어갈 값을 지정함
즉, setString( ) 메서드의 선언부를 찾아보면 다음과 같음
첫번째 매개변수가 parameterIndex값이므로 ?는 순서에 따라 1번부터 번호 부여
∴ 값의 타입에 따라 Setter메서드를 선택한 후, 매개변수 첫번째에는 ?의 순번을, 두번째에는 값을 지정
③ executeUpdate( ) 메서드의 경우, 리턴하는 값이 저장된 행 수이므로 정상적으로 실행될 경우 1을 리턴함
→ 총 3개의 정보를 추가해보았고,
그 결과는 Database Navigator 창 - 테이블명(users)에서 오른쪽 클릭 - [ 데이터 수정 ] - 상단 [ Data ] 클릭해서 확인
▶ boards 테이블 : 게시물 정보를 저장해보자.
① 매개변수화된 INSERT문을 사용하여 PreparedStatement 객체 이용
String sql = "" +
"INSERT INTO boards ( " +
" btitle, bcontent, bwriter, bdate, bfilename, bfiledata )" +
"VALUES ( ?, ?, ?, now(), ?, ? ) ; " ;
❶ bno의 경우, 'auto_increment'로 자동 증가 컬럼이므로 생략됨
❷ bdate의 경우, default 값이므로 ?를 지정할 필요 없음 → now( ) 메서드로 현재 시간을 나타냄
② 매개변수화된 INSERT문을 실행하기 위해 PreparedStatement 객체를 얻는데, 이전과는 다르게 두 번째 매개값이 존재함
PreparedStatement pstmt = conn.prepareStatement( sql, Statement.RETURN_GENERATED_KEYS);
- 두번째 매개변수인 Statement.RETURN_GENERATED_KEYS
· INSERT문이 실행된 후 가져올 키 값으로, 자동 증가된 bno값을 가져옴
· SQL문이 실행되기 전까지는 bno 값을 모르기 때문에 SQL문이 실행된 후에 bno 컬럼에 실제로 저장된 값을 얻어보는 것
③ 매개변수화된 sql문의 ?에 들어갈 값을 지정함
pstmt.setString(1, "눈 오는 날");
pstmt.setString(2, "함박눈이 내려요.");
pstmt.setString(3, "winter");
pstmt.setString(4,"snow.jpg");
pstmt.setBlob( 5, new FileInputStream("src/mariadb/images/snow.jpg"));
- bfilename 컬럼은 바이너리 타입( blob )
→ ?에 값을 지정하기 위해서는 setBinaryStream( ), setBlob( ), setByte( ) 메서드 중 하나를 이용해야 함
setBlob( ) 메서드를 이용해서 바이트 입력 스트림을 제공함 → setBlob( ) 메서드의 B는 'Binary'를 의미함
· 파일의 종류
❶ 바이너리 파일 : .jpg, .png와 같은 그림 파일 / .mp3와 같은 음악 파일 / .exe와 같은 실행 파일 등이 해당함
- 사용자 또는 프로그램이 사용하던 정보나 숫자 값을 특별한 가공 없이 그대로 파일에 저장
- 바이너리 파일은 데이터를 읽거나 쓸 때 파일 구성 형식에 특별한 조건이 없기 때문에 대부분 데이터의 크기로 판단
→ '20바이트를 읽어라'와 같은 명령 사용
∴ 이런 특징으로 인해 파일의 내용을 확인하기 위해서는 해당 파일을 볼 수 있는 프로그램이 별도로 존재해야 함
❷ 텍스트 파일 : 문자를 기반으로 하는 코드 값이 저장된 파일
- 프로그램이 이 파일의 데이터를 읽거나 쓸 때는 포맷 형식에 따라 데이터의 변환이 일어남
· FileInputStream(매개변수로 파일의 위치가 들어감)
※ 파일의 위치는 항상 현재 사용하고 있는 프로젝트를 기준으로!
- 파일을 열었을 때, 데이터를 순차적으로 읽어온다는 의미 ( Input를 read로 해석 )
- 자바는 모든 데이터를 Stream으로 취급 → 모든 데이터가 한 줄로 늘어서있음
( InputStream에 대해서는 다음시간에 더 자세히 다룰 예정 )
- 파일을 해당 경로에 저장한 후, Package Explorer 창에서 [ Refresh ] 클릭 시, 아래와 같이 images 폴더에 이미지가 업로드됨
④ INSERT문 실행 후, 저장된 bno 값 얻기
// SQL문 실행
int rows = pstmt.executeUpdate();
System.out.println("현재 DB에 저장된 행 수 : " + rows);
// SQL문 실행 후, 저장된 bno 값 얻기
if ( rows == 1 ) {
ResultSet rs = pstmt.getGeneratedKeys();
// 만약 값이 있다면
if(rs.next()) {
int bno = rs.getInt(1);
System.out.println("저장된 bno : " + bno);
rs.close();
}
❶ 게시물의 정보가 저장되었을 경우 ( = executeUpdate( ) 메서드에 의해 저장된 행 수가 1 ),
getGeneratedKeys( ) 메서드에 의해 반환타입으로 ResultSet를 얻음
즉, 마지막에 추가된 primary key인 bno의 컬럼값을 가져옴
❷ 만약 값이 있다면, if문의 블럭 실행
- boards 테이블에서 첫번째 컬럼인 bno의 컬럼 인덱스 번호를 변수 bno에 저장
- 저장된 bno 값을 출력하면 현재 boards 테이블에 저장된 bno 값을 알 수 있음
package mariadb;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class BoardInsertExampleApp {
public static void main(String[] args) {
Connection conn = null;
String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
String JDBC_URL = "jdbc:mariadb://localhost:3306/thisisjava";
String USER = "root";
String PASSWORD = "mariadb";
try {
Class.forName(JDBC_DRIVER);
System.out.println("DRIVER 불러오기 성공!");
conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
System.out.println("MariaDB 연결 성공!");
// 여기서부터 추가 질의
String sql = "" +
"INSERT INTO boards ( " +
" btitle, bcontent, bwriter, bdate, bfilename, bfiledata )" +
"VALUES ( ?, ?, ?, now(), ?, ? );";
// preparedStatement 얻기 및 값 지정
PreparedStatement pstmt = conn.prepareStatement(
sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, "눈 오는 날");
pstmt.setString(2, "함박눈이 내려요.");
pstmt.setString(3, "winter");
pstmt.setString(4,"snow.jpg");
pstmt.setBlob(
5, new FileInputStream("src/mariadb/images/snow.jpg"));
// SQL문 실행
int rows = pstmt.executeUpdate();
System.out.println("현재 DB에 저장된 행 수 : " + rows);
// 마지막 추가된 레코드의 마지막 primary key 일련번호 ( = bno )
if ( rows == 1 ) {
// 현재 DB에 저장된 행 수가 1이므로, 1로 설정함
ResultSet rs = pstmt.getGeneratedKeys();
// 가장 최근에 추가된 id값을 리턴해줌
if(rs.next()) {
int bno = rs.getInt(1);
System.out.println("저장된 bno : " + bno);
rs.close();
}
}
// PreparedStatement 닫기
pstmt.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
// 연결 끊기
if ( conn != null ) {
conn.close();
}
} catch(SQLException e) {}
}
}
}
▷ 출력 결과
+ 추가로 데이터를 저장한 후, 출력 결과
'백엔드 > JAVA' 카테고리의 다른 글
게시판 구현 (1) (0) | 2023.06.02 |
---|---|
데이터 입출력 (2) (0) | 2023.05.31 |
JDBC 드라이버 다운로드 및 등록 (0) | 2023.05.23 |
H2 데이터베이스 연결 (2) (0) | 2023.05.22 |
H2 데이터베이스 연결 (1) (0) | 2023.05.22 |