본문 바로가기

국비 교육

2020.10.29 일자 수업 : JDBC, JDBC Driver

 JDBC 

 

 

JDBC 와 JDBC Driver의 관계 

JDBC                          implements     MariaDB JDBC Driver

Driver                       <-------------    Driver (Driver 정보와, DB 커넥션을 관리하는 객체)

Connection               <-------------    MariaDBConnection (DBMS와 연결하는 객체)

Statement                <-------------    MariaDBStatement (DBMS에 SQL을 전달하는 객체)

PreparedStatement  <-------------    ServerSidePreparedStatement (DBMS에 SQL을 전달하는 객체)
ResultSet                  <-------------    SelectResultSet (DBMS에서 select를 실행한 결과를 한개씩 가져온다.)

DBMS Driver 객체 등록하기

Driver -> Connection -> Statement/PreparedStatement -> ResultSet 순서로 서로의 객체를 생성하기 때문에 Driver 객체 없이는 그 밑의 객체들을 모두 생성할 수 없다. 

 

Driver 클래스는 생성하여 DriveManager에게 등록해야만 Connection 객체를 요청해서 리턴받아 DBMS에 연결할 수가 있다. 다만 위에 나온 클래스들과 다른 점은 DriveManager는 드라이버에 포함된 것이 아니므로 다양한 DBMS의 드라이버에 있는 Driver 구현체를 모두 관리한다는 점이다. 

 

DriverManager에게 여러 DBMS의 Driver를 등록하면 동시에 여러 DB를 사용할 수가 있다. 다음 예제는 각 DBMS JDBC Driver에 있는 Driver 객체를 생성하여 DriverMananger에 등록하는 과정이다.

  • 각 DBMS의 JDBC 파일 안에 정의된 Driver 객체를 생성한다. 어느 경로에 위치한 Driver인지 명시하기 위해 클래스를 임포트하지 않고, 패키지 네임까지 명시한 클래스 풀네임을 사용한다.
  • DriverManger에 대해 registerDriver() 스태틱 메서드를 호출하고, 각 Driver 객체를 파라미터로 넘겨준다.
  • DriverManager에 등록된 Driver 객체를 확인하기 위해 getDriver메서드를 호출하고, 각 DBMS의 JDBC URL을 파라미터로 넘긴다. 각 DBMS의 URL 포맷은 DBMS 관련 문서를 참고한다.
import java.sql.DriverManager;

public class Exam0110 {
  
  public static void main(String[] args) {
    try {
      java.sql.Driver driver = new org.mariadb.jdbc.Driver();
      
      java.sql.Driver oracleDriver = new oracle.jdbc.driver.OracleDriver();
      
      java.sql.Driver mssqlDriver = new com.microsoft.sqlserver.jdbc.SQLServerDriver();
      
      DriverManager.registerDriver(driver);
      DriverManager.registerDriver(oracleDriver);
      DriverManager.registerDriver(mssqlDriver);
      
      java.sql.Driver driver2 = DriverManager.getDriver("jdbc:oracle:thin:");
      System.out.println(driver2);
      
      java.sql.Driver driver3 = DriverManager.getDriver("jdbc:mssql");
      System.out.println(driver2);
      
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

그러나 Driver 클래스를 들여다보면 스태틱 블록에서 객체를 자동으로 생성해서 그 객체를 자동으로 DriveManager에 등록하므로 우리가 굳이 등록하지 않아도 된다.

import java.sql.DriverManager;

public class Exam0120 {
  
  public static void main(String[] args) {
    try {
      new org.mariadb.jdbc.Driver();
      
      new oracle.jdbc.driver.OracleDriver();
      
      new com.microsoft.sqlserver.jdbc.SQLServerDriver();
      
      java.sql.Driver driver1 = DriverManager.getDriver("jdbc:mariadb://");
      System.out.println(driver1);
      
      java.sql.Driver driver2 = DriverManager.getDriver("jdbc:oracle:thin://");
      System.out.println(driver2);
      
      java.sql.Driver driver3 = DriverManager.getDriver("jdbc:sqlserver://");
      System.out.println(driver3);
      
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

또 스태틱 블록에서 스스로 객체를 생성하기 때문에 객체를 직접 생성하지 않고, 클래스를 로딩만 해줘도 스태틱 블록이 실행되어 객체를 생성하고, 그것을 자동으로 DriverManager에 등록한다. 클래스 로딩은 Class 클래스의 forName() 스태틱 메서드를 호출하고 파라미터로 클래스 풀 네임을 넘겨주면 된다.

import java.sql.DriverManager;

public class Exam0130 {
  
  public static void main(String[] args) {
    try {
      
      Class.forName("org.mariadb.jdbc.Driver");
      Class.forName("oracle.jdbc.driver.OracleDriver");
      Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
      
      java.sql.Driver driver1 = DriverManager.getDriver("jdbc:mariadb://");
      System.out.println(driver1);
      
      java.sql.Driver driver2 = DriverManager.getDriver("jdbc:oracle:thin://");
      System.out.println(driver2);
      
      java.sql.Driver driver3 = DriverManager.getDriver("jdbc:sqlserver://");
      System.out.println(driver3);
      
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 

사실 DriverManager는 Driver 구현체를 찾아서 로딩하므로 우리가 직접 클래스를 로딩해줄 필요도 없다. DriverManager가 Driver 구현체 클래스를 찾아 로딩하는 과정은 다음과 같다.

  • jdbc.drivers 시스템 프로퍼티에 등록된 클래스를 생성하여 등록한다.
  • JDBC Driver 들이 담긴 .jar 파일에서 META-INF/services/java.sql.Driver 파일을 찾는다. 이 파일에 적혀있는 클래스를 생성한다.

따라서 다음과 같이 jdbc.drivers 시스템 프로퍼티에 클래스를 직접 저장하면 그것이 DriverManager는 이것을 자신에 등록할 것이다.

import java.sql.DriverManager;

public class Exam0140 {
  
  public static void main(String[] args) {
    try {
      
      System.setProperty("jdbc.drivers", "org.mariadb.jdbc.Driver:
             oracle.jdbc.driver.OracleDriver:com.microsoft.sqlserver.jdbc.SQLServerDriver");
             
      System.out.printf("jdbc.drivers=%s\n", System.getProperty("jdbc.drivers"));
      
      java.sql.Driver driver1 = DriverManager.getDriver("jdbc:mariadb://");
      System.out.println(driver1);
      
      java.sql.Driver driver2 = DriverManager.getDriver("jdbc:oracle:thin://");
      System.out.println(driver2);
      
      java.sql.Driver driver3 = DriverManager.getDriver("jdbc:sqlserver://");
      System.out.println(driver3);
      
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

그러나 이 시스템 프로퍼티에 클래스를 저장하지 않아도, DriverManager는 각 .jar 파일에 META-INF 폴더 밑 services 폴더에 저장된 java.sql.Driver 파일에 적힌 클래스의 객체를 생성하여 스스로에 등록하므로 사실은 아무것도 하지 않아도 자동으로 이 프로젝트가 참조하는 DBMS 라이브러리의 Driver 클래스를 등록한다.

import java.sql.DriverManager;

public class Exam0141 {
  
  public static void main(String[] args) {
    try {
      
      java.sql.Driver driver1 = DriverManager.getDriver("jdbc:mariadb://");
      System.out.println(driver1);
      
      java.sql.Driver driver2 = DriverManager.getDriver("jdbc:oracle:thin://");
      System.out.println(driver2);
      
      java.sql.Driver driver3 = DriverManager.getDriver("jdbc:sqlserver://");
      System.out.println(driver3);
      
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

DBMS에 연결하기

DriverManager에게 getConnection()을 하고, jdbc의 URL을 파라미터로 넘겨주면 URL로 특정된 jdbc의 Driver에 대하여 connect() 메서드를 호출하고 Driver가 생성자를 통해 생성한 java.sql.Connection 구현체를 리턴받는다. 그리고 getConnection()을 호출한 자리로 Connection 구현체를 리턴한다.

      // [App]...................[DriverManager].............[Driver 구현체]
      // ..| getConnection()............|............................|
      // ..|--------------------------->|............................|
      // ..|............................|......connect().............|
      // ..|............................|--------------------------->|
      // ..|............................|............................|-> new Connection()
      // ..|............................|..........return............|
      // ..|........return..............|<---------------------------|
      // ..|<---------------------------|............................|
      //

 

다음 예제는 위와 같은 방식으로 DBMS와 연결된 Connection 객체를 리턴받는 과정이다.

  • DriverManager.getConnection()의 파라미터로 jdbc URL과 DBMS의 아이디, 비밀번호를 넘겨야 한다. 이 URL에서 첫번째로 connect 메서드를 호출할 Driver 객체를 명시한다. 두번째로는 해당 DBMS가 있는 IP주소와 포트번호를 명시해야 한다. 이 주소가 틀리면 연결이 불가능하다. 포트 번호는 DBMS마다 이미 지정된 번호가 있으니 참고하여 작성한다. 세번째로는 DBMS를 통해 연결할 데이터베이스 이름을 명시한다.
  • DBMS와 연결된 커넥션 객체 또한 자원이기 때문에 사용이 끝나면 꼭 닫아줘야한다. 따라서 finally를 통해 사용이 끝나면 자원을 닫아준다.
import java.sql.DriverManager;

public class Exam0210 {
  
  public static void main(String[] args) {
    java.sql.Connection con = null;
    
    try {
      con = DriverManager.getConnection(
          "jdbc:mariadb://localhost:3306/studydb",
          "study",
          "1111");
      
      System.out.println("DBMS와 연결됨!");
      
      System.out.println(con.getClass().getName());
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      try {
        con.close();
      } catch (Exception e) {
        
      }
    }
  }
}

그런데 finally를 굳이 사용하지 않아도 try / resource 문에 Connection 객체를 등록하면 try 블록 안의 코드가 모두 실행되든, 중간에 예외가 발생하든 마지막에는 자동으로 자원을 닫아준다.

public class Exam0210 {
  
  public static void main(String[] args) {
    
    try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb",
        "study",
        "1111"); ) {
      
      System.out.println("DBMS와 연결됨!");
      
      System.out.println(con.getClass().getName());
      
    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

두번째, 세번째 파라미터로 아이디와 비밀번호를 주지 않아도, 첫번째 파라미터인 URL 가장 뒤에 user, password 변수에 할당을 해주는 방식으로 메서드를 호출할 수도 있다.

import java.sql.DriverManager;

public class Exam0310 {
  
  public static void main(String[] args) {
        try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111"); ) {
      
      System.out.println("DBMS와 연결됨!");
      
      System.out.println(con.getClass().getName());
      
    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

 


DBMS에 SQL 보내기

이제 DBMS에 연결된 Connection 객체를 받았으니 이를 통해서 본격적으로 DBMS에 SQL문을 전송할 수가 있다. SQL문을 보내는 작업을 수행하는 객체는 Statement 구현체로, Connection 객체에 대하여 createStatment() 메서드를 호출하면 Statement의 새로 생성된 객체를 리턴받을 수 있다. 이 Statement 객체에 대하여 특정 메서드를 호출하면 이 객체는 전용 프로토콜에 따라 가공된 SQL문을 DBMS에 전송하여 데이터 베이스에 대한 작업을 수행한다. 다만, 이때 DML/DQL 별로 사용하는 메서드가 다르다.

DML 

executeUpdate() 메서드를 통해 DML 명령(insert, update, delete)을 DBMS에 전송할 수 있다. DML SQL문을 문자열 형태로 파라미터로 넘겨, executeUpdate를 호출하면 전용 프로토콜에 따라 가공된 SQL문을 DBMS에 전송하여 데이터 베이스에 대한 작업을 수행한 후, 변경(insert, update, delete)된 레코드의 개수를 리턴한다. 

 

다음은 insert SQL 명령을 전송한 예제이다.

import java.sql.DriverManager;

public class Exam0310 {
  
  public static void main(String[] args) {
        try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
         java.sql.Statement stmt = con.createStatement();) {
      
      System.out.println("DBMS와 연결됨!");
      
      int count = stmt.executeUpdate(
          "insert  into x_board(title, contents) values('제목10', '내용')");
      System.out.printf("%d 개 입력 성공!", count);

    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

데이터를 변경하려면 똑같이 executeUpdate를 사용하되, 파라미터로 주는 sql문을 변경한다.

import java.sql.DriverManager;

public class Exam0350 {
  
  public static void main(String[] args) {
        try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
         java.sql.Statement stmt = con.createStatement();) {
      
      int count = stmt.executeUpdate(
          "update x_board set view_count = view_count + 1 where board_id = 4");
      System.out.printf("%d 개 변경 성공!", count);

    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

데이터를 삭제하는 것도 마찬가지이다.

import java.sql.DriverManager;

public class Exam0360 {
  
  public static void main(String[] args) {
        try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
         java.sql.Statement stmt = con.createStatement();) {
      
      int count = stmt.executeUpdate(
          "delete from x_board where board_id = 6");
      System.out.printf("%d 개 삭제 성공!", count);

    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

다만, 지우고자 하는 컬럼명이 foreign key이므로 이미 특정 컬럼 값이 자식 테이블에서 참조되고 있는 경우에는 자식 테이블에서 참조하고 있는 데이터를 먼저 지워야 한다. 

package com.eomcs.jdbc.ex1;

import java.sql.DriverManager;

public class Exam0361 {
  
  public static void main(String[] args) {
        try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
         java.sql.Statement stmt = con.createStatement();) {

      int count = stmt.executeUpdate(
          "delete from x_board_file where board_id = 4");
      count = stmt.executeUpdate(
          "delete from x_board where board_id = 4");
      System.out.printf("%d 개 삭제 성공!", count);

    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

DQL

DQL 문법을 통해 데이터를 조회하는 sql문을 실행하는 메서드는 executeQuery() 메서드이다. 이것이 리턴하는 객체는 ResultSet 객체인데, 이것은 데이터 베이스가 주는 결과 그 자체가 아니라 결과를 가져오는 작업을 수행하는 객체일 뿐이다. 

 

try resource 문에서 executeQuery로 리턴받은 ResultSet 객체를 포함한다. 이에 대하여 next()라는 메서드를 호출했을 때, ResultSet이 가져온 레코드가 한 줄 있다면 true를 리턴하며 select문으로 가져온 결과 중에 가장 처음에 해당하는 레코드에 커서를 위치시킨다. 

import java.sql.DriverManager;

public class Exam0320 {

  public static void main(String[] args) {
    try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
        java.sql.Statement stmt = con.createStatement();
        java.sql.ResultSet rs = stmt.executeQuery("select * from x_board order by board_id desc");) {

      boolean isReceived = rs.next();

    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

만약에 rs.next() 메서드를 호출하여 리턴받은 값이 true일 경우, ResultSet에 대하여 숫자를 차례대로 파라미터로 넘기며 getString()를 호출하면 ResultSet이 갖는 한 레코드의 컬럼값을 순서대로 문자열 형태로 리턴한다. 

import java.sql.DriverManager;

public class Exam0320 {

  public static void main(String[] args) {
    try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
        java.sql.Statement stmt = con.createStatement();
        java.sql.ResultSet rs = stmt.executeQuery("select * from x_board order by board_id desc");) {

      boolean isReceived = rs.next();
      
      if (isReceived) {
        System.out.printf("%s, %s, %s, %s, %s\n",
            rs.getString(1),
            rs.getString(2),
            rs.getString(3),
            rs.getString(4),
            rs.getString(5));
      } else {
        System.out.println("서버에서 한 개의 레코드를 가져오지 못했다.");
      }
    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}
// 결과 !
// 7, 제목10, 내용, 2020-10-29 13:02:47.0, 0

또한 문자열 형태가 아니라 DBMS에 설정된 컬럼의 타입에 따라 값을 변환해서 받고 싶다면, 다음과 같이 해당 타입의 값을 리턴하는 메서드를 호출한다.

  • int, number : getInt();
  • char, varchar, text : getString()
  • date, datetiem : getDate()
  • float : getFloat()
      if (isReceived) {
        System.out.printf("%d, %s, %s, %s, %d\n",
            rs.getInt(1),
            rs.getString(2),
            rs.getString(3),
            rs.getDate(4),
            rs.getInt(5));
      } else {
        System.out.println("서버에서 한 개의 레코드를 가져오지 못했다.");
      }

getXXX()의 파라미터로 컬럼의 이름을 넘겨주어도 된다.

      if (isReceived) {
        System.out.printf("%d, %s, %s, %s, %d\n",
            rs.getInt("board_id"),
            rs.getString("title"),
            rs.getString("contents"),
            rs.getDate("created_date"),
            rs.getInt("view_count"));
      } else {
        System.out.println("서버에서 한 개의 레코드를 가져오지 못했다.");
      }

이제 더 이상 ResultSet 객체가 다음 레코드를 받지 못할 때까지 반복해서 next() 메서드를 호출하면, next() 메서드는 가장 처음의 레코드를 시작으로 더 이상 읽을 레코드가 없을 때까지 하나씩 커서를 옮긴다. 따라서 next()를 호출하여 true를 리턴할 때마다 getXXX() 메서드를 호출하여 각 컬럼 값을 출력하면 모든 결과 레코드의 컬럼 값을 출력할 수가 있다.

      while (rs.next()) {
        System.out.printf("%d, %s, %s, %s, %d\n",
            rs.getInt("board_id"),
            rs.getString("title"),
            rs.getString("contents"),
            rs.getDate("created_date"),
            rs.getInt("view_count"));
      }
// 결과!
// 7, 제목10, 내용, 2020-10-29, 0
// 6, 제목6, 내용, 2020-10-29, 0
// 5, 제목5, 내용, 2020-10-29, 0
// 4, 제목4, 내용, 2020-10-29, 0
// 3, 제목3, 내용, 2020-10-29, 0
// 2, 제목2, 내용, 2020-10-29, 0
// 1, 제목1, 내용, 2020-10-29, 0

사용자에게 입력 받은 데이터 저장하기

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Scanner;

public class Exam0110 {
  
  public static void main(String[] args) throws Exception {
    String title = null;
    String contents = null;
    
    try (Scanner keyScan = new Scanner(System.in)) {
      
      System.out.print("제목? ");
      title = keyScan.nextLine();
      
      System.out.print("내용? ");
      contents = keyScan.nextLine();
      
      System.out.print("입력하시겠습니까?(Y/n) ");
      String input = keyScan.nextLine();
      
      if (!input.equalsIgnoreCase("y") && input.length() != 0) {
        System.out.println("등록을 취소 하였습니다.");
        return;
      }
    }
    
    try (Connection con = DriverManager.getConnection(
        "jdbc:mysql://localhost:3306/studydb?user=study&password=1111");
        Statement stmt = con.createStatement();) {
      String sql = String.format(
          "insert into x_board(title, contents) values('%s', '%s')", title, contents);
      int count = stmt.executeUpdate(sql);
      System.out.printf("%d 개 입력 성공!", count);
    }
  }
}

사용자가 원하는 데이터 검색

import java.sql.DriverManager;
import java.util.Scanner;

public class Exam0130 {

  public static void main(String[] args) {
    int num = 0;
    
 try (Scanner keyScan = new Scanner(System.in)) {
      System.out.print("번호? ");
      num = Integer.parseInt(keyScan.nextLine());
    }
    
    try (java.sql.Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
        java.sql.Statement stmt = con.createStatement();
        java.sql.ResultSet rs = stmt.executeQuery("select * from x_board where board_id=" + num);) {
      
      if (rs.next()) {
        System.out.printf("제목 : %d\n", rs.getInt("board_id"));
        System.out.printf("내용 : %s\n", rs.getString("title"));
        System.out.printf("등록일 : %s\n", rs.getDate("created_date"));
        System.out.printf("조회수 : %d\n", rs.getInt("view_count"));
        
      } else {
        System.out.println("해당 번호의 게시물 없음");
      }
      
    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

 

사용자가 입력한 데이터 변경

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Scanner;

public class Exam0140 {
  
  public static void main(String[] args) throws Exception {
    String num = null;
    String title = null;
    String contents = null;
    
    try (Scanner keyScan = new Scanner(System.in)) {
      
      System.out.print("번호? ");
      num = keyScan.nextLine();
      
      System.out.print("제목? ");
      title = keyScan.nextLine();
      
      System.out.print("내용? ");
      contents = keyScan.nextLine();
      
      System.out.print("입력하시겠습니까?(Y/n) ");
      String input = keyScan.nextLine();
      
      if (!input.equalsIgnoreCase("y") && input.length() != 0) {
        System.out.println("변경을 취소 하였습니다.");
        return;
      }
    }
    
    try (Connection con = DriverManager.getConnection(
        "jdbc:mysql://localhost:3306/studydb?user=study&password=1111");
        Statement stmt = con.createStatement();) {
      String sql = String.format(
          "update x_board set title='%s', contents='%s' where board_id=%s", title, contents, num);
      int count = stmt.executeUpdate(sql);
      
      if (count == 0) {
        System.out.println("해당 게시물이 존재하지 않습니다.");
      } else {
        System.out.printf("%d 개 변경 성공!", count);
      }
    }
  }
}

사용자가 입력한 데이터 삭제

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Scanner;

public class Exam0150 {
  
  public static void main(String[] args) throws Exception {
    String num = null;
    
    try (Scanner keyScan = new Scanner(System.in)) {
      
      System.out.print("번호? ");
      num = keyScan.nextLine();
      
      System.out.print("삭제하시겠습니까?(Y/n) ");
      String input = keyScan.nextLine();
      
      if (!input.equalsIgnoreCase("y") && input.length() != 0) {
        System.out.println("삭제를 취소 하였습니다.");
        return;
      }
    }
    
    try (Connection con = DriverManager.getConnection(
        "jdbc:mysql://localhost:3306/studydb?user=study&password=1111");
        Statement stmt = con.createStatement();) {
      
      String sql = String.format(
          "delete from x_board_file where board_id=%s", num);
      stmt.executeUpdate(sql);
      
      sql = String.format(
          "delete from x_board where board_id=%s", num);
      int count = stmt.executeUpdate(sql);
      
      if (count == 0) {
        System.out.println("해당 게시물이 존재하지 않습니다.");
      } else {
        System.out.printf("삭제하였습니다.");
      }
    }
  }
}

SQL 삽입 공격

만약 사용자가 입력한 값을 전혀 가공하지 않고 그대로 sql문에 삽입하여 DBMS에 전송할 경우에는, 사용자가 프로그램에서 요구하는 대로 입력하지 않고, 사용자가 SQL문을 멋대로 완성하여 데이터를 완전히 왜곡시킬 가능성이 있다. 

 

예를 들어, 사용자에게 입력받은 데이터를 삭제하는 예제에서 사용자가 다음과 같이 삭제할 레코드의 번호를 입력할 때, 다음과 같이 입력할 수가 있다.

번호? 0 or board_id>0

이렇게 입력하면 프로그램은 sql문을 다음과 같이 완성시켜 곧바로 DBMS로 보내버린다.

delete from x_board where board_id=0 or board_id>0

이렇게 되면, DBMS는 board_id 값이 0보다 큰, 모든 레코드를 삭제하는 것이다.

 

또 다른 사례로는, 데이터를 변경하는 예제에서도 사용자가 다음과 같이 입력할 수도 있다.

번호? 12
제목? zzz
내용? haha', view_count=10000, created_date='1997-01-01

이렇게 입력하면 프로그램은 sql문을 다음과 같이 완성시켜 곧바로 DBMS로 보내버린다.

update x_board set title='zzz', contents='haha', view_count=10000, created_date='1997-01-01' where board_id=12

이러면 사용자가 변경할 수 없는 컬럼 값까지 사용자의 멋대로 데이터가 변경되는 문제가 발생한다.