SQL 매퍼 프레임워크 구현과 관련해서 Application 클래스, Executor 클래스, Shop 클래스, SqlMapper 클래스를 작성한다. 


Application 클래스는 JDBC 인터페이스를 사용해서 데이터를 조회한다.

Executor 클래스는 Application 객체를 생성한 다음 view( ) 메소드를 호출한다.

이때 Shop 클래스를 리절트 객체로 사용한다.

SqlMapper 클래스는 Application 클래스의 부모 클래스로 개선이 필요한 소스 코드를 추출해서 메소드로 작성한다.


먼저 Shop 클래스를 작성한 다음 Application 클래스Executor 클래스를 순차적으로 작성한다. 

SqlMapper 클래스는 기능 구현에 따라 단계적으로 작성한다.


1. Shop 클래스 생성 경로 : /chapter01/src/org/mybatis/domain/Shop.java


shopNo;

shopName;

shopLocation;

shopStatus;

2. SHOP 테이블에 정의한 컬럼을 기준으로 프로퍼티를 선언한다. 프로퍼티명은 컬럼명을 낙타 표기 형식에 맞추어 변환한 문자열을 사용한다. 


int shopNo;

String shopName;

String shopLocation;

String shopStatus;

3. 프로퍼티를 선언한 다음 컬럼 타입에 적합한 자바 타입을 프로퍼티 타입으로 선언한다. 컬럼 타입이 숫자 타입인 경우 int 타입 또는 Integer 타입을 프로퍼티 타입으로 선언하고, 문자 타입인 경우 String 타입을 프로퍼티 타입으로 선언한다.


/* 접근 제한자 */

private int shopNo;

private String shopName;

private String shopLocation;

private String shopStatus;


/* 셋터 메소드 */

public void setShopNo(int shopNo){

this.shopNo = shopNo;

}


/* 겟터 메소드 */

public int getShopNo( ){

return this.shopNo;

}

4. 선언한 프로퍼티 값을 직접 변경하지 못하도록 접근 제한자를 지정한 다음 셋터 메소드와 겟터 메소드를 작성한다.


private int shopNo;

private String shopName;

private String shopLocation;

private String shopStatus;


/* 기본 생성자 */

public Shop( ) {


}


/* 생성자 */

public Shop(int shopNo, String shopName, String shopLocation, String shopStatus){

this.shopNo = shopNo;

this.shopName = shopName;

this.shopLocation = shopLocation;

this.shopStatus = shopStatus;

}

5. 두 개의 생성자를 작성한다. 하나는 기본 생성자이고, 다른 하나는 모든 프로퍼티를 인자로 지정한 생상자다.

* 생성자는 클래스명과 이름이 동일하고, 반환 타입이 생략된 메소드를 말한다. 특히 생성자 가운데 인자가 없는 생성자를 기본 생성자라고 한다.


import java.io.Serializable;


public class Shop implements Serializable {


}

6. Serializable 인터페이스를 지정한다.


Application 클래스를 작성한다. JDBC 인터페이스 호출 순서를 따라 작성한 Application 클래스는 데이터베이스로부터 조건에 맞는 데이터를 한 건 조회한다. Application 클래스는 단계적인 리팩토링 작업을 통해서 점진적으로 작성한다. Application 클래스를 작성할 때 Shop 클래스를 리절트 객체로 사용한다.


7. Application 클래스 생성 경로 : /chapter01/src/step0/Application.java


import java.sql.SQLException;

import java.util.List;


public class Application {

public void view(List<Object> parameters) throws SQLException {

try {

// JDBC 드라이버 로딩

Class.forName("oracle.jdbc.driver.OracleDriver");

} catch (ClassNotFoundException e) {

e.printStackTrace( );

}

}

}

8. view( ) 메소드를 정의한다. view( ) 메소드에 Class 클래스의 forName( ) 메소드를 사용해서 JDBC 드라이버를 로딩하는 소스 코드를 작성한다. forName( ) 메소드를 호출할 때 패키지 경로를 포함한 (오라클 데이터베이스의) JDBC 드라이버 클래스를 인자로 전달한다. 빌드 경로에서 JDBC 드라이버 클래스를 찾지 못하면 ClassNotFoundException 예외가 발생한다. 이런 경우에 try ~ catch 문을 사용해서 예외를 처리한다. 


import java.sql.Connection;

import java.sql.DriverManager;


public class Application {

public void view(List<Object> parameters) throws SQLException {

Connection connection = null;


try {

// JDBC 드라이버 로딩

Class.forName("oracle.jdbc.driver.OracleDriver");


// 데이터베이스 연결

connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "mybatis", "mybatis");

} catch (ClassNotFoundException e) {

throw new RuntimeException(e);

}

}

}

9. JDBC 드라이버를 로딩한 다음 DriverManager 클래스의 getConnection( ) 메소드를 호출하면, Connection 객체를 반환받을 수 있다. getConnection( ) 메소드를 호출할 때 데이터베이스 연결 URL, 계정, 비밀번호를 인자로 지정한다.


import java.sql.PreparedStatement;


public class Application {

public void view(List<Object> parameters) throws SQLException {

Connection connection = null;

PreparedStatement preparedStatement = null;


try {

...


// 데이터베이스 연결

connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "mybatis", "mybatis");


// 쿼리문 처리 객체 생성

preparedStatement = connection.prepareStatement(

"SELECT SHOP_NO, SHOP_NAME, SHOP_LOCATION, SHOP_STATUS "

 + "FROM SHOP WHERE SHOP_NO = ? AND SHOP_STATUS = ?");


// 파라미터 바인딩

preparedStatement.setInt(1, (Integer) parameters.get(0));

preparedStatement.setString(2, String.valueOf(parameters.get(1));

} catch (ClassNotFoundException e) {

throw new RuntimeException(e);

}

}

}

10. 반환된 Connection 객체의 preparedStatement( ) 메소드를 호출하면, PreparedStatement 객체를 반환받을 수 있다. PreparedStatement에서 제공하는 다양한 셋터 메소드를 사용해서 매개 변수를 지정한 쿼리문에 파라미터를 바인딩할 수 있다. 매개 변수의 컬럼 타입이 숫자 타입인 경우 setInt( ) 메소드를 사용하고, 문자 타입인 경우 setString( ) 메소드를 사용한다. 셋터 메소드의 인자로 매개 변수 순번과 파라미터를 전달한다.


import java.sql.ResultSet;


public class Application {

public void view(List<Object> parameters) throws SQLException {

...

PreparedStatement preparedStatement = null;

ResultSet resultSet = null;


try {

...


// 파라미터 바인딩

preparedStatement.setInt(1, (Integer) parameters.get(0));

preparedStatement.setString(2, String.valueOf(parameters.get(1));


// 쿼리문 실행 및 결과 반환

resultSet = preparedStatement.executeQuery( );

} catch (ClassNotFoundException e) {

throw new RuntimeException(e);

}

}

}

11. PreparedStatement 객체의 executeQuery( ) 메소드를 호출하면, 조회 쿼리문을 실행한 다음 결과를 담은 ResultSet 객체를 반환받을 수 있다.


import org.mybatis.domain.Shop;


public class Application {

public Shop view(List<Object> parameters) throws SQLException {

...

ResultSet resultSet = null;

Shop shop = null;


try {

...


// 쿼리문 실행 및 결과 반환

resultSet = preparedStatement.executeQuery( );


// 결과 처리

if(resultSet.next()){

shop = new Shop( );


shop.setShopNo(resultSet.getInt("SHOP_NO"));

shop.setShopName(resultSet.getString("SHOP_NAME"));

shop.setShopLocation(resultSet.getString("SHOP_LOCATION"));

shop.setShopStatus(resultSet.getString("SHOP_STATUS"));

}

} catch (ClassNotFoundException e) {

throw new RuntimeException(e);

}


return shop;

}

}

12. 조회 결과는 ResultSet 객체에 행과 열 형태로 담겨 반환된다. ResultSet 객체의 next( ) 메소드를 호출하면, 각 행에 순차적으로 접근할 수 있다. 행에 존재하는 각 열의 값은 다양한 겟터 메소드를 사용해서 가져올 수 있다. 열의 값이 숫자 타입인 경우 getInt( ) 메소드를 사용하고, 문자 타입인 경우 getString( ) 메소드를 사용한다. 겟터 메소드의 인자로 컬럼명을 지정한다.


public class Application {

public Shop view(List<Object> parameters) throws SQLException {

...


try {

...

}

} catch (ClassNotFoundException e) {

throw new RuntimeException(e);

} finally {

// 자원 및 데이터베이스 연결 해제

if(resultSet!=null){

resultSet.close();

}

if(preparedStatement!=null){

preparedStatement.close();

}

if(connection!=null){

connection.close();

}

}


return shop;

}

}

13. try ~ catch 문에 finally 문을 추가한 다음 사용한 자원 및 데이터베이스 연결을 해제한다. 객체 생성 순서의 역순으로 자원 및 데이터베이스 연결 객체의 close( ) 메소드를 호출한다.


Executor 클래스를 작성한다. Executor 클래스는 main( ) 메소드 실행을 통해서 Application 객체를 생성한 다음 view( ) 메소드를 호출한다.


14. Executor 클래스 생성 경로 : /chapter01/src/step0/Executor.java


import java.util.ArrayList;

import java.util.List;


public class Executor {

public static void main(String[] args){

// 파라미터 객체 생성 및 파라미터 등록

List<Object> parameters = new ArrayList<Object>( );

parameters.add(1);

parameters.add("Y");

}

}

15. main( ) 메소드를 작성한다. main( ) 메소드에 Application 객체의 view( ) 메소드를 호출할 때 필요한 파라미터 객체를 준비한다.


import org.mybatis.domain.Shop;


public class Executor {

public static void main(String[] args){

// 파라미터 객체 생성 및 파라미터 등록

List<Object> parameters = new ArrayList<Object>( );

parameters.add(1);

parameters.add("Y");


// 조회 쿼리문 실행 및 결과 반환

Application application = new Application();

try {

Shop shop = application.view(parameters);

} catch (SQLException e) {

e.printStackTrace();

}


}

}

16. Application 객체를 생성한 다음 view( ) 메소드를 호출한다. 준비한 파라미터 객체를 인자로 전달한다. Application 객체의 view( ) 메소드를 호출할 때 에러가 발생할 수 있다. try ~ catch 문을 사용해서 예외를 처리한다.


Application 클래스는 JDBC 인터페이스 호출 순서에 맞추어 작성한 전형적인 JDBC 인터페이스 호출 순서에 맞추어 작성한 전형적인 JDBC 프로그래밍이다. Application 클래스는 Executor 클래스의 main( ) 메소드 호출을 통해서 실행된다.


JDBC 프로그래밍 단점을 고스란히 가진 Application 클래스를 개선하는 데 필요한 SqlMapper 클래스를 작성한다. SqlMapper 클래스를 완성하면, Application 클래스는 좀 더 간결하고 유연한 구조를 가질 수 있다. 


SQLMapper 클래스에 구현할 기능

1. 중복된 기능 추출

2. 외부 파일 정의

3. 인자와 결과를 자동 처리

4. 단순한 실행 메소드 정의


+ Recent posts