[JDBC] JDBC 개요(이클립스-오라클 연결)
JDBC(Java DataBase Connectivity)란?
- 자바에서 데이터베이스에 접근할 수 있게 해주는 Programming API
DB 접속을 위한 Connection 인스턴스 생성을 위한 레퍼런스 변수 선언
나중에 finally 블럭에서 사용하기 위해 try 블럭 밖에 선언함
package com.greedy.section01.connection;
import java.sql.Connection;
public class Application1 {
public static void main(String[] args) {
Connection con = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
이 상태에서 실행시키면 ClassNotFoundExcception이 뜬다. ojdbc8을 복사해서 해당 프로젝트에 복사해준다.
나는 오라클에서 복사해서 넣었다.
경로 -> C:\app\(사용자이름)\product\18.0.0\dbhomeXE\jdbc\lib
프로젝트 내에서 lib이란 최상위폴더를 만들어서 그 안에 복붙함
프로젝트 우클릭-Properties-Java Build Path-Libraries-Add JARs.. 클릭
JAR Selection에서 lib-ojdbc8.jar 선택 후 OK클릭
Buil Path 경로에 ojdbc8이 추가된것 확인하고 Apply and Close 클릭
이후 실행하면 오류 안 난것 확인. 만약 오류가 난다면 상단 코드에서 클래스명에 대한 오타가 난 확률이 크다.
Connection 생성할 때, localhost는 ip주소인데
별도의 데이터베이스 서버를 구축해서 사용하는 경우에는 본인 컴퓨터의 ip를 따로 작성해줘야 한다.
나머지는 오라클에서 설정한 user, password 입력해준다.
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "C##EMPLOYEE", "EMPLOYEE");
package com.greedy.section01.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Application1 {
public static void main(String[] args) {
Connection conn = null;
try {
//사용할 드라이버 등록
Class.forName("oracle.jdbc.driver.OracleDriver");
//DriverManager를 이용해 Connection 생성
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "C##EMPLOYEE", "EMPLOYEE");
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
이후 실행하면 인스턴스 생긴것 확인
위에서 작성하던 Application1을 복사해서 해당 패키지에 그대로 복사해준다. Application2생성
변수를 이용해 작성한 문자열이 어떤 의미인지 알 수 있도록 개선
String driver, String url, String user, String password 따로 작성 후에
Class.forName 안에 값은 위에 정의돼있으니 driver 변수 넣어주고,
Connection 생성 시에 url, user, password 변수 넣어주면 문제 없이 상단과 동일하게 실행된다.
package com.greedy.section01.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Application2 {
public static void main(String[] args) {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String user = "C##EMPLOYEE";
String password = "EMPLOYEE";
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
.properties 파일에서 설정 정보 읽어와 접속하는 코드로 개선
jdbc-config.properties 파일 생성
Properties는 Key, Value값 둘 다 있는 Map형태인데 둘 다 String값을 갖는다.
주로 어떠한 설정 정보에 대한 키, 실제값을 하나로 만들어서 처리한다.
상단에 driver,url,user,password 써준 것을 properties 파일에 작성해주고
위에서 작성한 Application2를 복사해서 Application3을 생성한다.
package com.greedy.section01.connection;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class Application3 {
public static void main(String[] args) {
Properties prop = new Properties();
Connection conn = null;
try {
prop.load(new FileReader("src/com/greedy/section01/connection/jdbc-config.properties"));
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String password = prop.getProperty("password");
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Template 만드는 연습
최상위 폴더 안에 config 폴더 만든 후 기존에 만들어놓은 jdbc-config.properties복사 후 붙여넣어서
connection-info.properties로 수정해줌
DB와의 연결이 필요할 때 마다 호출해서 사용할 수 있는 메소드 정의(JDBCTemplate 클래스 생성)
package com.greedy.section02.template;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCTemplate {
public static Connection getConnection() {
Connection con = null;
Properties prop = new Properties();
try {
prop.load(new FileReader("config/connection-info.properties"));
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
Class.forName(driver); //connection에 연결하기위한 코드작성
con = DriverManager.getConnection(url, prop); //prop안에 user,password키값 전달
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
public static void close(Connection con) {
try {
if(con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
정상적으로 실행되는지 확인하는 Application
상단의 Template클래스에서 static으로 메소드를 정의했기 때문에 static은 따로 import를 작성해줘야 한다.
package com.greedy.section02.template;
import java.sql.Connection;
import static com.greedy.section02.template.JDBCTemplate.*;
public class Application {
public static void main(String[] args) {
Connection con = getConnection();
System.out.println(con);
//닫아주는 메소드 호출
close(con);
}
}
새 프로젝트 생성 후, 상단에서 만들어준 config, lib그대로 복사해주고 common 파일 만들어줘서 JDBCTemplate 복사를 해주었다.
이후 buildPath - Libraries에서 ojdbc8선택해준다.
연결이 됐으니 자바프로그램에서 실행하기 위한 Statement 객체를 통해 수행, 결과까지 출력
package com.greedy.section01.statement;
import static com.greedy.common.JDBCTemplate.*;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Application1 {
public static void main(String[] args) {
Connection con = getConnection();
//쿼리문을 저장하고 실행하는 기능을 하는 용도의 인터페이스
Statement stmt = null;
//select결과 집합을 받아올 용도의 인터페이스
ResultSet rset = null;
//Connection 인스턴스를 통해 Statement인스턴스 생성
try {
stmt = con.createStatement();
rset = stmt.executeQuery("SELECT EMP_ID, EMP_NAME FROM EMPLOYEE");
while(rset.next()) {
System.out.println(rset.getString("EMP_ID") + ", " + rset.getString("EMP_NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(stmt);
close(con);
}
}
}
여기서 close(con)까지는 JDBCTemplate에서 작성해주었지만 rset, stmt에 대한 close 메소드는 작성해주지 않았다.
추가로 작성해서 출력까지 해주자.
public static void close(Statement stmt) {
try {
if(stmt != null && stmt.isClosed()) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rset) {
try {
if(rset != null && rset.isClosed()) {
rset.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
특정 값을 전달 받아 Statement 출력
package com.greedy.section01.statement;
import static com.greedy.common.JDBCTemplate.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Application2 {
public static void main(String[] args) {
//1. Connection 생성
Connection con = getConnection();
Statement stmt = null;
ResultSet rset = null;
try {
//2. Statement 생성
stmt = con.createStatement();
String empId = "207";
String query = "SELECT EMP_ID, EMP_NAME FROM EMPLOYEE WHERE EMP_ID = '" + empId + "'";
System.out.println(query);
//3. executQuery()로 쿼리문 실행하고 결과를 ResultSet으로 반환 받음
rset = stmt.executeQuery(query);
//4. ResultSet에 담긴 결과 값을 컬럼 이름을 이용해 꺼내오기
if(rset.next()) {
//next() : ResultSet의 커서 위치를 하나 내리면서 행이 존재하면 true 존재하지 않으면 false를 반환
System.out.println(rset.getString("EMP_ID") + ", " + rset.getString("EMP_NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5. 사용한 자원 반납
close(rset);
close(stmt);
close(con);
}
}
}
Scanner를 통해 값을 입력해서 출력하기
package com.greedy.section01.statement;
import static com.greedy.common.JDBCTemplate.close;
import static com.greedy.common.JDBCTemplate.getConnection;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Application3 {
public static void main(String[] args) {
//1. Connection 생성
Connection con = getConnection();
Statement stmt = null;
ResultSet rset = null;
try {
//2. Statement 생성
stmt = con.createStatement();
Scanner sc = new Scanner(System.in);
System.out.print("조회하려는 사번을 입력해주세요 : ");
String empId = sc.nextLine();
String query = "SELECT EMP_ID, EMP_NAME FROM EMPLOYEE WHERE EMP_ID = '" + empId + "'";
System.out.println(query);
//3. executQuery()로 쿼리문 실행하고 결과를 ResultSet으로 반환 받음
rset = stmt.executeQuery(query);
//4. ResultSet에 담긴 결과 값을 컬럼 이름을 이용해 꺼내오기
if(rset.next()) {
//next() : ResultSet의 커서 위치를 하나 내리면서 행이 존재하면 true 존재하지 않으면 false를 반환
System.out.println(rset.getString("EMP_ID") + ", " + rset.getString("EMP_NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5. 사용한 자원 반납
close(rset);
close(stmt);
close(con);
}
}
}
상단의 코드에서 Scanner부분만 추가하고 변경해주면 쉽게 출력 가능하다.
DTO클래스 작성 후 EmployeeDTO에서 조회할 값을 Scanner로 입력해서 출력한다.
(EmployeeDTO 클래스 코드 생략)
package com.greedy.section01.statement;
import static com.greedy.common.JDBCTemplate.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
import com.greedy.model.dto.EmployeeDTO;
public class Application4 {
public static void main(String[] args) {
Connection con = getConnection();
Statement stmt = null;
ResultSet rset = null;
Scanner sc = new Scanner(System.in);
System.out.print("조회할 사번을 입력하세요 : ");
String empId = sc.nextLine();
String query = "SELECT * FROM EMPLOYEE WHERE EMP_ID = '" + empId + "'";
EmployeeDTO selectedEmp = null;
try {
stmt = con.createStatement();
rset = stmt.executeQuery(query);
if(rset.next()) {
selectedEmp = new EmployeeDTO();
selectedEmp.setEmpId(rset.getString("EMP_ID"));
selectedEmp.setEmpName(rset.getString("EMP_NAME"));
selectedEmp.setEmpNo(rset.getString("EMP_NO"));
selectedEmp.setEmail(rset.getString("EMAIL"));
selectedEmp.setPhone(rset.getString("PHONE"));
selectedEmp.setDeptCode(rset.getString("DEPT_CODE"));
selectedEmp.setJobCode(rset.getString("JOB_CODE"));
selectedEmp.setSalLevel(rset.getString("SAL_LEVEL"));
selectedEmp.setSalary(rset.getInt("SALARY"));
selectedEmp.setBonus(rset.getDouble("BONUS"));
selectedEmp.setManagerId(rset.getString("MANAGER_ID"));
selectedEmp.setHireDate(rset.getDate("HIRE_DATE"));
selectedEmp.setEntDate(rset.getDate("ENT_DATE"));
selectedEmp.setEntYn(rset.getString("ENT_YN"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(stmt);
close(con);
}
System.out.println("selectedEmp : " + selectedEmp);
}
}
모든 행에 대한 모든 객체를 ArrayList로 출력해보기
package com.greedy.section01.statement;
import static com.greedy.common.JDBCTemplate.close;
import static com.greedy.common.JDBCTemplate.getConnection;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.greedy.model.dto.EmployeeDTO;
public class Application5 {
public static void main(String[] args) {
Connection con = getConnection();
Statement stmt = null;
ResultSet rset = null;
// 한 행 정보를 담을 DTO
EmployeeDTO row = null;
//여러 DTO를 하나의 인스턴스로 묶기 위한 List
List<EmployeeDTO> empList = null;
String query = "SELECT * FROM EMPLOYEE";
try {
stmt = con.createStatement();
rset = stmt.executeQuery(query);
empList = new ArrayList<>();
while(rset.next()) {
row = new EmployeeDTO();
row.setEmpId(rset.getString("EMP_ID"));
row.setEmpName(rset.getString("EMP_NAME"));
row.setEmpNo(rset.getString("EMP_NO"));
row.setEmail(rset.getString("EMAIL"));
row.setPhone(rset.getString("PHONE"));
row.setDeptCode(rset.getString("DEPT_CODE"));
row.setJobCode(rset.getString("JOB_CODE"));
row.setSalLevel(rset.getString("SAL_LEVEL"));
row.setSalary(rset.getInt("SALARY"));
row.setBonus(rset.getDouble("BONUS"));
row.setManagerId(rset.getString("MANAGER_ID"));
row.setHireDate(rset.getDate("HIRE_DATE"));
row.setEntDate(rset.getDate("ENT_DATE"));
row.setEntYn(rset.getString("ENT_YN"));
empList.add(row);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(stmt);
close(con);
}
for(EmployeeDTO emp : empList) {
System.out.println(emp);
}
}
}
Prepared Statement 예시
위의 예시와의 차이점은 PreparedStatement는 생성 시에 수행할 쿼리문을 전달하고 실행 시에는 쿼리문을 전달하지 않는다. 미리 쿼리문을 전달한다.
package com.greedy.section02.preparedstatement;
import static com.greedy.common.JDBCTemplate.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Application1 {
public static void main(String[] args) {
Connection con = getConnection();
PreparedStatement pstmt = null;
ResultSet rset = null;
try {
pstmt = con.prepareStatement("SELECT EMP_ID, EMP_NAME FROM EMPLOYEE");
rset = pstmt.executeQuery();
//생성 시에 수행할 쿼리문을 전달하고 실행 시에는 쿼리문을 전달하지 않는다. 미리 쿼리문을 전달한다
while(rset.next()) {
System.out.println(rset.getString("EMP_ID") + ", " + rset.getString("EMP_NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
close(con);
}
}
}