加強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。
 app

自定義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語句,其等同於: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

自定義Update Bean註解

相似的,經過重寫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);

 
連接:https://www.jianshu.com/p/03642b807688自動化

相關文章
相關標籤/搜索