* 이 포스팅은 kyun2.tistory.com/ 님과 cocodo.tistory.com/ 님의 포스팅을 학습한 뒤 추가로 공부한 후 재정리한 글입니다.
* 2차 가공 및 재배포를 금지합니다.
* 오탈자 및 잘못된 내용은 댓글달아주세요.
* 부분별 소스코드를 잘못 작성하여 새롭게 포스팅했습니다. 아래 포스팅은 개념 참고만 하되, 소스코드는 아래의 링크에서 확인해주세요.
>> 소스코드 최종
* SQL 전송에 대해 알아보기 전 JDBC 동작 순서에 대해 먼저 살펴보겠습니다.
1. JDBC
- Java Database Connectivty
- 자바에서 제공하는 데이터베이스와 연결하여 데이터를 주고받을 수 있도록 하는 인터페이스
- 자바 언어로 다양한 종류의 관계형 데이터베이스에 접속하고 SQL문을 수행하여 처리하고자 할 때 데이터베이스를 조작하는 표준 SQL 인터페이스 API
- java.sql 패키지에 위치 ( JDBC 프로그래밍을 할 때 java.sql의 인터페이스로 작업함 )
- JDBC 드라이버 : java.sql의 인터페이스들을 상속하여 메소드의 몸체를 구현한 클래스 파일
- 단점 : DB에서 정보를 가져올 때 마다 DB Connection, Disconnection을 해야하고 서버 과부하, 속도 저하의 문제가 있음
- 이러한 단점을 극복하기 위해 JNDI를 사용
1-1. JDBC 사용법
- Class.forName() : 드라이버를 로드한다.
- DriverManager : 로드된 드라이버를 통해서 Connection을 활성화해주는 객체
- Connection : 데이터베이스와의 연결
- Statement : SQL을 실행하는 객체
- ResultSet : SQL문 실행 후 데이터를 받는 객체
2. 드라이버 및 데이터베이스 접속 연결 소스코드 - private void connect()
package ex1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
public class TstJDBC {
private String ip = "해당 IP";
private int port = 해당포트;
private String database = "해당 데이터베이스 이름";
private String user = "DB 사용자 이름";
private String password = "DB 사용자 비밀번호";
private final String DRIVER_NAME = "DB DRIVER NAME";
private final String JDBC_URL = "jdbc:해당 DB:thin:@" + ip + ":" + port + ":" + database;
private Connection conn = null;
private Statement stmt = null;
// 드라이버 및 데이터베이스 접속
private void connect() {
try {
Class.forName(DRIVER_NAME);
System.out.println("드라이버 로드 성공");
} catch (ClassNotFoundException e) {
System.err.println("Class Not Found");
}
try {
conn = DriverManager.getConnection(JDBC_URL, user, password);
System.out.println("데이터베이스 접속 성공");
} catch (SQLException e) {
System.err.println("DB Connection Error");
}
}
public static void main(String[] args) {
TstJDBC test = new TstJDBC();
test.connect();
}
}
* 출처 포스팅과 소스코드가 다름을 밝힙니다.
* 학습을 위해 connect를 분리했습니다.
* 소스코드에서 계정 정보를 하드코딩하는 것은 절대로 피해야하는 위험한 방법이지만 예시이기 때문에 하드코딩을 해서 작성했습니다.
2-1. Class.forName() 동작 원리
- 의문1 : Class.forName() 메소드에 Driver 클래스 위치를 넘겨줄 뿐 리턴값을 받는다던지 하는 아무런 동작이 없음
- 의문2 : 후에 DriverManager.getConnection()을 하면 해당 연결객체를 넘겨주는 것의 이유
- 의문1의 해답 : Class.forName()은 클래스로더를 통해서 해당 데이터베이스 드라이버를 로드할 뿐 데이터베이스와의 연결에 관해서는 아무런 동작을 하지 않음
- 다시 확인 : kyun2.tistory.com/23?category=732999
* private void connect()를 통해 드라이버 및 데이터베이스 접속을 한 뒤 private void exeuteQuery()에서 SQL 쿼리문을 작성합니다.
3. SQL 쿼리문 전송 소스코드 - private void exeuteQuery()
package ex1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
public class TstJDBC {
private String ip = "해당 IP";
private int port = 해당포트;
private String database = "해당 데이터베이스 이름";
private String user = "DB 사용자 이름";
private String password = "DB 사용자 비밀번호";
private final String DRIVER_NAME = "DB DRIVER NAME";
private final String JDBC_URL = "jdbc:해당 DB:thin:@" + ip + ":" + port + ":" + database;
private Connection conn = null;
private Statement stmt = null;
// SQL SELECT 쿼리문 전송
private void exeuteQuery() {
String sql = "SELECT * FROM test";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
ResultSetMetaData rsmd = rs.getMetaData();
// ResultSetMetaData의 getColumnCount() 메서드는 컬럼의 갯수 반환
int columnCount = rsmd.getColumnCount();
// 컬럼 목록 보여주기
// 해당 위치의 컬럼명 반환
for(int i = 1; i <= columnCount; i++) {
System.out.print(rsmd.getColumnName(i) + "\t\t");
}
System.out.println();
// 얻어낸 컬럼 값 보여주기
while(rs.next()) {
for(int i = 1; i<= columnCount; i++) {
System.out.print(rs.getObject(i) + "\t\t");
}
System.out.println();
}
} catch (SQLException e) {
System.err.println("Execute sql Error");
}
}
// disconnect
private void disconnect() {
if (conn != null && stmt != null) {
try {
stmt.close();
conn.close();
} catch (SQLException e) {
System.err.println("Disconnect Error");
}
}
}
public static void main(String[] args) {
TstJDBC test = new TstJDBC();
test.exeuteQuery();
test.disconnect();
}
}
* 출처 포스팅과 소스코드가 다름을 밝힙니다.
* 학습을 위해 exeuteQuery, disconnect를 나눴습니다.
* 위의 소스는 Statement를 사용했습니다.
* 소스코드에서 계정 정보를 하드코딩하는 것은 절대로 피해야하는 위험한 방법이지만 예시이기 때문에 하드코딩을 해서 작성했습니다.
* 모든 작업이 끝나면 반드시 statement와 connection을 close해줘야합니다.
* SQL 쿼리문 전송 시 Statement, PreparedStatement 객체
- 자바는 주로 웹 개발을 위해서 사용되는데, 웹 개발에서는 보안문제(SQL 인젝션) 때문에 PreparedStatement를 사용합니다.
- 또한 반복적으로 쿼리를 수행해야한다면 PreparedStatement가 성능이 더 좋습니다.
< 가장 큰 차이점 >
- 캐시 사용 유무
- Statement : SQL문을 실행할 때 마다 SQL을 매 번 구문을 새로 작성해야하고 해석해야 해서 오버헤드가 있음
- PreparedStatement : 선처리 방식 사용 ( 준비된 statement ) 즉 SQL문을 미리 준비해 놓고 바인딩 변수(? 연산자)를 사용해서 반복되는 비슷한 SQL문을 쉽게 처리
* 쿼리 실행 순서
1. 쿼리 문장 분석
2. 컴파일
3. 실행
- Statement를 사용하면 매번 쿼리를 수행할 때마다 1 ~ 3 단계를 거침
- PreparedStatement를 사용하면 처음 한 번만 세 단계를 거친 후 캐시에 담아 재사용함
- 두 클래스의 주요 메소드
Statement | PreparedStatement |
ResultSet executeQuery(String sql) : Select문을 실행 | ResultSet executeQuery(String sql) : Select문을 실행 |
int executeUpdate(String sql) : insert, update, delete | int executeUpdate(String sql) : insert, update, delete |
void setXXX() : ?에 바인딩 시킬 변수를 데이터타입에 맞게 |
* SQL 쿼리문 전송 소스코드 - Statement
try {
Connection conn = null;
Statement stmt = null;
String sql = "INSERT CUSTOMER (NAME, AGE) VALUES('" + name + "', "+ age +"')";
conn = DriverManager.getConnection(JDBC_URL, user, password);
stmt = conn.createStatement();
stmt.executeQuery(sql);
stmt.close();
conn.close();
} catch (SQLException e) {
System.err.println("Execute sql Error");
}
String sqlstr = "SELECT name, memo FROM TABLE WHERE num =" + num
Statement stmt = conn.credateStatement();
ResultSet rst = stmt.executeQuerey(sqlstr);
* sqlstr를 실행 시 결과값을 생성
* 동일 코드에 대해 Statement가 아닌 PreparedStatement를 사용해서 예시를 만들어보도록 하겠습니다.
* 구조가 크게 차이가 나지는 않지만 sql문을 생성할 때, 쿼리문을 실행할 때 차이가 있습니다.
* PreparedStatement를 사용하면 sql문을 생성할 때 변수 값 대신 ?를 사용하게 됩니다.
* SQL 쿼리문 전송 소스코드 - PreparedStatement
try {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "INSERT CUSTOMER (NAME, AGE) VALUES(?, ?)";
conn = DriverManager.getConnection(JDBC_URL, user, password);
pstmt = conn.createStatement(sql);
pstmt.setString(1, name);
pstmt.setInt(2, age);
pstmt.executeUpate(sql);
stmt.close();
conn.close();
} catch (SQLException e) {
System.err.println("Execute sql Error");
}
String sqlstr = "SELECT name, memo FROM TABLE WHERE num = ? "
PreparedStatement stmt stmt = conn.credateStatement(sqlstr);
pstmt.setint(1, num);
ResultSet rst = stmt.executeQuerey();
* sqlstr은 생성시에 실행
* 인덱스에 해당하는 숫자는 물음표의 순번이라 정해진 순서대로 변수를 채워주면 됨
* 데이터 타입에 따른 set~
- setString(int, String) : 인덱스를 String값으로 지정
- setCharacter(int, Reader, int) : 인덱스의 파라미터 값을 LONG VARCHAR로 지정
- setInt(int, int) : 인덱스를 int값으로 지정
- setLong(int, long) : 인덱스를 long으로 지정
- setDouble(int, double) : 인덱스를 double로 지정
- setFloat(int, float) : 인덱스를 float로 지정
- setTime(int, Time) : 인덱스를 java.sql.Time으로 지정
- sysdate를 사용하는 경우 : sql문 생성 시 sysdate를 넣으면 됨
String sql = "INSERT CUSTOMER (NAME, AGE, ENROLLDATE) VALUES(?,?,sysdate)";
* Statement를 사용해야 하는 경우
- Dynamic SQL을 사용할 경우
: Dynamic SQL을 사용한다면 매번 조건절이 틀려지게 됨으로 statement가 낫다.
* PreparedStatement를 사용해야 하는 경우
- 사용자 입력값으로 쿼리를 생성하는 경우
- 쿼리 반복수행 작업일 경우
* executeQuery와 executeUpdate의 차이
- executeQuery : result set을 만드는 sql문에서 사용하며, 주로 SELECT문을 수행할 때 사용됨
- executeUpdate : INSERT나 UPDATE와 같은 DDL이나 DML을 실행할 때 사용
* 마지막으로 자원누수가 되지 않게 stmt, conn close는 필수 작업
'programming > Java' 카테고리의 다른 글
JAVA) JDBC 동작 순서 및 DB 연결( 소스코드 포함 ) - 최종 (0) | 2021.01.06 |
---|---|
JAVA) JDBC, DBCP, JNDI 차이점 (1) | 2020.12.31 |
자바 컴파일, 실행 (0) | 2020.11.04 |
자바 IoT 프로그래밍 (import), 파라미터 (paramter), 아규먼트(argument) (0) | 2020.11.04 |
데이터 타입 ( 숫자형, 문자형 ), 변수, casting, toString (0) | 2020.11.04 |
댓글