개발자입니다
[비트캠프] 67일차(14주차4일) - JDBC(JDBC API), myapp-35 본문
[비트캠프] 67일차(14주차4일) - JDBC(JDBC API), myapp-35
끈기JK 2023. 2. 9. 10:30
JDBC API
Java Database Connectivity - 자바용 데이터베이스 연결자
DBMS API : Vendor API (Native API)
static library
*잠깐!
App. 이 printf(), scanf() 등 .lib 를 사용하면 .exe 실행파일에 함께 묶인다. 이를 실행하면 App + .lib 가 함께 process(실행 중인 프로그램) 된다. App.2 에 의해 다른 process 도 실행되면 .lib 가 메모리에 중복 로딩 된다.
.lib 는 static library (정적 라이브러리) 이다.
프로그램 실행이 무조건 로딩되면 중복 로딩으로 메모리 낭비이다.
App. 에서 call → Oracle API 에서 요청 → Oracle 하고 응답 받아 결과를 App. 으로 return 한다.
App. 에서 call → MS-SQL API 에서 요청 → MS-SQL 하고 응답 받아 결과를 App. 으로 return 한다.
App. 에서 call → MySQL API 에서 요청 → MySQL 하고 응답 받아 결과를 App. 으로 return 한다.
API 들은 DBMS 제조사에서 제공하며, C/C++ 로 만들어졌다.
windows 에서는 dynamic linking library : .dll, static library : .lib 이다.
Unix 에서는 dynamic linking library : .so (shared object), static library : .lib 이다.
DBMS 마다 API 사용법이 다르다! → DBMS 에 맞춰서 프로그래밍을 해야한다.
컴퓨터를 실행하면 0번 섹터에 있는 명령부터 실행한다. 보통 운영체제가 설치되어 있다.
java의 System.out.println() 을 호출하면 JVM 은 운영체제의 C function 을 호출한다. python, javascript 모두 마찬가지이다.
dynamic library
App. 이 실행 파일을 실행하면 App + dll 정보가 process 된다.
App2 가 실행 파일을 실행하면 App2 + dll 정보가 process 된다.
각 process의 dll 정보에 의해 .dll 파일을 공유한다. 라이브러리 중복 로딩 방지 = 메모리 낭비 줄임!
dynamic library : 어떤 App. 을 실행할 때 한 번 로딩되면, 다른 App. 은 로딩된 것을 그냥 사용한다. 중복 로딩되지 않는다!
DBMS API : ODBC API 명세 구현 → ODBC Driver (.dll/.lib/.so)
ODBC API : Open Database Connectivity, DBMS API 명세서
App 의 API 사용법이 통일되었기 때문에 특정 DBMS 에 종속되지 않는다. DBMS 마다 따로 개발할 필요가 없다.
App(C/C++) 이 각 [Oracle | MS-SQL | MySQL] ODBC API 구현체(C/C++)를 call 하면 여기서 각 [Oracle | MS-SQL | MySQL] API 를 call 하고 여기서 [Oracle | MS-SQL | MySQL] 로 요청해서 응답 받는다.
ODBC API 규격에 맞춰 함수 구현한 것을 "ODBC Driver" 라 한다.
DBMS API : JDBC API
J(ava)DBC API (인터페이스 : java.sql.*, javax.sql.*) 구현한 것이 JDBC Driver(클래스)
Type 1 = 'ODBC-JDBC Bridge'
Java App. 에서 JDBC Type 1 Driver 를 call 한다. JDBC Type 1 Driver 는 ODBC Driver 의 함수 호출한다. JRE 에 기본 포함되어 있다. JDBC API 인터페이스 구현하였다.
ODBC Driver의 함수 호출하기 때문에 문제점은 local 에 ODBC Driver 를 설치해야 한다는 것이다.
Oracle, MS-SQL, MySQL ODBC API 구현체가 각 API 를 call 하고 결과를 return 받는다.
그 결과를 JDBC Type 1 Driver 가 return 한다.
Type 2 = 'Native API call'
Java App. 에서 [Oracle | MS-SQL | MySQL] JDBC Type 2 Driver 를 call 한다. API 사용법이 같기 때문에 다른 프로그램을 짤 필요가 없다. 이는 Vendor 에서 제공하므로 다운로드 해야 한다. Native C/C++ API 호출하므로 local Vender API 가 있어야 한다.
문제점! DBMS 를 변경한다면, local 에 Vendor API 를 교체하고 Type 2 JDBC Driver 를 교체 해야한다.
Type 3 = 'Network driver'
로컬, 서버2 (middleware), 서버1 로 나뉜다.
Java App. 은 JDBC Driver 를 다른 타입으로 교체하더라도 API 는 같기 때문에 프로그램을 새로 짤 필요가 없다. 즉 App 은 드라이버 타입과 아무 상관이 없다. 여기서 중개서버 통신용 JDBC Type 3 Driver 를 call 한다.
JDBC Type 3 Driver 는 JDBC API 규격에 따라 만든 구현체이다. JDBC Driver 중계서버에 요청하고 응답 받는다.
- DBMS 를 교체하더라도 영향을 받지 않는다.
- 중계 서버에서 제공한다. 중계 서버를 교체하면 드라이버 교체해야 한다.
- 중계서버를 따로 구매하면 비용 증가!
- 중계 서버 경유하기 때문에 실행속도가 떨어진다.
Type 4 = 'Network Protocol Driver'
Java App. 에서 [Oracle | MS-SQL | MySQL] Type 4 Driver 를 call 한다. DBMS Vendor 에서 제공하므로 다운로드 해야한다. DBMS 와 직접 통신하므로 local에 추가로 설치할 게 없다. C/C++과 같은 특정 OS에 종속되는 API 를 사용하지 않는다. → "Pure Java"
여기서 [Oracle | MS-SQL | MySQL] 에 요청하고 응답 받는다.
MariaDB JDBC 드라이버 추가
eomcs-java-lang > build.gradle 파일에서 아래 코드 추가한다.
dependencies {
// MariaDB JDBC 드라이버
implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.2'
MariaDB JDBC 드라이버 최신 버전은 다음에서 확인한다.

설정 후 아래 명령창에 입력해서 mariadb 라이브러리 다운로드 되게 한다.
C:\Users\bitcamp\git\bitcamp-ncp\eomcs-java-lang>gradle eclipse
라이브러리에 추가된 것 확인 가능하다.

JDBC Driver 사용법

insert, update, delete 는 Statement 구현체까지 진행하고, select 는 ResultSet 구현체까지 진행한다.
① java.sql.DriverManager 의 getConnecction(JDBC URL(DBMS 주소), ID, PWD) 메서드로 DBMS 연결 요청한다.
② DBMS 는 스레드(T) 생성하여 연결하고 Connection 구현체(con) return 한다.
③ con.createStatement() 로 SQL 실행 도구 요청한다.
④ Statement 구현체(stmt) return 한다.
⑤ SQL 전달하는데 select 요청은 stmt.executeQuery(SQL select 문)
insert, update, delete 요청은 stmt.executeUpdate(SQL insert, update, delete 문) 으로 한다.
⑥ stmt 는 SQL 을 Connection 구현체로 전달한다.
⑦ con 은 T 로 SQL 전달한다.
⑧ T 는 결과 전달한다.
⑨ con → stmt 에서 ResultSet 구현체(rs) return 한다.
⑩ 사용자가 next() 로 select 결과 한개를 요청한다.
⑪ rs 가 다음 레코드를 stmt 에 요청한다.
⑫ stmt → con 로 요청한다.
⑬ con → T 로 다음 레코드 요청한다.
⑭ T → con 로 다음 레코드 전달한다.
⑮ con → stmt → rs 로 전달하고, rs 에서 서버에서 가져온 다음 레코드의 각 컬럼 값을 보관한다.
※ 주의! 서버에서 select 결과를 모두 가져오는 것이 아니다. 한 개씩 가져온다!
⑯ rs.getXxx(컬럼명 | 컬럼 번호) 로 rs 에서 컬럼값을 꺼낸다.
myapp
35. DBMS 도입

app-server 를 데이터 처리 전문 Application 인 DBMS로 교체한다.
DBMS 도입 후 System Architecture

app-client 에서 call(SQL 문을 이용해 데이터 처리 작업을 지시한다)을 MariaDB JDBC Driver(download) 로 한다.
여기서 《DBMS》MariaDB(local에 설치(나중에 Naver Cloud 서비스 이용)) 로 요청한다.
여기서 파일로 I/O 하고 결과를 응답한다.
Application Architecture : app-client

사용자가 ClientApp 으로 게시글 관리하려 하면 BoardHandler 객체에서 service() 호출한다.
BoardHandler 는 약결합된 《interface》BoardDao 를 call 하고 그 구현체 JdbcBoardDao 를 사용한다.
JdbcBoardDao 는 DBMS 의 스레드(T) 와 연결한다.
myapp-client > build.gradle 에 아래 코드 추가한다.
dependencies {
// MariaDB JDBC 드라이버
implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.2'
MySQL JDBC 명령어는 검색어 'mysql jdbc driver documentation ' 참고한다.
MySQL 컬럼명 관행은 검색어 'mysql column name convention' 참고한다.
### 35. DBMS 도입하기
- DBMS를 이용해 데이터를 처리하는 방법
app-client 에서 Jdbc----Dao 생성하고, Network----Dao, Local----Dao 삭제한다.

DBMS 에 테이블 생성한다.
create table app_board (
board_id int not null,
title varchar(255) not null,
content text not null,
pwd varchar(10),
created_date datetime default now(),
view_cnt int default 0
);
alter table app_board
add constraint primary key (board_id),
modify column board_id int not null auto_increment;
create table app_student(
student_id int not null,
name varchar(50) not null,
tel varchar(20),
created_date datetime default now(),
pst_no varchar(5),
bas_addr varchar(255),
det_addr varchar(255),
work boolean,
gender char(1),
level int
);
alter table app_student
add constraint primary key (student_id),
modify column student_id int not null auto_increment;
create table app_teacher(
teacher_id int not null,
name varchar(50) not null,
tel varchar(20),
created_date datetime default now(),
email varchar(50),
degree int,
school varchar(50),
major varchar(50),
wage int
);
alter table app_teacher
add constraint primary key (teacher_id),
modify column teacher_id int not null auto_increment;
myapp-client > Network----Dao 복사해서 Jdbc----Dao 생성한다.
package bitcamp.myapp.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import bitcamp.myapp.vo.Board;
public class JdbcBoardDao implements BoardDao {
@Override
public void insert(Board b) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format("insert into app_board(title, content, pwd) values('%s', '%s', '%s')",
b.getTitle(), b.getContent(), b.getPassword());
stmt.executeUpdate(sql);
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public Board[] findAll() {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select board_id, title, created_date, view_cnt from app_board order by board_id desc")) {
ArrayList<Board> list = new ArrayList<>();
while (rs.next()) {
Board b = new Board();
b.setNo(rs.getInt("board_id"));
b.setTitle(rs.getString("title"));
b.setCreatedDate(rs.getString("created_date"));
b.setViewCount(rs.getInt("view_cnt"));
list.add(b);
}
Board[] boards = new Board[list.size()];
list.toArray(boards);
return boards;
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public Board findByNo(int no) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select board_id, title, content, pwd, created_date, view_cnt from app_board where board_id=" + no)) {
if (rs.next()) {
Board b = new Board();
b.setNo(rs.getInt("board_id"));
b.setTitle(rs.getString("title"));
b.setContent(rs.getString("content"));
b.setPassword(rs.getString("pwd"));
b.setCreatedDate(rs.getString("created_date"));
b.setViewCount(rs.getInt("view_cnt"));
return b;
}
return null;
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public void update(Board b) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format("update app_board set title='%s', content='%s' where board_id=%d",
b.getTitle(), b.getContent(), b.getNo());
stmt.executeUpdate(sql);
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public boolean delete(Board b) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format("delete from app_board where board_id=%d", b.getNo());
return stmt.executeUpdate(sql) == 1;
} catch (Exception e) {
throw new DaoException(e);
}
}
}
package bitcamp.myapp.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import bitcamp.myapp.vo.Teacher;
public class JdbcTeacherDao implements TeacherDao {
@Override
public void insert(Teacher s) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format(
"insert into app_teacher(name, tel, email, degree, school, major, wage)"
+ " values('%s','%s','%s',%d,'%s','%s',%d)",
s.getName(),
s.getTel(),
s.getEmail(),
s.getDegree(),
s.getSchool(),
s.getMajor(),
s.getWage());
stmt.executeUpdate(sql);
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public Teacher[] findAll() {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select teacher_id, name, tel, degree, major, wage"
+ " from app_teacher"
+ " order by teacher_id desc")) {
ArrayList<Teacher> list = new ArrayList<>();
while (rs.next()) {
Teacher s = new Teacher();
s.setNo(rs.getInt("teacher_id"));
s.setName(rs.getString("name"));
s.setTel(rs.getString("tel"));
s.setDegree(rs.getInt("degree"));
s.setMajor(rs.getString("major"));
s.setWage(rs.getInt("wage"));
list.add(s);
}
Teacher[] arr = new Teacher[list.size()];
list.toArray(arr);
return arr;
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public Teacher findByNo(int no) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select teacher_id, name, tel, created_date, email, degree, school, major, wage"
+ " from app_teacher"
+ " where teacher_id=" + no)) {
if (rs.next()) {
Teacher s = new Teacher();
s.setNo(rs.getInt("teacher_id"));
s.setName(rs.getString("name"));
s.setTel(rs.getString("tel"));
s.setCreatedDate(rs.getString("created_date"));
s.setEmail(rs.getString("email"));
s.setDegree(rs.getInt("degree"));
s.setSchool(rs.getString("school"));
s.setMajor(rs.getString("major"));
s.setWage(rs.getInt("wage"));
return s;
}
return null;
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public void update(Teacher t) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format(
"update app_teacher set "
+ " name='%s', tel='%s', email='%s', degree=%d,"
+ " school='%s', major='%s', wage=%d "
+ " where teacher_id=%d",
t.getName(),
t.getTel(),
t.getEmail(),
t.getDegree(),
t.getSchool(),
t.getMajor(),
t.getWage(),
t.getNo());
stmt.executeUpdate(sql);
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public boolean delete(Teacher t) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format("delete from app_teacher where teacher_id=%d", t.getNo());
return stmt.executeUpdate(sql) == 1;
} catch (Exception e) {
throw new DaoException(e);
}
}
}
package bitcamp.myapp.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import bitcamp.myapp.vo.Student;
public class JdbcStudentDao implements StudentDao {
@Override
public void insert(Student s) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format(
"insert into app_student(name, tel, pst_no, bas_addr, det_addr, work, gender, level)"
+ " values('%s','%s','%s','%s','%s',%b,'%s',%d)",
s.getName(),
s.getTel(),
s.getPostNo(),
s.getBasicAddress(),
s.getDetailAddress(),
s.isWorking(),
s.getGender(),
s.getLevel());
stmt.executeUpdate(sql);
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public Student[] findAll() {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select student_id, name, tel, work, level"
+ " from app_student"
+ " order by student_id desc")) {
ArrayList<Student> list = new ArrayList<>();
while (rs.next()) {
Student s = new Student();
s.setNo(rs.getInt("student_id"));
s.setName(rs.getString("name"));
s.setTel(rs.getString("tel"));
s.setWorking(rs.getBoolean("work"));
s.setLevel(rs.getByte("level"));
list.add(s);
}
Student[] arr = new Student[list.size()];
list.toArray(arr);
return arr;
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public Student findByNo(int no) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select student_id, name, tel, created_date, pst_no, bas_addr, det_addr, work, gender, level"
+ " from app_student"
+ " where student_id=" + no)) {
if (rs.next()) {
Student s = new Student();
s.setNo(rs.getInt("student_id"));
s.setName(rs.getString("name"));
s.setTel(rs.getString("tel"));
s.setCreatedDate(rs.getString("created_date"));
s.setPostNo(rs.getString("pst_no"));
s.setBasicAddress(rs.getString("bas_addr"));
s.setDetailAddress(rs.getString("det_addr"));
s.setWorking(rs.getBoolean("work"));
s.setGender(rs.getString("gender").charAt(0));
s.setLevel(rs.getByte("level"));
return s;
}
return null;
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public void update(Student s) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format(
"update app_student set "
+ " name='%s', tel='%s', pst_no='%s', bas_addr='%s', det_addr='%s',"
+ " work=%b, gender='%s', level=%d "
+ " where student_id=%d",
s.getName(),
s.getTel(),
s.getPostNo(),
s.getBasicAddress(),
s.getDetailAddress(),
s.isWorking(),
s.getGender(),
s.getLevel(),
s.getNo());
stmt.executeUpdate(sql);
} catch (Exception e) {
throw new DaoException(e);
}
}
@Override
public boolean delete(Student s) {
try (Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb", "study", "1111");
Statement stmt = con.createStatement()) {
String sql = String.format("delete from app_student where student_id=%d", s.getNo());
return stmt.executeUpdate(sql) == 1;
} catch (Exception e) {
throw new DaoException(e);
}
}
}
ClientApp 에서 Handler 생성자에 Jdbc----Dao 객체 주입으로 변경한다.
public class ClientApp {
public static void main(String[] args) {
new ClientApp().execute("localhost", 8888);
}
void execute(String ip, int port) {
try {
JdbcBoardDao boardDao = new JdbcBoardDao();
JdbcStudentDao studentDao = new JdbcStudentDao();
JdbcTeacherDao teacherDao = new JdbcTeacherDao();
조언
*
과제
학습
- com/eomcs/jdbc/ex0~5
'네이버클라우드 AIaaS 개발자 양성과정 1기 > DBMS, SQL, JDBC, Servlet' 카테고리의 다른 글
[비트캠프] 68일차(14주차5일) - JDBC: myapp-36~37 (0) | 2023.02.10 |
---|---|
[SQL] 예제 소스 정리 - JDBC (0) | 2023.02.09 |
[비트캠프] 66일차(14주차3일) - SQL(ER Diagram, JOIN) (0) | 2023.02.08 |
[SQL] 예제 소스 정리 - ORDER BY, JOIN, 서브 쿼리, GROUP BY, HAVING (0) | 2023.02.07 |
[비트캠프] 65일차(14주차2일) - SQL(트랜잭션, commit, rollback) (0) | 2023.02.07 |