MyBatis提供了简单的Java注解,使得咱们能够不配置XML格式的Mapper文件,方便的编写简单的数据库操做代码:
数据库
public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{userId}") User getUser(@Param("userId") String userId); }
<configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>
可是没有Dynamic SQL的注解是不完整的,故这里向你们介绍下如何经过实现LanguageDriver,优雅的在MyBatis注解中使用Dynamic SQL。
app
@Lang(SimpleSelectInExtendedLanguageDriver.class) @Select("SELECT * FROM users WHERE id IN (#{userIds})") List<User> selectUsers(@Param("userIds") List<String> userIds);
// 一次编写,受益终生 public class SimpleSelectInExtendedLanguageDriver extends XMLLanguageDriver implements LanguageDriver { private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)"); @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\" separator=\",\" >#{__item}</foreach>)"); } script = "<script>" + script + "</script>"; return super.createSqlSource(configuration, script, parameterType); } }
咱们经过实现本身的LanguageDriver,在MyBatis编译语句前,将咱们自定义的标签替换为了动态SQL语句,其等同于:ide
@Select({"<script>", "SELECT *", "FROM user", "WHERE id IN", "<foreach item='item' index='index' collection='list'", "open='(' separator=',' close=')'>", "#{item}", "</foreach>", "</script>"}) List<User> selectUsers(@Param("userIds") List<Intger> userIds);
经过实现LanguageDriver,剥离出了冗长的动态SQL语句,简化Select In的注解代码。
code
相似的,经过重写LanguageDriver,咱们还能扩展出远比其它方案(e.g. XML SQL Mapper配置、在注解语句中写动态SQL)简洁的自定义操做。
一个经常使用的操做是更新数据库中的一条记录。一般而言,每张表(采用下划线命名法)会有一个对应的Domain对象(采用驼峰式命名法),当咱们更新一条记录时,须要为对象中的每一个字段配置映射关系,会写出以下的代码:orm
int updateUser(User user);
<update id="updateUser"> update users <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="homeAddress != null">home_address=#{homeAddress}</if> </set> where id=#{userId} </update>
冗长的代码只是把驼峰式命名的变量名映射为下划线式命名的列名,显然咱们能够将这种映射规律自动化:对象
@Update("UPDATE user (#{user}) WHERE id =#{userId}") @Lang(SimpleUpdateExtendedLanguageDriver.class) int updateUser(Store store);
/** * Created by benxue on 3/1/16. */ public class SimpleUpdateExtendedLanguageDriver extends XMLLanguageDriver implements LanguageDriver { private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)"); @Override public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) { Matcher matcher = inPattern.matcher(script); if (matcher.find()) { StringBuffer ss = new StringBuffer(); ss.append("<set>"); for (Field field : parameterType.getDeclaredFields()) { String temp = "<if test=\"__field != null\">__column=#{__field},</if>"; ss.append(temp.replaceAll("__field", field.getName()) .replaceAll("__column", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()))); } ss.deleteCharAt(ss.lastIndexOf(",")); ss.append("</set>"); script = matcher.replaceAll(ss.toString()); script = "<script>" + script + "</script>"; } return super.createSqlSource(configuration, script, parameterType); } }
一个常见的状况是,Domain中的部分属性在数据库表中并不存在对应的列,咱们增长一个自定义的注释并对LanguageDriver的实现稍做修改:ip
public class User{ ... @Invisible private UserSearchDO userSearchDO; ... }
/** * Created by benxue on 3/10/16. * The field marked as Invisible will not be scanned by customized simple extended language drivers * of myBatis */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Invisible { }
for (Field field : parameterType.getDeclaredFields()) { if (!field.isAnnotationPresent(Invisible.class)) { String temp = "<if test=\"__field != null\">__column=#{__field},</if>"; ss.append(temp.replaceAll("__field", field.getName()) .replaceAll("__column", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()))); } }
由此,之后就能够用一句话完成动态Update语句了。
get
经过实现LanguageDriver,咱们能够实现方便的自定义注解。在遵循一些约定的状况下(e.g. Domain使用驼峰命名法,数据库表使用下划线命名法),就能够和麻烦的XML配置和动态SQL编写say 88了:)it
// 清爽的数据库操做 @Select("SELECT * FROM user WHERE user_id IN (#{userIds})") @Lang(SimpleSelectInExtendedLanguageDriver.class) List<User> getUsers(@Param("userIds") List<Long> userIds); @Update("UPDATE user (#{user}) WHERE user_id =#{userId}") @Lang(SimpleUpdateExtendedLanguageDriver.class) int updateUser(User user); @Insert("INSERT INTO user (#{user})") @Lang(SimpleInsertExtendedLanguageDriver.class) void insertUser(User user);