Mybatis의 SqlMapper 구성 확장 및 응용 프로그램 상세 설명 (1)

며칠 동안 디버깅 프로그램을 분투하고 블로그를 여러 편 썼으며 마침내 Mybatis 설정의 확장 메커니즘을 세웠다.비록 확장 메커니즘이 중요하지만 진정으로 실용적인 확장 기능이 없다면 적어도 사람들의 마음을 그렇게 고무시키지 않을 것이다. 이 블로그는 몇 가지 확장의 예를 들 것이다.
이번 원본 코드를 연구하는 이유는 Oracle과 MySQL 데이터베이스의 호환성이다. 예를 들어 Oracle에서 쌍세로선을 연결부호로 사용하고 MySQL에서 CONCAT 함수를 사용한다.예를 들어 Oracle에서는 DECODE 함수를 사용할 수 있지만 MySQL에서는 표준 CASE WHEN만 사용할 수 있습니다.예를 들어 Oracle에서 DELETE FORM TABLE WHERE FIELD1 IN(SELECT FIELD1 FORM TABLE WHERE FIELD2=?)을 실행할 수 있습니다.하지만 MySQL에서 이상이 발생합니다. 등등.
다음은 이러한 호환성 문제를 해결하는 것부터 시작하여 먼저 설정에 데이터베이스 표지와 관련된 설정을 추가해야 한다.

<!--  Configuration  --> 
<bean id="mybatisConfig" class="org.dysd.dao.mybatis.schema.SchemaConfiguration"/>
<bean id="sqlSessionFactory" p:dataSource-ref="dataSource" 
class="org.dysd.dao.mybatis.schema.SchemaSqlSessionFactoryBean">
<!--  mybatis  -->
<property name="configuration" ref="mybatisConfig"/>
<!--  SqlMapper  -->
<property name="mapperLocations">
<array>
<value>classpath*:**/*.sqlmapper.xml</value>
</array>
</property>
<!--   -->
<property name="databaseIdProvider">
<bean class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
<property name="properties">
<props>
<!--  MYSQL, mysql Configuration databaseId,mybatis , Oracle DB2 -->
<prop key="MySQL">mysql</prop>
<prop key="oracle">oracle</prop>
<prop key="H2">h2</prop>
<prop key="db2">db2</prop>
</props>
</property>
</bean>
</property>
</bean>
1. 연결 문자 문제
1. SQL 구성 함수 구현 클래스 작성

public class ConcatSqlConfigFunction extends AbstractSqlConfigFunction{// order 
@Override
public String getName() {
return "concat";
}
@Override
public String eval(String databaseId, String[] args) {
if(args.length < 2){
Throw.throwException("the concat function require at least two arguments.");
}
if("mysql".equalsIgnoreCase(databaseId)){
return "CONCAT("+Tool.STRING.join(args, ",")+")";
}else{
return Tool.STRING.join(args, "||");
}
}
}
2. SchemaHandlers 클래스의 정적 코드 블록에 등록하거나 시작 초기화 클래스에서 SchemaHandlers를 호출하는 방법으로 등록

static {
// StatementHandler
register("cache-ref", new CacheRefStatementHandler());
register("cache", new CacheStatementHandler());
register("parameterMap", new ParameterMapStatementHandler());
register("resultMap", new ResultMapStatementHandler());
register("sql", new SqlStatementHandler());
register("select|insert|update|delete", new CRUDStatementHandler());
// ScriptHandler
register("trim", new TrimScriptHandler());
register("where", new WhereScriptHandler());
register("set", new SetScriptHandler());
register("foreach", new ForEachScriptHandler());
register("if|when", new IfScriptHandler());
register("choose", new ChooseScriptHandler());
//register("when", new IfScriptHandler());
register("otherwise", new OtherwiseScriptHandler());
register("bind", new BindScriptHandler());
//  
registerExtend("db", new DbStatementHandler(), new DbScriptHandler());
//  SqlConfigFunction
register(new DecodeSqlConfigFunction());
register(new ConcatSqlConfigFunction());
//  SqlConfigFunctionFactory
register(new LikeSqlConfigFunctionFactory());
}
위의 코드는 ConcatSQLConfigFunction을 등록하는 것 외에 다른 등록 코드도 있습니다. 여기에 제시된 바와 같이 다음 문장은 생략됩니다.
3. SqlMapper 구성 수정

<select id="selectString" resultType="string">
select PARAM_NAME, $concat{PARAM_CODE, PARAM_NAME} AS CODE_NAME 
from BF_PARAM_ENUM_DEF
<if test="null != paramName and '' != paramName">
where PARAM_NAME LIKE $CONCAT{'%', #{paramName, jdbcType=VARCHAR}, '%'}
</if>
</select>
4. dao 인터페이스 클래스 작성

@Repository
public interface IExampleDao {
public String selectString(@Param("paramName")String paramName);
}
5. 테스트 클래스 작성

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:spring/applicationContext.xml" 
})
@Component
public class ExampleDaoTest {
@Resource
private IExampleDao dao;
@Test
public void testSelectString(){
String a = dao.selectString(" ");
Assert.assertEquals(" ", a);
}
}
6, 각각 MySQL과 H2에서 다음과 같이 실행(mybatis 로그 레벨을 TRACE로 조정)
(1)MySQL

20161108 00:12:55,235 [main]-[DEBUG] ==> Preparing: select PARAM_NAME, CONCAT(PARAM_CODE,PARAM_NAME) AS CODE_NAME from BF_PARAM_ENUM_DEF where PARAM_NAME LIKE CONCAT('%',?,'%') 
20161108 00:12:55,269 [main]-[DEBUG] ==> Parameters:  (String)
20161108 00:12:55,287 [main]-[TRACE] <== Columns: PARAM_NAME, CODE_NAME
20161108 00:12:55,287 [main]-[TRACE] <== Row:  , DISPLAY_AREA 
20161108 00:12:55,289 [main]-[DEBUG] <== Total: 1
(2)H2

20161108 00:23:08,348 [main]-[DEBUG] ==> Preparing: select PARAM_NAME, PARAM_CODE||PARAM_NAME AS CODE_NAME from BF_PARAM_ENUM_DEF where PARAM_NAME LIKE '%'||?||'%' 
20161108 00:23:08,364 [main]-[DEBUG] ==> Parameters:  (String)
20161108 00:23:08,411 [main]-[TRACE] <== Columns: PARAM_NAME, CODE_NAME
20161108 00:23:08,411 [main]-[TRACE] <== Row:  , DISPLAY_AREA 
20161108 00:23:08,411 [main]-[DEBUG] <== Total: 1
연결 문자의 호환성 문제가 해결된 것을 볼 수 있습니다.
또한 LIKE 키워드를 사용할 때 쓰기가 번거롭다는 것을 발견했습니다. 그러면 새로운 SQL 구성 함수 세트를 드리겠습니다.

public class LikeSqlConfigFunctionFactory implements ISqlConfigFunctionFactory{
@Override
public Collection<ISqlConfigFunction> getSqlConfigFunctions() {
return Arrays.asList(getLeftLikeSqlConfigFunction(),getRightLikeSqlConfigFunction(),getLikeSqlConfigFunction());
}
private ISqlConfigFunction getLeftLikeSqlConfigFunction(){
return new AbstractLikeSqlConfigFunction(){
@Override
public String getName() {
return "llike";
}
@Override
protected String eval(String arg) {
return "LIKE $concat{'%',"+arg+"}";
}
};
}
private ISqlConfigFunction getRightLikeSqlConfigFunction(){
return new AbstractLikeSqlConfigFunction(){
@Override
public String getName() {
return "rlike";
}
@Override
protected String eval(String arg) {
return "LIKE $concat{"+arg+", '%'}";
}
};
}
private ISqlConfigFunction getLikeSqlConfigFunction(){
return new AbstractLikeSqlConfigFunction(){
@Override
public String getName() {
return "like";
}
@Override
protected String eval(String arg) {
return "LIKE $concat{'%',"+arg+", '%'}";
}
};
}
private abstract class AbstractLikeSqlConfigFunction extends AbstractSqlConfigFunction{
@Override
public String eval(String databaseId, String[] args) {
if(args.length != 1){
Throw.throwException("the like function require one and only one argument.");
}
return eval(args[0]);
}
protected abstract String eval(String arg);
}
}
여기에는 왼쪽 유사, 오른쪽 유사, 중간 유사 일치, 그리고 SQL 구성 함수 세트가 정의되어 있습니다.따라서 SqlMapper의 구성 파일은 다음과 같이 단순화됩니다.

<select id="selectString" resultType="string">
select PARAM_NAME, $concat{PARAM_CODE, PARAM_NAME} AS CODE_NAME 
from BF_PARAM_ENUM_DEF
<if test="null != paramName and '' != paramName">
where PARAM_NAME $like{#{paramName, jdbcType=VARCHAR}}
</if>
</select>
실행 결과가 완전히 같습니다.
만약 아직도 귀찮다면, PARAM_NAME와 paramName은 낙타 봉우리식으로 대응하고 필드라이크 함수를 추가하여 설정을

where $fieldLike{#{PARAM_NAME, jdbcType=VARCHAR}}
데이터 사전을 결합하면 jdbcType 구성도 자동으로 생성됩니다.

where $fieldLike{#{PARAM_NAME}}
이 경우 여러 개의 매개 변수가 있어도 잘못된 뜻이 나타나지 않습니다. (또는 구성 함수 $likes{}를 새로 정의하여 잘못된 뜻을 없애는 것) 여러 조건을 간소화할 수 있습니다.

where $likes{#{PARAM_NAME, PARAM_NAME2, PARAM_NAME3}}
물론 발굴할 수 있는 간소화는 호환성 범주에 그치지 않고 더 이상 전개되지 않는다.
둘째, DECODE 함수/CASE...WHEN
Oracle의 DECODE 함수는 다음과 같이 간단합니다.
DECODE(조건, 값 1, 반환값 1, 값 2, 반환값 2,...값 n, 반환값 n[, 기본값])
등가의 표준 표기법:

CASE  
WHEN  1 THEN  1
WHEN  2 THEN  2
...
WHEN  n THEN  n
[ELSE  ]
END
이제 $decode 구성 함수를 구현합니다.

public class DecodeSqlConfigFunction extends AbstractSqlConfigFunction{
@Override
public String getName() {
return "decode";
}
@Override
public String eval(String databaseId, String[] args) {
if(args.length < 3){
Throw.throwException("the decode function require at least three arguments.");
}
if("h2".equalsIgnoreCase(databaseId)){// , h2 oracle, oracle
return "DECODE("+Tool.STRING.join(args, ",")+")";
}else{
StringBuffer sb = new StringBuffer();
sb.append("CASE ").append(args[0]);
int i=2, l = args.length;
for(; i < l; i= i+2){
sb.append(" WHEN ").append(args[i-1]).append(" THEN ").append(args[i]);
}
if(i == l){// , 
sb.append(" ELSE ").append(args[l-1]);
}
sb.append(" END");
return sb.toString();
}
}
}
그런 다음 SchemaHandlers를 사용하여 등록하고 SqlMapper에서 구성을 수정합니다.

<select id="selectString" resultType="string">
select PARAM_NAME, $decode{#{paramName}, '1', 'A', '2', 'B','C'} AS DECODE_TEST 
from BF_PARAM_ENUM_DEF
<if test="null != paramName and '' != paramName">
where PARAM_NAME $like{#{paramName, jdbcType=VARCHAR}}
</if>
</select>
테스트는 다음과 같습니다.
(1) H2에서 (Oracle 대신 H2)

20161108 06:53:29,747 [main]-[DEBUG] ==> Preparing: select PARAM_NAME, DECODE(?,'1','A','2','B','C') AS DECODE_TEST from BF_PARAM_ENUM_DEF where PARAM_NAME LIKE '%'||?||'%'
(2) MySQL

20161108 06:50:55,998 [main]-[DEBUG] ==> Preparing: select PARAM_NAME, CASE ? WHEN '1' THEN 'A' WHEN '2' THEN 'B' ELSE 'C' END AS DECODE_TEST from BF_PARAM_ENUM_DEF where PARAM_NAME LIKE '%'||?||'%'
위에서 말한 것은 편집자가 여러분께 소개한 Mybatis의 SqlMapper 설정의 확장과 응용에 대한 상세한 소개(1)입니다. 여러분께 도움이 되었으면 합니다. 궁금한 점이 있으면 저에게 메시지를 남겨 주십시오. 편집자는 제때에 답장을 드리겠습니다.여기에서도 저희 사이트에 대한 지지에 감사드립니다!

좋은 웹페이지 즐겨찾기