增强MyBatis注解

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。
 

自定义Select In注解

@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语句,其等同于:

@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的注解代码。
 

自定义Update Bean注解

类似的,通过重写LanguageDriver,我们还能扩展出远比其它方案(e.g. XML SQL Mapper配置、在注解语句中写动态SQL)简洁的自定义操作。
 
一个常用的操作是更新数据库中的一条记录。通常而言,每张表(采用下划线命名法)会有一个对应的Domain对象(采用驼峰式命名法),当我们更新一条记录时,需要为对象中的每个字段配置映射关系,会写出如下的代码:

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的实现稍作修改:

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语句了。
 

总结

通过实现LanguageDriver,我们可以实现方便的自定义注解。在遵循一些约定的情况下(e.g. Domain使用驼峰命名法,数据库表使用下划线命名法),就可以和麻烦的XML配置和动态SQL编写say 88了:)

// 清爽的数据库操作

@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);

 
链接:https://www.jianshu.com/p/03642b807688

转载于:https://blog.51cto.com/jtech/2324855

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值