728x90
반응형
💡JDBC(Java DataBase Connectivity)
- 자바 프로그래밍 언어로 만들어진 클래스와 인터페이스로 이루어진 API로서 ANSI SQL(1999)를 지원
- SQL문을 실행할 수 있는 함수 호출 인터페이스이다
- java.sql 패키지를 사용
💡JDBC 특징
- DBMS 종류에 독립적인 자바 프로그래밍 가능
- 데이터베이스가 달라지더라도(MySQL/Oracle.. 등등) 동일한 API를 사용하게 해 준다(드라이버 및 URL만 수정하면 어떤 DBMS도 가능)
- 자바가 가지는 플랫폼에 독립적이라는 특성과 DBMS에 독립적인 특성을 가진다
자바를 만든 곳은 [썬마이크로시스템즈]이다. 자바에서 sql을 연결하기 위해 일일이 코딩을 해야 하는 불편함을 썬도 알았다. 그래서 java에서 sql을 연결할 수 있는 API를 썬 측에서 만든다(java.sql package). 하지만 구현은 되어있지 않고 모양만 갖춰놨다. 왜 그럴까? DBMS(Oracle, MySQL..)사마다 요구하는 모든 성능을 썬 측에서 구현해줄 순 없다.
💡JDBC 기능
- 데이터베이스에 연결 설정한다(Connection)
- SQL 문장을 DBMS에 전송한다(Statement)
- SQL문장 전송 후 결과를 처리할 수 있게 해 준다(ResultSet)
💡JDBC Interface
- Database를 만드는 업체에게 제공되는 인터페이스(업체에게 제공되는 인터페이스를 각각의 DBMS 업체들이 구현해 놓은 것으로서 이것이 바로 드라이버이다)
- 프로그래머에게 제공되는 인터페이스(SQL 패키지가 제공하고 있는 라이브러리로서 프로그래머는 이 라이브러리를 기반으로 DB 프로그램을 작성할 수 있다)
💡JDBC API : java.sql package
1. Driver (interface)
- 드라이버에 대한 정보를 가지고 있다
- 모든 드라이버가 반드시 구현해야 하는 인터페이스
- 드라이버의 버전이나 연결에 대한 정보를 알아볼 수 있는 메서드를 가지고 있다
썬은 DBMS회사한테 우리가 개발할 수 있는 인터페이스를 제공했으니, 그쪽은 껍데기를 채울 수 있는 무언가를 제공해야 돼! 그게 바로 드라이버(jar파일). class를 여러 개 묶어서 주는 게 jar파일 / 그래픽카드는 주로 dll로 주는데 그게 많으니 묶어서 exe, msi로 묶어서 준다. 그것처럼 db에서는 jar파일을 준다 / 인터페이스를 쓰면 내용을 알아서 채워줌 / 외부에서 다운받는 api를 '벤더 API'라고 함('표준이 아닌, 제품을 제공하는 쪽에서 제공한다' 해서 벤더 API)
2. Connection (interface)
- 데이터베이스에 대한 하나의 세션을 표현한다(세션은 하나의 클라이언트가 서버에 요청을 하기 위해 연결을 맺은 상태를 의미한다)
- DriverManager 클래스의 getConnection() 메서드를 이용하여 얻어 올 수 있다
- 디폴트로 setAutoCommit(true)로 설정된다
- 개발자가 원하는 경우에 commit을 해주고 싶거나 트랜잭션이 아주 중요한 부분에 있어서 RollBack 처리를 하고자 할 경우에는 setAutoCommit(false)로 설정한다(단, 이 경우에는 SQL문을 수행할 때마다 명시적으로 commit()을 호출해야 한다)
3. Statement (interface)
- SQL 문장을 실행하고 그것에 대한 결과 값을 가져오기 위해 사용한다
- public boolean execute(String sql) throws SQLException(특별히 SQL문을 구분하지 않고 DML(delete, update, insert), Query(select), DDL(create, drop)등을 수행할 수 있다. 결과가 ResultSet이면 true이고 결과가 DML이거나 특별한 결과가 없으면 false를 리턴한다)
- public ResultSet executeQuery(String sql) throws SQLException(->Select를 처리할 때 사용한다)
- public int executeUpdate(String sql) throws SQLException(->주로 DML(delete, update, insert)등의 SQL을 수행할 때 사용한다)
4. PreparedStatement (interface) -> Statiement 중에 가장 많이 사용
- 동일한 SQL 문장이 여러 번 반복적으로 수행될 때 사용하는 객체
- 대용량의 문자나 바이너리 타입의 데이터(이미지나 사운드 등)를 저장하기 위해서도 사용될 수 있다
- SQL 문장이 미리 컴파일되어 PreparedStatement 객체에 저장된다
- 여러 번 반복 수행 시 clearParameters() 메서드를 이용해 Statement에 남겨진 값을 초기화한다
- public ResultSet executeQuery() throws SQLException(->Select를 처리할 때 사용한다)
- public int executeUpdate() throws SQLException(주로 DML(delete, update, insert)등의 SQL을 수행할 때 사용한다)
5. CallableStatement (interface)
- 데이터베이스에 대하여 실제 SQL문을 실행하는 것이 아니라 Stored Procedures(함수를 만들어서 데이터에 넣어놓을 수 있는데, 함수를 호출하면 쿼리 문장이 실행된다)를 호출한다
- Stored Procedures란 연속되는 SQL문으로 데이터베이스에 저장해 두고 마치 함수의 호출처럼 사용한다
- 데이터베이스에 Stored Procedures를 만들어 두고 자바에서 호출하여 사용할 수 있게 한다
- Stored Procedures 사용 시 속도의 향상을 기대할 수 있고, 자바 코드에 SQL 문장이 들어가지 않으므로 자바 코드가 SQL에 독립적이 된다
6. ResultSet (interface)
- 쿼리에 대한 결과값 처리
- SELECT일 때만 결과를 얻어올 때만 사용
- ResultSet 객체의 커서는 첫 번째 레코드보다 바로 이전을 가리킨다
- next() -> ResultSet 객체의 커서를 이동
- getXXX(index or name) 메서드를 이용하여 데이터를 얻을 수 있다 -> getString(index or name); / getInt(index or name); / getDate(index or name); /...
💡 JDBC Programming 개발 순서(외우기)
- JDBC Driver Loading
- DBMS와 연결 (Connection 생성)
- SQL 실행 준비 (Statement, PreparedStatement 생성) + SQL 문장도 이때 만들면 됨
- SQL 실행 DML (insert, update, delete)→int를 return + Query(select)→resultSet을 return
- DBMS 연결 끊기 : throws SQLException으로 예외처리가 되어있기에 마지막에 꼭 끊어줘야 함, 연결 역순으로!!
throws SQLException은 비 런타임 Exception(예외가 안 나도 예외 처리해줘야 함)이다. 그럼 예외처리를 꼭 해줘야 하는데 즉, try-catch를 무조건 해줘야 한다. try안에서 exception을 하면 종료가 안될 거고, catch에다 하면 정상 실행 안될 거고, 그럼 어디에다?? finally에서 해줘야 한다.
1. JDBC Driver Loading
- 데이터베이스와의 접속을 오픈하기 위해 애플리케이션의 JVM안으로 특정 드라이버 클래스를 적재
- JavaAPI: lang-class-forName메서드를 쓰면 클래스의 정보를 읽어드릴 수 있다
Class.forName(Driver ClassName);
- 각 DataBase별 Driver Class(reference library에 jar파일에 가면 있음)
- MySQL : com.mysql.cj.jdbc.Driver
- Oracle : oracle.jdbc.driver.OracleDriver
- MSSQL : com.microsoft.sqlserver.jdbc.SQLServerDriver
- 실제 코드
public class ClassLoadingTest {
private final String DRIVER = "com.mysql.cj.jdbc.Driver";// 길어서 따로 상수로 뺌
public ClassLoadingTest() { // 생성자
try {
Class.forName(DRIVER);
System.out.println("Driver Loading Success!"); // 드라이버가 잘 로딩됐는지 확인
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ClassLoadingTest(); //생성자 호출만
}
}
2. DBMS와 연결 (Connection 생성)
- DriverManager 클래스를 이용하여 URL형태로 주어진 데이터 베이스에 대한 접속을 요청
- JavaAPI: sql-DriverManager-Connection메서드를 쓰면 DB와 연결할 수 있다
Connection conn = DriverManager.getConnection(URL, dbid, dbpassword);
- JDBC URL은 드라이버 고유의 방식으로 개별적인 데이터베이스를 식별
- MySQL : jdbc:mysql://HOST:PORT/DBNAME[?param1=value1¶m2=value2&..]
- Oracle : jdbc:oracle:thin:@HOST:PORT:SID
- MSSQL : jdbc:sqlserver://HOST:PORT;databaseName=DB
- 실제 코드
import java.sql.*;
public class ConnectionTest {
private final String DRIVER = "com.mysql.cj.jdbc.Driver";
private final String URL = "jdbc:mysql://127.0.0.1:3306/feastforall?serverTimezone=UTC&useUniCode=yes&characterEncoding=UTF-8";
private final String ID = "MySQL의 접속 아이디를 적으세요";
private final String PW = "MySQL의 접속 비밀번호를 적으세요";
public ConnectionTest() { // 생성자 안에 그대로 복사
try {
Class.forName(DRIVER);
System.out.println("Driver Loading Success!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ConnectionTest ct = new ConnectionTest();
ct.connect(); // connect메서드를 생성
}
private void connect() {
try {
Connection conn = DriverManager.getConnection(URL, ID, PW);
System.out.println("DB Connection Success!");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3. SQL 실행 준비 (Statement, PreparedStatement 생성)
- String sql = “insert, update, delete, select, …”;
- Statement 생성
Statement stmt = conn.createStatement();
- PreparedStatement 생성
PreparedStatement pstmt = conn.prepareStatement(sql);
4. SQL 실행 (executeUpdate, executeQuery)
- Statement(preparedStataemet와 다르게 실행할 때 sql문장을 가져옴, 준비할 땐 안 가져오고)
■ DML -> int cnt = stmt.executeUpdate(sql); //새로운 데이터를 업데이트한다는 뜻, iud의 update 아님
■ Select -> ResultSet rs = stmt.executeQuery(sql);
- PreparedStatement(statement와 다르게 sql을 준비단계에서 가져왔기 때문에 괄호 안에 sql문장 안 들어감)
- sql문의 치환 변수 값 설정 : pstmt.setXXX(index, val);
■ DML -> int cnt = pstmt.executeUpdate();
■ Select -> ResultSet rs = pstmt.executeQuery();
- ResultSet
- ResultSet을 얻어온 후 rs.next()를 실행해야 한다.
■ select의 결과가 반드시 하나가 나오는 경우. : rs.next();
■ select의 결과가 하나 또는 못 얻어 오는 경우. : if(rs.next) { }
■ select의 결과가 여러 개가 나올 수 있는 경우. : while(rs.next()) { }
- 값 얻기
■ String str = rs.getString(index or name);
■ int cnt = rs.getInt(index or name);
- 코드실행1 (statement사용) : INSERT
import java.sql.*;
public class InsertTest {
private final String URL = "jdbc:mysql://127.0.0.1:3306/feastforall?serverTimezone=UTC&useUniCode=yes&characterEncoding=UTF-8";
private final String ID = "MySQL의 접속 아이디를 적으세요";
private final String PW = "MySQL의 접속 비밀번호를 적으세요";
public static void main(String[] args) {
InsertTest it = new InsertTest();
ProductDto productdto = new ProductDto();// 만든 dto에 값넣기
productdto.setProductCode("AA200");
productdto.setProductName("시크릿쥬쥬에디션");
productdto.setPrice(100000);
// 상품을 등록하는 메서드(DTO가 필요한 시점)
int cnt = it.registProduct(productdto); // productdto의 모든 정보를 이 메서드에 넣어서 db에 전달해줘
System.out.println(cnt != 0 ? "insert success!" : "insert fail!");
}
private int registProduct(ProductDto productdto) {
int cnt = 0; // Tip: 개수를 반환할 때에는 return에 반환변수를 넣어놓고 중간채우기
try {
Connection conn = DriverManager.getConnection(URL, ID, PW);
Statement stmt = conn.createStatement();// SQL실행준비
String sql = "insert into product(productCode,productName,price)";
sql += "values('" + productdto.getProductCode() + "','" + productdto.getProductName() + "',"
+ productdto.getPrice() + ")"; // 숫자는 홑따옴표 안붙이고, 문자열은 붙이고
cnt = stmt.executeUpdate(sql);// SQL실행 -> 성공했으면1 실패했으면 0
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("DB Connection Success!");
return cnt;
}
}
- 코드실행2 (PreparedStatement사용) : INSERT
import java.sql.*;
public class InsertTest {
private final String URL = "jdbc:mysql://127.0.0.1:3306/feastforall?serverTimezone=UTC&useUniCode=yes&characterEncoding=UTF-8";
private final String ID = "MySQL의 접속 아이디를 적으세요";
private final String PW = "MySQL의 접속 비밀번호를 적으세요";
public static void main(String[] args) {
InsertTest it = new InsertTest();
ProductDto productdto = new ProductDto();// 만든 dto에 값넣기
productdto.setProductCode("AA203");
productdto.setProductName("어쩔티비");
productdto.setPrice(100000);
// 상품을 등록하는 메서드(DTO가 필요한 시점)
int cnt = it.registProduct(productdto); // productdto의 모든 정보를 이 메서드에 넣어서 db에 전달해줘
System.out.println(cnt != 0 ? "insert success!" : "insert fail!");
}
private int registProduct(ProductDto productdto) {
int cnt = 0; // Tip: 개수를 반환할 때에는 return에 반환변수를 넣어놓고 중간채우기
try {
Connection conn = DriverManager.getConnection(URL, ID, PW);
String sql = "insert into product(productCode,productName,price)";
// statement사용
// sql += "values('" + productdto.getProductCode() + "','" + productdto.getProductName() + "',"
// + productdto.getPrice() + ")"; // 숫자는 홑따옴표 안붙이고, 문자열은 붙이고
// Statement stmt = conn.createStatement();// SQL실행준비
// cnt = stmt.executeUpdate(sql);// SQL실행 -> 성공했으면1 실패했으면 0
// PreparedStatement사용
sql += "values(?,?,?)"; // 치환변수 사용, 세미콜론 있어도 MySQL은 실행가능,,하지만 가능하면 쓰지말것
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL실행준비
pstmt.setString(1, productdto.getProductCode()); // db의 인덱스는 항상 1부터
pstmt.setString(2, productdto.getProductName());
pstmt.setInt(3, productdto.getPrice());// 가격은 정수라서 setInt
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("DB Connection Success!");
return cnt;
}
}
- 코드실행3 (PreparedStatement사용) : UPDATE
import java.sql.*;
public class UpdateTest {
private final String URL = "jdbc:mysql://127.0.0.1:3306/feastforall?serverTimezone=UTC&useUniCode=yes&characterEncoding=UTF-8";
private final String ID = "MySQL의 접속 아이디를 적으세요";
private final String PW = "MySQL의 접속 비밀번호를 적으세요";
public static void main(String[] args) {
UpdateTest ut = new UpdateTest();
ProductDto productdto = new ProductDto();
productdto.setProductCode("AA200"); //AA200의 가격을
productdto.setPrice(120000); //120000으로 바꿔보자
int cnt = ut.updateProduct(productdto);
System.out.println(cnt != 0 ? "update success!" : "update fail!");
}
private int updateProduct(ProductDto productdto) {
int cnt = 0;
try {
Connection conn = DriverManager.getConnection(URL, ID, PW);
String sql = "update product \n";
sql += "set price = ?\n";
sql += "where productcode = ?\n";
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL실행준비
pstmt.setInt(1, productdto.getPrice());// 실행순서 주의!! sql에서 set부터 하니까
pstmt.setString(2, productdto.getProductCode());
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("DB Connection Success!");
return cnt;
}
}
- 코드실행4 : DELETE
import java.sql.*;
public class DeleteTest {
private final String URL = "jdbc:mysql://127.0.0.1:3306/feastforall?serverTimezone=UTC&useUniCode=yes&characterEncoding=UTF-8";
private final String ID = "MySQL의 접속 아이디를 적으세요";
private final String PW = "MySQL의 접속 비밀번호를 적으세요";
public static void main(String[] args) {
DeleteTest dt = new DeleteTest();
ProductDto productdto = new ProductDto();
int cnt = dt.deleteProduct("AA203");// delete할때에는 코드만 있으면 된다
System.out.println(cnt != 0 ? "delete success!" : "delete fail!");
}
private int deleteProduct(String productcode) {
int cnt = 0;
try {
Connection conn = DriverManager.getConnection(URL, ID, PW);
String sql = "delete from product \n";
sql += "where productcode = ?\n";
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL실행준비
pstmt.setString(1, productcode);// 코드만 지우면 되니까
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("DB Connection Success!");
return cnt;
}
}
5. DBMS 연결 끊기
- 모든 작업이 끝난 경우에는 ResultSet, Statement(PreparedStatement), Connection 객체의 close() 메서드를 이용하여 작업을 종료한다(연결한 순의 역순으로 종료)
- Connection은 상당한 Overhead를 가져온다. 따라서 최적화된 상태를 유지하기 위해서는 반드시 Connection을 닫아 주어야 한다(그래서 finally에서 작업해야 함)
■ rs.close();
■ pstmt.close();
■ conn.close();
728x90
반응형