Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[비트캠프] 67일차(14주차4일) - JDBC(JDBC API), myapp-35 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/DBMS, SQL, JDBC, Servlet

[비트캠프] 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