: JDBC는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API이다. JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Main {
/*
* [JDBC - Java Database Connectivity]
* - Java에서 데이터베이스에 접속할 수 있도록 하는 Java API
*/
public static void main(String[] args) {
//데이터베이스와 연결하는 객체
Connection con = null;
// 1. JDBC Driver Class
String driver = "com.mysql.jdbc.Driver";
// 2. 데이터베이스에 연결하기 위한 정보
String url = "jdbc:mysql://localhost:3306/test_db"; // 연결문자열, localhost - 127.0.0.1
String user = "root"; // 데이터베이스 ID
String pw = "1234"; // 데이터베이스 PW
try {
//1. JDBC 드라이버 로딩
Class.forName(driver);
// 2. Connection 생성
con = DriverManager.getConnection(url, user, pw); //데이터베이스 연결
System.out.println("[Database 연결 성공]");
} catch (SQLException e) {
System.out.println("[SQL Error : " + e.getMessage() +"]");
} catch (ClassNotFoundException e1) {
System.out.println("[JDBC Connector Driver Error : " + e1.getMessage() + "]");
} finally {
//Connection 사용 후 Close
if(con != null) {
try {
con.close();
} catch (Exception e) {
}
}
}
}
}
<Resource name="jdbc/myoracle" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:mysid"
username="scott" password="tiger" maxTotal="20" maxIdle="5"
maxWaitMillis="-1"/>
<resource-ref>
<description>Oracle Datasource example</description>
<res-ref-name>jdbc/myoracle</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
이처럼 jndi 이름을 통해 dataSource를 찾아온다.
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myoracle" resource-ref="true" />
혹은 xml 파일 내부에서 jndi-lookup 태그를 통해 dataSource를 찾아올 수 있다.
: Database Query 는 Statement 객체를 이용하는데, 이 객체는 DBConnection 을 통해서 얻을 수 있다.
Statement stmt = con.createStatement();
Statement 에는 다음의 2개의 대표함수가 있다.
int executeUpdate( String sql ); // db 조작에 관한 어떤 명령을 수행한다.
ResultSet executeQuery( String sql ) // db query 에 사용된다.
Statement 문제점 : Statement를 사용하여 쿼리를 하면 대소문자만 바뀌어도 다른 문장으로 인식 -> 서버에 문장정보를 저장하는 작업(파싱)을 다시 수행 -> 성능문제
해결책 : 그래서 나타난 것이 PreparedStatement.
String EmpName;
EmpName = "ABC"
String sql = "SELECT * FROM Emp WHERE EmpName = " + EmpName;
stmt = conn.prepareStatement(sql);
stmt.executeUpdate();
잘못된 방식 : Statement 객체를 이용해 문자열에 변수를 + 하는 형태로 매개변수를 전달하는 방식을 사용
SELECT * FROM Emp WHERE EmpName = 'ABC'
SELECT * FROM Emp WHERE EmpName = 'ABC123'
1.[SELECT * FROM Emp WHERE EmpName = 'ABC'] 라는 문장을 실행할 최적의 실행계획을 생성
2.해당 SQL문에 대한 Id를 부여하고 플랜 캐시에 저장
3.값을 추출
4.[SELECT * FROM Emp WHERE EmpName = 'ABC123'] 이라는 문장을 실행할 실행계획을 다시 생성
5.2번 3번의 반복
문제점 : 같은 실행계획을 사용하지만 실행계획을 다시 생성
sql = "SELECT * FROM Emp WHERE EmpName = ?";
psmt = conn.prepareStatement(sql);
psmt.setString(1, "ABC");
psmt.executeUpdate();
[SELECT * FROM Emp WHERE EmpName = ?] 라는 SQL 실행계획을 공유할 수 있어 실행계획이 다시 생성되지 않음 -> 속도도 훨씬 빠르며, 적은 부하로 데이터를 추출가능
Statement.executeUpdate(DML) >> int 반환
Statement.executeQuery(DQL) >> ResultSet 반환 >> 명령을 전달하여 실행시키는 메소드
ResultSet : 검색행을 저장하기 위한 인스턴스 정보
SELECT 명령은 Statement.executeQuery() 메소드로 전달하여 실행
=> 검색결과를 ResultSet 인스턴스에 저장하여 반환
1.ResultSet은 스크롤 기능을 설정, 사용 가능(커서유형)
ResultSet.TYPE_FORWARD_ONLY : 단방향 이동
ResultSet.TYPE_SCROLL_INSENSITIVE : 양방향 이동은 가능하지만 데이터의 변경이 바로 적용되지 않는다.
ResultSet.TYPE_SCROLL_SENSITIVE : 양방향 이동도 가능하고, 데이터 변경이 바로 적용된다.
2.병행성 : ResultSet 내의 결과값 집합에 대한 Update와 같은 조작이 가능한가?
CONCUR_READ_ONLY : 기본값. 읽기만 가능.
CONCUR_UPDATABLE : 값 수정 가능.
3.유지성 : ResultSet 객체를 계속 유지할 것인가?
HOLD_CURSORS_OVER_COMMIT : 커밋후에도 ResultSet 객체를 유지한다.
CLOSE_CURSORS_AT_COMMIT : 커밋후에는 ResultSet 객체를 닫는다. 성능향상.
Connection.createStatement() 메소드를 통해 지정한다.
4.DBMS가 지원하는 ResultSet 형태 정보 알아내기
5.Cursor
6.메소드 리턴값
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
sql = "SELECT * FROM T_BOARD"
// 1. 드라이버 연결 DB 커넥션 객체를 얻음
connection = DriverManager.getConnection(DBURL, DBUSER, DBPASSWORD);
// 2. 쿼리 수행을 위한 PreparedStatement 객체 생성
pstmt = conn.createStatement();
// 3. executeQuery: 쿼리 실행 후
// ResultSet: DB 레코드 ResultSet에 객체에 담김
rs = pstmt.executeQuery(sql);
} catch (Exception e) {
} finally {
conn.close();
pstmt.close();
rs.close();
}
}
이런 문제를 해결하기 위해서 커넥션풀(DBCP)를 사용한다.
Commons DBCP 스프링 설정
<bean id="dataSource-mysql"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${Globals.DriverClassName}"/>
<property name="url" value="${Globals.Url}" />
<property name="username" value="${Globals.UserName}"/>
<property name="password" value="${Globals.Password}"/>
<property name="maxActive" value="${Globals.maxActive}"/>
<property name="maxIdle" value="${Globals.maxIdle}"/>
<property name="maxWait" value="${Globals.maxWait}"/>
</bean>
pom.xml
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
Commons DBCP 구조
속성 이름 | 설명 |
---|---|
initialSize | BasicDataSource 클래스 생성 후 최초로 getConnection() 메서드를 호출할 때 커넥션 풀에 채워 넣을 커넥션 개수 |
maxActive | 동시에 사용할 수 있는 최대 커넥션 개수(기본값: 8) |
maxIdle | 커넥션 풀에 반납할 때 최대로 유지될 수 있는 커넥션 개수(기본값: 8) |
minIdle | 최소한으로 유지할 커넥션 개수(기본값: 0) |