mybatis를 통한 Select, Insert, Delete

과거에 mybatis 라이브러리 사용하기라는 제목으로 mybatis를 이용하기 위한 프로젝트 설정과 간단한 SELECT 문을 호출해 그 결과를 살펴보았습니다. 이번에 실제 프로젝트에 mybatis를 사용하기로 결정하여 좀더 구체적인 SQL 문을 호출하는 API를 파악하여 정리해 봅니다.

먼저 사용하는 테이블의 구조는 다음과 같습니다. (헐! 그림 대따 크게 표시되네.. -_-;)

앞서 언급해 드린 mybatis 라이브러리 사용하기를 통해 프로젝트를 구성하고, SQL 문을 정의하는 sql.xml 파일을 아래처럼 구성합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
<select id="getFirstPlayerName" resultType="string">
SELECT name FROM players LIMIT 1
</select>
<select id="getFirstPlayer" resultType="map">
SELECT name, age FROM players LIMIT 1
</select>
<select id="getPlayers" resultType="map">
SELECT name, age FROM players
</select>
<select id="getPlayersWhere" resultType="map">
SELECT name, age FROM players WHERE age > #{age}
</select>
<select id="getPlayersWhere2" resultType="map">
SELECT name, age FROM players WHERE age > #{age} AND name LIKE #{name}
</select>
<insert id="insertPlayer">
INSERT INTO players (name, age) VALUES (#{name}, #{age})
</insert>
<delete id="deletePlayerWhere">
DELETE FROM players WHERE name = #{name}
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="test"> <select id="getFirstPlayerName" resultType="string"> SELECT name FROM players LIMIT 1 </select> <select id="getFirstPlayer" resultType="map"> SELECT name, age FROM players LIMIT 1 </select> <select id="getPlayers" resultType="map"> SELECT name, age FROM players </select> <select id="getPlayersWhere" resultType="map"> SELECT name, age FROM players WHERE age > #{age} </select> <select id="getPlayersWhere2" resultType="map"> SELECT name, age FROM players WHERE age > #{age} AND name LIKE #{name} </select> <insert id="insertPlayer"> INSERT INTO players (name, age) VALUES (#{name}, #{age}) </insert> <delete id="deletePlayerWhere"> DELETE FROM players WHERE name = #{name} </insert> </mapper>
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="test">
    <select id="getFirstPlayerName" resultType="string">
        SELECT name FROM players LIMIT 1
    </select>
    
    <select id="getFirstPlayer" resultType="map">
        SELECT name, age FROM players LIMIT 1
    </select>
    
    <select id="getPlayers" resultType="map">
        SELECT name, age FROM players
    </select>
    
    <select id="getPlayersWhere" resultType="map">
        SELECT name, age FROM players WHERE age > #{age}
    </select>
    
    <select id="getPlayersWhere2" resultType="map">
        SELECT name, age FROM players WHERE age > #{age} AND name LIKE #{name}
    </select>
    
    <insert id="insertPlayer"> 
    	INSERT INTO players (name, age) VALUES (#{name}, #{age}) 
    </insert>
    
    <delete id="deletePlayerWhere"> 
    	DELETE FROM players WHERE name = #{name} 
    </insert>    
</mapper>

보시는 것처럼 SELECT 문에 대한 getFirstPlayerName, getFirstPlayer, getPlayers, getPlayersWhere, getPlayersWhere2과 INSERT 문에 대한 insertPlayer, DELETE 문에 대한 deletePlayerWhere가 정의되어 있습니다. 이 7개에 대한 SQL문을 실행하는 API의 설명을 통해 mybatis에서 SELECT, INSERT, DELETE 문의 호출을 정리해 보겠습니다.

먼저 getFirstPlayerName은 테이블의 첫번째 Row에 대한 name 필드값을 가져오는 것인데, 관련된 API는 다음과 같습니다. 반환값이 string이고 반환결과는 1건 이하이므로 String과 SqlSession 객체의 selectOne 함수를 사용합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
String result = session.selectOne("test.getFirstPlayerName");
// 홍길동
String result = session.selectOne("test.getFirstPlayerName"); // 홍길동
String result = session.selectOne("test.getFirstPlayerName");
// 홍길동

다음은 getFirstPlayer인데, 반환결과는 역시 1건 이하인데 반환 필드가 name과 age로 2개입니다. API는 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Map<string, object=""> result = session.selectOne("test.getFirstPlayer");
// {name=홍길동, age=18}
</string,>
Map<string, object=""> result = session.selectOne("test.getFirstPlayer"); // {name=홍길동, age=18} </string,>
Map result = session.selectOne("test.getFirstPlayer");
 // {name=홍길동, age=18}

다음은 getPlayers로 반환 결과는 0건 이상이며 API는 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<Map<String, Object>> result = session.selectList("test.getPlayers");
// [{name=홍길동, age=18}, {name=임꺽정, age=32}, {name=일지매, age=21}]
List<Map<String, Object>> result = session.selectList("test.getPlayers"); // [{name=홍길동, age=18}, {name=임꺽정, age=32}, {name=일지매, age=21}]
List<Map<String, Object>> result = session.selectList("test.getPlayers");
// [{name=홍길동, age=18}, {name=임꺽정, age=32}, {name=일지매, age=21}]

getPlayersWhere는 조건 결과가 지정된 것으로 사용하는 API는 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
List<Map<String, Object>> result = session.selectList("test.getPlayersWhere", 20);
// [{name=임꺽정, age=32}, {name=일지매, age=21}]
List<Map<String, Object>> result = session.selectList("test.getPlayersWhere", 20); // [{name=임꺽정, age=32}, {name=일지매, age=21}]
List<Map<String, Object>> result = session.selectList("test.getPlayersWhere", 20);
// [{name=임꺽정, age=32}, {name=일지매, age=21}]

getPlayersWhere2는 getPlayersWhere처럼 조건이 지정되어 있지만 비교 조건으로 사용되는 변수가 2개입니다. 사용하는 API는 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Map<string, object=""> param = new HashMap<>();
param.put("age", 20);
param.put("name", "일%");
List<Map<String, Object>> result = session.selectList("test.getPlayersWhere2", param);
// [{name=일지매, age=21}]
</string,>
Map<string, object=""> param = new HashMap<>(); param.put("age", 20); param.put("name", "일%"); List<Map<String, Object>> result = session.selectList("test.getPlayersWhere2", param); // [{name=일지매, age=21}] </string,>
Map param = new HashMap<>(); 
param.put("age", 20); 
param.put("name", "일%");

List<Map<String, Object>> result = session.selectList("test.getPlayersWhere2", param);
// [{name=일지매, age=21}]

insertPlayer는 데이터를 추가하는 것으로 API는 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Map<String, Object> param = new HashMap<>();
param.put("age", 41);
param.put("name", "황장군");
int result = session.insert("test.insertPlayer", param); // 1
session.commit();
Map<String, Object> param = new HashMap<>(); param.put("age", 41); param.put("name", "황장군"); int result = session.insert("test.insertPlayer", param); // 1 session.commit();
Map<String, Object> param = new HashMap<>(); 
param.put("age", 41); 
param.put("name", "황장군");

int result = session.insert("test.insertPlayer", param); // 1
session.commit();

deletePlayerWhere는 지정된 조건과 매칭되는 데이터를 삭제하는 것으로 사용하는 API는 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Map<String, Object> param = new HashMap<>();
param.put("name", "황장군");
int result = session.delete("test.deletePlayerWhere", param); // 1
session.commit();
Map<String, Object> param = new HashMap<>(); param.put("name", "황장군"); int result = session.delete("test.deletePlayerWhere", param); // 1 session.commit();
Map<String, Object> param = new HashMap<>(); 
param.put("name", "황장군");

int result = session.delete("test.deletePlayerWhere", param);  // 1
session.commit();

mybatis는 별도의 xml 파일을 통해 SQL 문의 정의 부분을 독립적으로 유지할 수 있으며, SQL문을 보다 유연하게 처리할 수 있다는 문법 구문을 제공합니다. 또한 mybatis는 데이터베이스 연결시 발생하는 부하를 줄이기 위한 Connection Pool을 자체적으로 제공하고 있습니다. 이외에 JDBC를 직접 사용함에 따라 발생하는 복잡한 try .. catch .. finally 코드 부분이 제거되어 코드의 가독성이 향상됩니다.

dat.GUI의 API 정리

dat.GUI는 값을 GUI를 통해 직관적으로 변경하여 기능을 테스트해 볼 수 있는 javascript 기반의 매우 직관적인 라이브러리입니다.

dat.gui를 js에서 사용하기 위해 간단히 CDN을 통해 아래처럼 페이지에 스크립트를 포함할 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.min.js"></script>

아래는 dat.GUI의 가장 간단한 예제입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var MyData = function() {
this.dataString = "Dip2K";
this.dataNumber = 0.7;
this.dataBoolean = false;
this.dataFunction = function() {
alert(
"dataString: " + this.dataString + "\n" +
"dataNumber: " + this.dataNumber + "\n" +
"dataBoolean: " + this.dataBoolean
);
};
};
window.onload = function() {
var myData = new MyData();
var gui = new dat.GUI();
gui.add(myData, "dataString");
gui.add(myData, "dataNumber");
gui.add(myData, "dataBoolean");
gui.add(myData, "dataFunction");
}
var MyData = function() { this.dataString = "Dip2K"; this.dataNumber = 0.7; this.dataBoolean = false; this.dataFunction = function() { alert( "dataString: " + this.dataString + "\n" + "dataNumber: " + this.dataNumber + "\n" + "dataBoolean: " + this.dataBoolean ); }; }; window.onload = function() { var myData = new MyData(); var gui = new dat.GUI(); gui.add(myData, "dataString"); gui.add(myData, "dataNumber"); gui.add(myData, "dataBoolean"); gui.add(myData, "dataFunction"); }
var MyData = function() {
    this.dataString = "Dip2K";
    this.dataNumber = 0.7;
    this.dataBoolean = false;
    this.dataFunction = function() {
        alert(
            "dataString: " + this.dataString + "\n" +
            "dataNumber: " + this.dataNumber + "\n" +
            "dataBoolean: " + this.dataBoolean
        );
    };
};

window.onload = function() {
    var myData = new MyData();
        
    var gui = new dat.GUI();
   
    gui.add(myData, "dataString");
    gui.add(myData, "dataNumber");
    gui.add(myData, "dataBoolean");
    gui.add(myData, "dataFunction");
}

관리하고자 하는 데이터는 함수에 대한 new 호출을 통한 this에 바인딩되어져야 합니다. 위의 코드를 실행해 보면 아래와 UI가 페이지의 우측 상단에 표시됩니다.

[xyz-ihs snippet=”DAT-GUI-1″]

위의 UI에서 보는 것처럼 함수의 new 호출로 this에 바인딩된 dataString, dataNumber, dataBoolean, dataFunction은 각각의 데이터 타입에 따라 적절한 UI로 표시되며 사용자가 쉽게 변경할 수 있습니다. function 타입의 데이터는 버튼 UI로 표시되며 버튼 클릭시에 해당 함수가 호출됩니다.

이 정도만 되어도 dat.gui를 적절하게 활용할 수 있지만, 여기에 덧붙여 변수값에 대한 제약 조건의 지정 등 다양한 활용이 가능합니다.

예를 들어, dataString 변수에 대해서 입력받을 수 있는 문자열을 Dip2K, James, Anold로 제한하고 싶다면 다음처럼 코드를 작성하면 됩니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
gui.add(myData, "dataString", ["Dip2K", "James", "Anold"]);
gui.add(myData, "dataString", ["Dip2K", "James", "Anold"]);
gui.add(myData, "dataString", ["Dip2K", "James", "Anold"]);

dataNumber에 대해서 0~100 사이의 값만을 입력받으며 0.1 단위로 값의 증가, 감소하고자 한다면 아래처럼 코드를 작성하면 되구요.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
gui.add(myData, "dataNumber", 0, 100).step(0.1);
gui.add(myData, "dataNumber", 0, 100).step(0.1);
gui.add(myData, "dataNumber", 0, 100).step(0.1);

dataNumber가 비록 수치 타입이지만, 항목으로써 선택 받도록 하고 각 항목에 대한 수치값을 지정하는 방식도 가능한데, 아래와 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
gui.add(myData, "dataNumber", { A: 0, B:50, C:100 });
gui.add(myData, "dataNumber", { A: 0, B:50, C:100 });
gui.add(myData, "dataNumber", { A: 0, B:50, C:100 });

색상값에 대한 직관적인 입력도 가능합니다. 그 예는 아래와 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var MyData = function() {
this.dataString = "Dip2K";
this.dataNumber = 50;
this.dataBoolean = false;
this.dataColor1 = "#ff0000";
this.dataColor2 = [ 0, 255, 0 ];
this.dataColor3 = { h: 350, s: 0.5, v: 0.7 };
this.dataFunction = function() {
alert(
"dataString: " + this.dataString + "\n" +
"dataNumber: " + this.dataNumber + "\n" +
"dataBoolean: " + this.dataBoolean + "\n" +
"dataColor1: " + this.dataColor1 + "\n" +
"dataColor2: " + this.dataColor2 + "\n" +
"dataColor3: " + this.dataColor3
);
};
};
window.onload = function() {
var myData = new MyData();
var gui = new dat.GUI();
gui.add(myData, "dataString", ["Dip2K", "James", "Anold"]);
gui.add(myData, "dataNumber", 0, 100).step(0.1);
gui.add(myData, "dataBoolean");
gui.addColor(myData, "dataColor1");
gui.addColor(myData, "dataColor2");
gui.addColor(myData, "dataColor3");
gui.add(myData, "dataFunction");
}
var MyData = function() { this.dataString = "Dip2K"; this.dataNumber = 50; this.dataBoolean = false; this.dataColor1 = "#ff0000"; this.dataColor2 = [ 0, 255, 0 ]; this.dataColor3 = { h: 350, s: 0.5, v: 0.7 }; this.dataFunction = function() { alert( "dataString: " + this.dataString + "\n" + "dataNumber: " + this.dataNumber + "\n" + "dataBoolean: " + this.dataBoolean + "\n" + "dataColor1: " + this.dataColor1 + "\n" + "dataColor2: " + this.dataColor2 + "\n" + "dataColor3: " + this.dataColor3 ); }; }; window.onload = function() { var myData = new MyData(); var gui = new dat.GUI(); gui.add(myData, "dataString", ["Dip2K", "James", "Anold"]); gui.add(myData, "dataNumber", 0, 100).step(0.1); gui.add(myData, "dataBoolean"); gui.addColor(myData, "dataColor1"); gui.addColor(myData, "dataColor2"); gui.addColor(myData, "dataColor3"); gui.add(myData, "dataFunction"); }
var MyData = function() {
    this.dataString = "Dip2K";
    this.dataNumber = 50;
    this.dataBoolean = false;

    this.dataColor1 = "#ff0000";
    this.dataColor2 = [ 0, 255, 0 ];
    this.dataColor3 = { h: 350, s: 0.5, v: 0.7 };

    this.dataFunction = function() {
        alert(
            "dataString: " + this.dataString + "\n" +
            "dataNumber: " + this.dataNumber + "\n" +
            "dataBoolean: " + this.dataBoolean + "\n" +
            "dataColor1: " + this.dataColor1 + "\n" +
            "dataColor2: " + this.dataColor2 + "\n" +
            "dataColor3: " + this.dataColor3
        );
    };
};

window.onload = function() {
    var myData = new MyData();

    var gui = new dat.GUI();

    gui.add(myData, "dataString", ["Dip2K", "James", "Anold"]);
    gui.add(myData, "dataNumber", 0, 100).step(0.1);
    gui.add(myData, "dataBoolean");

    gui.addColor(myData, "dataColor1");
    gui.addColor(myData, "dataColor2");
    gui.addColor(myData, "dataColor3");
            
    gui.add(myData, "dataFunction");
}

그런데 만약, 값의 변경이 dat.gui가 아닌 다른 곳에서 이루어진 다면, 변경된 값을 dat.gui에서 반영해 줘야 할 것입니다. 이는 매우 간단합니다. 즉, 아래처럼 listen을 호출해 주면 됩니다. 이 listen 함수는 타이머를 생성하고 이 타이머에서 값의 변경 여부를 검사하여 dat.gui에 반영해주게 됩니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
gui.add(myData, "dataNumber").listen();
gui.add(myData, "dataNumber").listen();
gui.add(myData, "dataNumber").listen();

끝으로 dat.gui를 통한 값의 변경이 발생하면, 이에 대한 이벤트가 호출되도록 할 수 있는데, 그 예는 아래와 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
gui.add(myData, "dataNumber").onChange(
function(v) {
//alert(v);
}).onFinishChange(
function(v) {
alert(v);
});
gui.add(myData, "dataNumber").onChange( function(v) { //alert(v); }).onFinishChange( function(v) { alert(v); });
gui.add(myData, "dataNumber").onChange(
    function(v) {
        //alert(v);
    }).onFinishChange(
    function(v) {
        alert(v);
    });

onChange는 값 변경 중의 매 순간 발생하는 이벤트이고, onFinishChange는 최종적인 값의 변경이 발생할 때 호출되는 이벤트입니다.

이외에도 dat.gui는 설정된 값을 localStorage에 저장할 수 있는 기능도 있다는 것도 알아두시면 유용할듯합니다.