Mybatis如何實(shí)現(xiàn)@Select等注解動(dòng)態(tài)組合SQL語(yǔ)句
一、背景說(shuō)明
由于以前在項(xiàng)目中一直使用sqlmap.xml進(jìn)行mybatis語(yǔ)句的編寫(xiě)和實(shí)現(xiàn),其xml實(shí)現(xiàn)動(dòng)態(tài)更新和查詢較為方便,而目前由于技術(shù)框架所定,采用@Select、@Insert等注解方式來(lái)實(shí)現(xiàn)對(duì)應(yīng)的持久化操作(MyBatis提供了簡(jiǎn)單的Java注解,使得我們可以不配置XML格式的Mapper文件,也能方便的編寫(xiě)簡(jiǎn)單的數(shù)據(jù)庫(kù)操作代碼)
對(duì)于簡(jiǎn)單的數(shù)據(jù)庫(kù)操作基本能夠滿足日常需要,但注解對(duì)動(dòng)態(tài)SQL的支持一直差強(qiáng)人意,即使MyBatis提供了InsertProvider等*Provider注解來(lái)支持注解的Dynamic SQL,也沒(méi)有降低SQL的編寫(xiě)難度,甚至比XML格式的SQL語(yǔ)句更難編寫(xiě)和維護(hù),實(shí)現(xiàn)較為復(fù)雜的語(yǔ)句時(shí)還是不那么方便,團(tuán)隊(duì)成員一直通過(guò)類來(lái)實(shí)現(xiàn)SQL語(yǔ)句的硬拼接
這樣的硬語(yǔ)句編寫(xiě)的SQL語(yǔ)句很長(zhǎng)又冗余,給維護(hù)和修改帶來(lái)一定的成本且易讀性差,為了提高效率,一次編寫(xiě)重復(fù)使用的原則,Mybatis在3.2版本之后,其實(shí)提供了LanguageDriver接口,就是便于使用該接口自定義SQL的解析方式。
故在這里將研究的MyBatis如何在注解模式下簡(jiǎn)化SQL語(yǔ)句的硬拼接實(shí)現(xiàn)動(dòng)態(tài)組合版SQL的方案進(jìn)行分享。
二、實(shí)現(xiàn)方案
我們先來(lái)看下LanguageDriver接口中的3個(gè)方法:
public interface LanguageDriver {
ParameterHandler createParameterHandler(MappedStatement var1, Object var2, BoundSql var3);
SqlSource createSqlSource(Configuration var1, XNode var2, Class<?> var3);
SqlSource createSqlSource(Configuration var1, String var2, Class<?> var3);
}- createParameterHandler方法為創(chuàng)建一個(gè)ParameterHandler對(duì)象,用于將實(shí)際參數(shù)賦值到JDBC語(yǔ)句中
- 將XML中讀入的語(yǔ)句解析并返回一個(gè)sqlSource對(duì)象
- 將注解中讀入的語(yǔ)句解析并返回一個(gè)sqlSource對(duì)象
一旦實(shí)現(xiàn)了LanguageDriver,我們即可指定該實(shí)現(xiàn)類作為SQL的解析器,在不使用XML Mapper的形式下,我們可以使用@Lang注解
@Mapper
public interface RoleDAO {
/**
* 查詢角色信息列表
*
* @param roleParam 查詢參數(shù)
* @return 角色列表
*/
@Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified"
+ " from admin_role (#{roleParam})")
@Lang(SimpleSelectLangDriver.class)
List<RoleDO> findListRoleByPage(ListRoleParam roleParam);LanguageDriver的默認(rèn)實(shí)現(xiàn)類為XMLLanguageDriver和RawLanguageDriver;
分別為XML和Raw,Mybatis默認(rèn)是XML語(yǔ)言,所以我們來(lái)看看XMLLanguageDriver中是怎么實(shí)現(xiàn)的:
public class XMLLanguageDriver implements LanguageDriver {
public XMLLanguageDriver() {
}
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
if(script.startsWith("<script>")) {
XPathParser textSqlNode1 = new XPathParser(script, false, configuration.getVariables(),
new XMLMapperEntityResolver());
return this.createSqlSource(configuration, textSqlNode1.evalNode("/script"), parameterType);
} else {
script = PropertyParser.parse(script, configuration.getVariables());
TextSqlNode textSqlNode = new TextSqlNode(script);
return (SqlSource)(textSqlNode.isDynamic()?new DynamicSqlSource(configuration, textSqlNode)
:new RawSqlSource(configuration, script, parameterType));
}
}
}發(fā)現(xiàn)其實(shí)mybatis已經(jīng)幫忙寫(xiě)好了解析邏輯,而且發(fā)現(xiàn)如果是以開(kāi)頭的字符串傳入后,會(huì)被以XML的格式進(jìn)行解析。那么方案就可以確認(rèn)了,我們繼承XMLLanguageDriver這個(gè)類,并且重寫(xiě)其createSqlSource方法,按照自己編寫(xiě)邏輯解析好sql后,再調(diào)用父類的方法即可。
三、 實(shí)現(xiàn)自定義注解
本例中將給出一些常見(jiàn)的自定義注解的實(shí)現(xiàn)和使用方式。
1、自定義Select注解
在本例中的業(yè)務(wù)場(chǎng)景下,我們需要根據(jù)對(duì)象中的字段進(jìn)行查詢,就會(huì)寫(xiě)出硬SQL語(yǔ)句拼接類,如下代碼:
/**
* 查詢
*
* @param userParam 查詢條件
* @return 用戶信息列表
*/
@SelectProvider(type = UserSql.class, method = "listByPage")
List<UserDO> listByPage(@Param(value = "userParam") ListUserParam userParam);public class UserSql {
/**
* 拼接查詢語(yǔ)句
*
* @param params 查詢條件
* @return 查詢語(yǔ)句
*/
public static String listByPage(Map params) {
ListUserParam userParam = (ListUserParam)params.get("userParam");
if (userParam == null) {
return "";
}
long begin = (userParam.getPi() - 1) * userParam.getPs();
long end = userParam.getPi() * userParam.getPs();
String condition = "";
StringBuffer sb = new StringBuffer(" with query as ( ");
sb.append(
" select row_number() over(order by user.last_modified desc, user.date_created desc) as row_nr, user.* ");
sb.append(" from ( ");
sb.append(" select * from admin_user where 1=1 ");
condition = " and username like '%#{userParam.username}%'";
sb.append(StringUtils.isBlank(userParam.getUsername()) ? "" : condition);
condition = " and name like '%#{userParam.name}%'";
sb.append(StringUtils.isBlank(userParam.getName()) ? "" : condition);
condition = " and mobile like '%#{userParam.mobile}%'";
sb.append(StringUtils.isBlank(userParam.getMobile()) ? "" : condition);
condition = " and authorities like '%#{userParam.authorities}%'";
sb.append(StringUtils.isBlank(userParam.getAuthorities()) ? "" : condition);
condition = " and enabled = #{userParam.enabled}";
sb.append(StringUtils.isBlank(userParam.getEnabled()) ? "" : condition);
sb.append(" ) ");
sb.append(" user) ");
sb.append(" ");
sb.append(" select ");
sb.append(" id, username, password, name, mobile, authorities, enabled, deleted, ");
sb.append(" creator_id as creatorId, creator, date_created as dateCreated, ");
sb.append(" modifier_id as modifierId, modifier, last_modified as lastModified ");
sb.append(" from query where row_nr > ");
sb.append(begin);
sb.append(" and row_nr <= ");
sb.append(end);
sb.append(" order by last_modified desc, date_created desc ");
log.info("====UserSql.query====sb:{}", sb.toString());
return sb.toString();
}
}對(duì)于這樣硬拼接的SQL語(yǔ)句可讀性較差,也不利用日常維護(hù),我們可以通過(guò)實(shí)現(xiàn)LanguageDriver將where子句抽象化,以此來(lái)簡(jiǎn)化Select查詢語(yǔ)句。
簡(jiǎn)化后代碼如下( 在上述實(shí)現(xiàn)方案中已貼過(guò)代碼):
/**
* 查詢角色信息列表
*
* @param roleParam 查詢參數(shù)
* @return 角色列表
*/
@Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified"
+ " from admin_role (#{roleParam})")
@Lang(SimpleSelectLangDriver.class)
List<RoleDO> findListRoleByPage(ListRoleParam roleParam);其SimpleSelectLangDriver的實(shí)現(xiàn)代碼如下:
package com.szss.admin.common;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import com.google.common.base.CaseFormat;
/**
* @author Allen
* @date 2018/3/9
*
* 自定義Select注解,用于動(dòng)態(tài)生成Select語(yǔ)句
*/
public class SimpleSelectLangDriver extends XMLLanguageDriver implements LanguageDriver {
/**
* Pattern靜態(tài)申明
*/
private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
/**
* 實(shí)現(xiàn)自定義Select注解
* @param configuration 配置參數(shù)
* @param script 入?yún)?
* @param parameterType 參數(shù)類型
* @return 轉(zhuǎn)換后的SqlSource
*/
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
StringBuilder sb = new StringBuilder();
sb.append("<where>");
for (Field field : parameterType.getDeclaredFields()) {
String tmp = "<if test=\"_field != null\"> AND _column=#{_field}</if>";
sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
}
sb.append("</where>");
script = matcher.replaceAll(sb.toString());
script = "<script>" + script + "</script>";
}
return super.createSqlSource(configuration, script, parameterType);
}
}上述代碼實(shí)現(xiàn)了動(dòng)態(tài)生成SQL語(yǔ)句的功能,但由于在VO實(shí)體類中可能有部分參數(shù)是我們不想加入到動(dòng)態(tài)組合里面的或部分字段在數(shù)據(jù)庫(kù)中并不存在相應(yīng)的列(比如自動(dòng) 生成的serialVersionUID等其他字段),這時(shí)我們就需要排除VO實(shí)體類的一些多余的不匹配的字段進(jìn)行邏輯隱藏;我們?cè)黾右粋€(gè)自定義的注解,并且對(duì)Language的實(shí)現(xiàn)稍作修改即可。
新建一個(gè)注解,其代碼如下:
package com.szss.admin.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Allen
* @date 2018/3/9
*
* 自定義的注解,用于排除多余的變量(自定義注解,過(guò)濾多余字段)
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Invisible {
}然后在VO實(shí)體類中不需要加入的字段可進(jìn)行引用該注解
package com.szss.admin.model.param;
import com.szss.admin.common.Invisible;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 角色查詢參數(shù)
*
* @author Allen
* @date 2018/3/8
*/
@Data
public class ListRoleParam {
/**
* 角色名稱
*/
@ApiModelProperty(value = "角色名稱", example = "管理員", position = 1)
private String name;
/**
* 是否啟用:0-不可用,1-可用
*/
@ApiModelProperty(value = "是否啟用", example = "0", position = 2)
private Boolean enabled;
/**
* 刪除標(biāo)示:0-未刪除,1-已刪除
*/
@ApiModelProperty(value = "刪除標(biāo)示", example = "0", position = 3)
private Boolean deleted;
/**
* 當(dāng)前頁(yè)碼
*/
@ApiModelProperty(value = "當(dāng)前頁(yè)碼", example = "1", position = 4)
@Invisible
private long pi;
/**
* 當(dāng)前頁(yè)面大小
*/
@ApiModelProperty(value = "當(dāng)前頁(yè)面大小", example = "10", position = 5)
@Invisible
private long ps;
}最后需要對(duì)上述中的SimpleSelectLangDriver實(shí)現(xiàn)類中將被該注解聲明過(guò)的字段排除操作,代碼如下:
for (Field field : parameterType.getDeclaredFields()) {
// 排除被Invisble修飾的變量
if (!field.isAnnotationPresent(Invisible.class)) {
String tmp = "<if test=\"_field != null\"> AND _column=#{_field}</if>";
sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
}
}如上所示,只是對(duì)SimpleSelectLangDriver類增加了if (!field.isAnnotationPresent(Invisible.class)) 這樣的判斷,已過(guò)濾多余的變量。
需要注意的是在使用Select的時(shí)候,傳入的參數(shù)前無(wú)需加入@Param注解,否則會(huì)導(dǎo)致Mybatis找不到參數(shù)而拋出異常,如需加入 就需要綁定對(duì)象屬性(如在語(yǔ)句中就需要使用param.name)。
2、自定義Select in注解
在使用Mybatis注解的時(shí)候,發(fā)現(xiàn)其對(duì)Select In格式的查詢支持不是很友好,在字符串中輸入十分繁瑣,可以通過(guò)將自定義的標(biāo)簽轉(zhuǎn)成格式;下面便通過(guò)我們自己實(shí)現(xiàn)的LanguageDriver來(lái)實(shí)現(xiàn)SQL的動(dòng)態(tài)解析:
DAO接口層中代碼如下:
@Select("SELECT * FROM admin_role WHERE id IN (#{roleIdList})")
@Lang(SimpleSelectInLangDriver.class)
List<RoleDO> selectRolesByRoleId(List<Integer> roleIdList);LanguageDriver實(shí)現(xiàn)類如下:
package com.szss.admin.common;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
/**
* @author Allen
* @date 2018/3/9
*
* 自定義Select in 注解,用于動(dòng)態(tài)生成Select in 語(yǔ)句
*/
public class SimpleSelectInLangDriver extends XMLLanguageDriver implements LanguageDriver {
/**
* Pattern靜態(tài)申明
*/
private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
/**
* 實(shí)現(xiàn)自定義Select in 注解
* @param configuration 配置參數(shù)
* @param script 入?yún)?
* @param parameterType 參數(shù)類型
* @return 轉(zhuǎn)換后的SqlSource
*/
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
script = matcher.replaceAll("<foreach collection=\"$1\" item=\"_item\" open=\"(\" "
+ "separator=\",\" close=\")\" >#{_item}</foreach>");
}
script = "<script>" + script + "</script>";
return super.createSqlSource(configuration, script, parameterType);
}
}通過(guò)自己實(shí)現(xiàn)LanguageDriver,在服務(wù)器啟動(dòng)的時(shí)候,就會(huì)將我們自定義的標(biāo)簽解析為動(dòng)態(tài)SQL語(yǔ)句,其等同于:
@Select("SELECT * " +
"FROM admin_role " +
"WHERE id IN " +
"<foreach item='item' index='index' collection='list'open='(' separator=',' close=')'>" +
"#{item}" +
"</foreach>")
List<RoleDO> selectRolesByRoleId(List<Integer> roleIdList);通過(guò)實(shí)現(xiàn)LanguageDriver,剝離了冗長(zhǎng)的動(dòng)態(tài)拼接SQL語(yǔ)句,簡(jiǎn)化了Select In的注解代碼。
需要注意的是在使用Select In的時(shí)候,則與上述相反需務(wù)必在傳入的參數(shù)前加@Param注解,否則會(huì)導(dǎo)致Mybatis找不到參數(shù)而拋出異常。
3、自定義Update的注解
在擴(kuò)展update注解時(shí),數(shù)據(jù)庫(kù)每張表的字段和實(shí)體類的字段必須遵循一個(gè)約定(數(shù)據(jù)庫(kù)中采用下劃線命名法,實(shí)體類中采用駝峰命名法)。
當(dāng)我們update的時(shí)候,會(huì)根據(jù)每個(gè)字段的映射關(guān)系,寫(xiě)出如下代碼:
/**
* 更新
*
* @param roleDO 角色信息
* @return 影響行數(shù)
*/
@Update("update admin_role set role_name = #{roleDO.roleName}, "
+ " enabled = #{roleDO.enabled}, deleted = #{roleDO.deleted}, modifierId = #{roleDO.modifierId},"
+ " modifier = #{roleDO.modifier}, last_modified = #{roleDO.lastModified} where id = #{roleDO.id}")
int update(@Param(value = "roleDO") RoleDO roleDO);上述的代碼我們可以將實(shí)體類中的駝峰式代碼轉(zhuǎn)換為下劃線式命名方式,這樣就可以將這種映射規(guī)律自動(dòng)化,但此代碼存在一定的問(wèn)題,就是當(dāng)你在更新部分字段時(shí)其余所有字段原來(lái)的值必須傳入,否則可能會(huì)將原有數(shù)據(jù)更新為null或空,亦或在更新時(shí)先查詢?cè)瓟?shù)據(jù)后將變更的數(shù)據(jù)進(jìn)行操作,這樣不僅增加了數(shù)據(jù)庫(kù)查詢操作且會(huì)造成代碼冗余,而經(jīng)過(guò)實(shí)現(xiàn)LanguageDriver后,注解代碼如下:
/**
* 更新
*
* @param roleParam 角色信息
*/
@Update("update admin_role (#{roleDO}) where id=#{id}")
@Lang(SimpleUpdateLangDriver.class)
void update(RoleParam roleParam);相對(duì)于原始的代碼量有很大的減少,尤其是對(duì)于一個(gè)類中字段越多,改善也就越明顯。實(shí)現(xiàn)方式為:
package com.szss.admin.common;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import com.google.common.base.CaseFormat;
/**
* @author Allen
* @date 2018/3/9
*
* 自定義Update注解,用于動(dòng)態(tài)生成Update語(yǔ)句
*/
public class SimpleUpdateLangDriver extends XMLLanguageDriver implements LanguageDriver {
/**
* Pattern靜態(tài)申明
*/
private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
/**
* 實(shí)現(xiàn)自定義Update注解
* @param configuration 配置參數(shù)
* @param script 入?yún)?
* @param parameterType 參數(shù)類型
* @return 轉(zhuǎn)換后的SqlSource
*/
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
StringBuilder sb = new StringBuilder();
sb.append("<set>");
for (Field field : parameterType.getDeclaredFields()) {
// 排除被Invisble修飾的變量
if (!field.isAnnotationPresent(Invisible.class)) {
String tmp = "<if test=\"_field != null\">_column=#{_field},</if>";
sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
}
}
sb.deleteCharAt(sb.lastIndexOf(","));
sb.append("</set>");
script = matcher.replaceAll(sb.toString());
script = "<script>" + script + "</script>";
}
return super.createSqlSource(configuration, script, parameterType);
}
}注意此處在傳入的參數(shù)前無(wú)需加入@Param注解。
4、自定義Insert的注解 同理
我們可以抽象化Insert操作,簡(jiǎn)化后的Insert注解為:
/**
* 插入
*
* @param roleParam 角色信息
*/
@Insert("insert into admin_role (#{roleDO})")
@Lang(SimpleInsertLangDriver.class)
void insert(RoleParam roleParam);SimpleInsertLanguageDriver實(shí)現(xiàn)類代碼如下:
package com.szss.admin.common;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import com.google.common.base.CaseFormat;
/**
* @author Allen
* @date 2018/3/9
*
* 自定義Insert注解,用于動(dòng)態(tài)生成Insert語(yǔ)句
*/
public class SimpleInsertLangDriver extends XMLLanguageDriver implements LanguageDriver {
/**
* Pattern靜態(tài)申明
*/
private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
/**
* 實(shí)現(xiàn)自定義Insert注解
* @param configuration 配置參數(shù)
* @param script 入?yún)?
* @param parameterType 參數(shù)類型
* @return 轉(zhuǎn)換后的SqlSource
*/
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
StringBuilder sb = new StringBuilder();
StringBuilder tmp = new StringBuilder();
sb.append("(");
for (Field field : parameterType.getDeclaredFields()) {
if (!field.isAnnotationPresent(Invisible.class)) {
sb.append(
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())
+ ",");
tmp.append("#{" + field.getName() + "},");
}
}
sb.deleteCharAt(sb.lastIndexOf(","));
tmp.deleteCharAt(tmp.lastIndexOf(","));
sb.append(") values ("
+ tmp.toString() + ")");
script = matcher.replaceAll(sb.toString());
script = "<script>" + script + "</script>";
}
return super.createSqlSource(configuration, script, parameterType);
}
}至此我們完成了基本的@Select、@Update、@Insert自定義注解,簡(jiǎn)單化繁雜的拼接SQL語(yǔ)句的尷尬,但以上代碼在SimpleSelectLangDriver中還有一定的局限性,比如對(duì)于一些字段我們需要使用like來(lái)進(jìn)行查詢,這時(shí)就需要對(duì)上述自定義Seleect注解進(jìn)行完善以實(shí)現(xiàn)各種業(yè)務(wù)的場(chǎng)景。
四、注意事項(xiàng)&遇到的一些坑
- 務(wù)必確保數(shù)據(jù)庫(kù)中列名和實(shí)體類中字段能一一對(duì)應(yīng)。
- 在使用自定義SQL解析器的時(shí)候,只能傳入一個(gè)參數(shù),即相應(yīng)的對(duì)象參數(shù)即可;傳入多個(gè)參數(shù)會(huì)導(dǎo)致解析器中獲得到的class對(duì)象改變,使得sql解析異常。
- Update的實(shí)現(xiàn)能滿足大部分的業(yè)務(wù),但有些業(yè)務(wù)場(chǎng)景可以會(huì)遇到根據(jù)查詢條件來(lái)更新查詢參數(shù)的情況,比如Update user SET uesr_name = ‘王雷’ WHERE user_name = ‘小王’; 在這樣的場(chǎng)景中請(qǐng)不要使用自定義的SQL解析器。
- 請(qǐng)使用Mybatis 3.3以上版本。3.2以下版本會(huì)存在一些Bug,在本例中使用的為mybatis-spring-boot-starter1.3.1,其Mybatis為3.4.5。
五、總結(jié)
通過(guò)實(shí)現(xiàn)Language Driver,我們可以很方便的自定義自己的注解。在遵循一些約定的情況下(數(shù)據(jù)庫(kù)下劃線命名,實(shí)體駝峰命名),我們可以大幅度的減少SQL的編寫(xiě)量,并且可以完全的屏蔽掉麻煩的XML編寫(xiě)方式,再也不用再編寫(xiě)復(fù)雜的拼接動(dòng)態(tài)SQL的煩惱,簡(jiǎn)化工作,提高開(kāi)發(fā)效率。
//簡(jiǎn)潔的數(shù)據(jù)庫(kù)操作
/**
* 查詢角色信息列表
*
* @param roleParam 查詢參數(shù)
* @return 角色列表
*/
@Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified"
+ " from admin_role (#{roleParam})")
@Lang(SimpleSelectLangDriver.class)
List<RoleDO> findListRoleByPage(ListRoleParam roleParam);
/**
* 插入
*
* @param roleParam 角色信息
*/
@Insert("insert into admin_role (#{roleDO})")
@Lang(SimpleInsertLangDriver.class)
void insert(RoleParam roleParam);
/**
* 更新
*
* @param roleParam 角色信息
*/
@Update("update admin_role (#{roleDO}) where id=#{id}")
@Lang(SimpleUpdateLangDriver.class)
void update(RoleParam roleParam);通過(guò)@Lang注解以及自定義LanguageDriver類實(shí)現(xiàn)來(lái)簡(jiǎn)化數(shù)據(jù)庫(kù)操作,不僅代碼減少便于可讀的同時(shí),還避免了在更新時(shí)需要獲取原數(shù)據(jù)的操作。
注:上述通過(guò)@Lang及實(shí)現(xiàn)LanguageDriver類的方法目前已基本不建議采用了,可采取Mybatis Plus來(lái)進(jìn)行取代更為方便和快捷,具體可參加本博客的另一篇文章<Spring Boot環(huán)境下Mybatis Plus的快速應(yīng)用>
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中BeanUtils.copyProperties基本用法與小坑
本文主要介紹了Java中BeanUtils.copyProperties基本用法與小坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Spring Security使用單點(diǎn)登錄的權(quán)限功能
本文主要介紹了Spring Security使用單點(diǎn)登錄的權(quán)限功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式
在前后端分離的項(xiàng)目中后端返回的格式一定要友好,不然會(huì)對(duì)前端的開(kāi)發(fā)人員帶來(lái)很多的工作量。那么SpringBoot如何做到統(tǒng)一的后端返回格式呢?本文將為大家詳細(xì)講講2022-04-04
SpringBoot實(shí)現(xiàn)接口等冪次校驗(yàn)的示例代碼
本文主要介紹了SpringBoot實(shí)現(xiàn)接口等冪次校驗(yàn)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
java使用POI實(shí)現(xiàn)html和word相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了java使用POI實(shí)現(xiàn)html和word的相互轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12

