MyBatis-Plus碼之重器 lambda 表達(dá)式使用指南,開(kāi)發(fā)效率瞬間提升80%

一、回顧

現(xiàn)在越來(lái)越流行基于 SpringBoot 開(kāi)發(fā) Web 應(yīng)用畜普,其中利用 Mybatis 作為數(shù)據(jù)庫(kù) CRUD 操作已成為主流唉韭。樓主以 MySQL 為例柔逼,總結(jié)了九大類使用 Mybatis 操作數(shù)據(jù)庫(kù) SQL 小技巧分享給大家。

  1. 分頁(yè)查詢
  2. 預(yù)置 sql 查詢字段
  3. 一對(duì)多級(jí)聯(lián)查詢
  4. 一對(duì)一級(jí)聯(lián)查詢
  5. foreach 搭配 in 查詢
  6. 利用if 標(biāo)簽拼裝動(dòng)態(tài) where 條件
  7. 利用 choose 和 otherwise組合標(biāo)簽拼裝查詢條件
  8. 動(dòng)態(tài)綁定查詢參數(shù):_parameter
  9. 利用 set 配合 if 標(biāo)簽享钞,動(dòng)態(tài)設(shè)置數(shù)據(jù)庫(kù)字段更新值

01 分頁(yè)查詢

利用 limit 設(shè)置每頁(yè) offset 偏移量和每頁(yè) size 大小揍诽。

select * from sys_user u
LEFT JOIN sys_user_site s ON u.user_id = s.user_id
LEFT JOIN sys_dept d ON d.dept_id = s.dept_id
LEFT JOIN sys_emailinfo e ON u.user_id = e.userid AND e.MAIN_FLAG = 'Y'
<where>
 <include refid="userCondition"/>
</where>
limit #{offset}, #{limit}

02 預(yù)置 sql 查詢字段

<sql id="columns">
  id,title,content,original_img,is_user_edit,province_id,status,porder
</sql>

查詢 select 語(yǔ)句引用 columns:

<select id="selectById" resultMap="RM_MsShortcutPanel">
 seelct
 <include refid="columns"/>
 from cms_self_panel
 where
 id = #{_parameter}
</select>

03 一對(duì)多級(jí)聯(lián)查詢

利用 mybatis 的 collection 標(biāo)簽,可以在每次查詢文章主體同時(shí)通過(guò) queryparaminstancelist 級(jí)聯(lián)查詢出關(guān)聯(lián)表數(shù)據(jù)栗竖。

<resultMap id="BaseResultMap" type="com.unicom.portal.pcm.entity.ArticleEntity">
 <id column="id" jdbcType="BIGINT" property="id"/> 
 <collection property="paramList" column="id" select="queryparaminstancelist"/>
</resultMap>

queryparaminstancelist 的 sql 語(yǔ)句

<select id="queryparaminstancelist" resultMap="ParamInstanceResultMap">
 select * from `cms_article_flow_param_instance` where article_id=#{id} 
</select>

04 一對(duì)一級(jí)聯(lián)查詢

利用 mybatis 的 association 標(biāo)簽暑脆,一對(duì)一查詢關(guān)聯(lián)表數(shù)據(jù)。

<resultMap id="BaseResultMap" type="com.unicom.portal.pcm.entity.ArticleEntity">
 <association property="articleCount" javaType="com.unicom.portal.pcm.entity.MsArticleCount"/>
</resultMap>

查詢sql語(yǔ)句:

MsArticlecount 實(shí)體對(duì)象的屬性值可以從 上面的 select 后的 sql 字段進(jìn)行匹配映射獲取狐肢。

05 foreach 搭配 in 查詢

利用 foreach 遍歷 array 集合的參數(shù)添吗,拼成 in 查詢條件

<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
 #{item}
</foreach>

06 利用 if 標(biāo)簽拼裝動(dòng)態(tài) where 條件

select r.*, (select d.org_name from sys_dept d where d.dept_id = r.dept_id) deptName from sys_role r
<where>
r.wid = #{wid}
<if test="roleName != null and roleName.trim() != ''">
and r.`role_name` like concat('%',#{roleName},'%')
</if>
<if test="status != null and status.trim() != ''">
and r.`status` = #{status}
</if>
</where>

07 利用 choose 和 otherwise 組合標(biāo)簽拼裝查詢條件

<choose>
 <when test="sidx != null and sidx.trim() != ''">
 order by r.${sidx} ${order}
 </when>
 <otherwise>
 order by r.role_id asc
 </otherwise>
</choose>

08 隱形綁定參數(shù):_parameter

_parameter 參數(shù)的含義

當(dāng) Mapper、association处坪、collection 指定只有一個(gè)參數(shù)時(shí)進(jìn)行查詢時(shí)根资,可以使用 _parameter,它就代表了這個(gè)參數(shù)同窘。

另外玄帕,當(dāng)使用 Mapper指定方法使用 @Param 的話,會(huì)使用指定的參數(shù)值代替想邦。

SELECT id, grp_no grpNo, province_id provinceId, status FROM tj_group_province
<where> 
 ...
 <if test="_parameter!=null">
 and grp_no = #{_parameter}
 </if>
</where>

09 利用 set 配合 if 標(biāo)簽裤纹,動(dòng)態(tài)設(shè)置數(shù)據(jù)庫(kù)字段更新值

<update id="updateById">
 UPDATE cms_label
 <set>
   <if test="labelGroupId != null">
     label_group_id = #{labelGroupId},
   </if>
   dept_id = #{deptId},
   <if test="recommend != null">
     is_recommend = #{recommend},
   </if>
 </set>
 WHERE label_id = #{labelId} 
</update

二、Mybatis-Plus Lambda 表達(dá)式理論篇

背景

如果 Mybatis-Plus 是扳手,那 Mybatis Generator 就是生產(chǎn)扳手的工廠鹰椒。

MyBatis 是一種操作數(shù)據(jù)庫(kù)的 ORM 框架锡移,提供一種 Mapper 類,支持讓你用 java 代碼進(jìn)行增刪改查的數(shù)據(jù)庫(kù)操作漆际,省去了每次都要手寫 sql 語(yǔ)句的麻煩淆珊。但是有一個(gè)前提,你得先在 xml 中寫好 sql 語(yǔ)句奸汇,也是很麻煩的施符。

題外話:Mybatis 和 Hibernate 的比較

  • Mybatis 是一個(gè)半 ORM 框架;Hibernate 是一個(gè)全 ORM 框架擂找。Mybatis 需要自己編寫 sql 戳吝。
  • Mybatis 直接編寫原生 sql,靈活度高贯涎,可以嚴(yán)格控制 sql 執(zhí)行性能听哭;Hibernate的自動(dòng)生成 hql,因?yàn)楦玫姆庋b型塘雳,開(kāi)發(fā)效率提高的同時(shí)陆盘,sql 語(yǔ)句的調(diào)優(yōu)比較麻煩。
  • Hibernate的 hql 數(shù)據(jù)庫(kù)移植性比 Mybatis 更好粉捻,Hibernate 的底層對(duì) hql 進(jìn)行了處理礁遣,對(duì)于數(shù)據(jù)庫(kù)的兼容性更好,
  • Mybatis 直接寫的原生 sql 都是與數(shù)據(jù)庫(kù)相關(guān)肩刃,不同數(shù)據(jù)庫(kù) sql 不同祟霍,這時(shí)就需要多套 sql 映射文件。
  • Hibernate 在級(jí)聯(lián)刪除的時(shí)候效率低盈包;數(shù)據(jù)量大沸呐, 表多的時(shí)候,基于關(guān)系操作會(huì)變得復(fù)雜呢燥。
  • Mybatis 和 Hibernate 都可以使用第三方緩存崭添,而 Hibernate 相比 Mybatis 有更好的二級(jí)緩存機(jī)制。

微信搜公眾號(hào)「猿芯」叛氨,后臺(tái)私信回復(fù) 1024 免費(fèi)領(lǐng)取 SpringCloud呼渣、SpringBoot,微信小程序寞埠、Java面試屁置、數(shù)據(jù)結(jié)構(gòu)、算法等全套視頻資料仁连。

為什么要選擇 Lambda 表達(dá)式蓝角?

Mybatis-Plus 的存在就是為了稍稍彌補(bǔ) Mybatis 的不足。

在我們使用 Mybatis 時(shí)會(huì)發(fā)現(xiàn),每當(dāng)要寫一個(gè)業(yè)務(wù)邏輯的時(shí)候都要在 DAO 層寫一個(gè)方法使鹅,再對(duì)應(yīng)一個(gè) SQL揪阶,即使是簡(jiǎn)單的條件查詢、即使僅僅改變了一個(gè)條件都要在 DAO層新增一個(gè)方法患朱,針對(duì)這個(gè)問(wèn)題鲁僚,Mybatis-Plus 就提供了一個(gè)很好的解決方案:lambda 表達(dá)式,它可以讓我們避免許多重復(fù)性的工作麦乞。

想想 Mybatis 官網(wǎng)提供的 CRUD 例子吧蕴茴,基本上 xml 配置占據(jù)了絕大部分劝评。而用 Lambda 表達(dá)式寫的 CRUD 代碼非常簡(jiǎn)潔姐直,真正做到零配置,不需要在 xml 或用注解(@Select)寫大量原生 SQL 代碼蒋畜。

LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
lqw.eq(UserEntity::getSex, 0L)
        .like(UserEntity::getUserName, "dun");
List<UserEntity> userList = userMapper.selectList(lqw);
userList.forEach(u -> System.out.println("like全包含關(guān)鍵字查詢::" + u.getUserName()));

lambda 表達(dá)式的理論基礎(chǔ)

Java中的 lambda 表達(dá)式實(shí)質(zhì)上是一個(gè)匿名方法声畏,但該方法并非獨(dú)立執(zhí)行,而是用于實(shí)現(xiàn)由函數(shù)式接口定義的唯一抽象方法姻成。

使用 lambda 表達(dá)式時(shí)插龄,會(huì)創(chuàng)建實(shí)現(xiàn)了函數(shù)式接口的一個(gè)匿名類實(shí)例,如 Java8 中的線程 Runnable 類實(shí)現(xiàn)了函數(shù)接口:@FunctionalInterface科展。

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

平常我們執(zhí)行一個(gè) Thread 線程:

new Thread(new Runnable() {
  @Override
  public void run() {
      System.out.println("xxxx");
  }
}).start();

如果用 lambda 會(huì)非常簡(jiǎn)潔均牢,一行代碼搞定。

 new Thread(()-> System.out.println("xxx")).start();

所以在某些場(chǎng)景下使用 lambda 表達(dá)式真的能減少 java 中一些冗長(zhǎng)的代碼才睹,增加代碼的優(yōu)雅性徘跪。

lambda 條件構(gòu)造器基礎(chǔ)類:包裝器模式(裝飾模式)之 AbstractWrapper AbstractWrapper 條件構(gòu)造器說(shuō)明

  1. 出現(xiàn)的第一個(gè)入?yún)?boolean condition 表示該條件是否加入最后生成的 sql 中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)
  2. 代碼塊內(nèi)的多個(gè)方法均為從上往下補(bǔ)全個(gè)別 boolean 類型的入?yún)?默認(rèn)為 true
  3. 出現(xiàn)的泛型 Param 均為 Wrapper 的子類實(shí)例(均具有 AbstractWrapper 的所有方法)
  4. 方法在入?yún)⒅谐霈F(xiàn)的 R 為泛型琅攘,在普通 wrapper 中是 String 垮庐,在 LambdaWrapper 中是函數(shù)(例:Entity::getId,Entity 為實(shí)體類坞琴,getId為字段id的getMethod)
  5. 方法入?yún)⒅械?R column 均表示數(shù)據(jù)庫(kù)字段哨查,當(dāng) R 具體類型為 String 時(shí)則為數(shù)據(jù)庫(kù)字段名(字段名是數(shù)據(jù)庫(kù)關(guān)鍵字的自己用轉(zhuǎn)義符包裹!)!而不是實(shí)體類數(shù)據(jù)字段名!!!,另當(dāng) R 具體類型為 SFunction 時(shí)項(xiàng)目 runtime 不支持 eclipse 自家的編譯器!
  6. 使用普通 wrapper剧辐,入?yún)?Map 和 List 的均以 json 形式表現(xiàn)!
  7. 使用中如果入?yún)⒌?Map 或者 List為空,則不會(huì)加入最后生成的 sql 中!

警告:

不支持以及不贊成在 RPC 調(diào)用中把 Wrapper 進(jìn)行傳輸寒亥。

Wrapper 很重 傳輸 Wrapper 可以類比為你的 controller 用 map 接收值(開(kāi)發(fā)一時(shí)爽,維護(hù)火葬場(chǎng)) 正確的 RPC 調(diào)用姿勢(shì)是寫一個(gè) DTO 進(jìn)行傳輸荧关,被調(diào)用方再根據(jù) DTO 執(zhí)行相應(yīng)的操作 我們拒絕接受任何關(guān)于 RPC 傳輸 Wrapper 報(bào)錯(cuò)相關(guān)的 issue 甚至 pr溉奕。

AbstractWrapper 內(nèi)部結(jié)構(gòu)

從上圖,我們了解到 AbstractWrapper 的實(shí)際上實(shí)現(xiàn)了五大接口:

  • SQL 片段函數(shù)接口:ISqlSegment
@FunctionalInterface
public interface ISqlSegment extends Serializable {
    /**
     * SQL 片段
     */
    String getSqlSegment();
}
  • 比較值接口 Compare<Children, R>羞酗,如 等值 eq腐宋、不等于:ne、大于 gt、大于等于:ge胸竞、小于 lt欺嗤、小于等于 le、between卫枝、模糊查詢:like 等等
  • 嵌套接口 Nested<Param, Children> 煎饼,如 and、or
  • 拼接接口 Join<Children>校赤,如 or 吆玖、exists
  • 函數(shù)接口 Func<Children, R>,如 in 查詢马篮、groupby 分組沾乘、having、order by排序等

微信搜公眾號(hào)「猿芯」浑测,后臺(tái)私信回復(fù) 1024 免費(fèi)領(lǐng)取 SpringCloud翅阵、SpringBoot,微信小程序迁央、Java面試掷匠、數(shù)據(jù)結(jié)構(gòu)、算法等全套視頻資料岖圈。

常用的 where 條件表達(dá)式 eq讹语、like、in蜂科、ne顽决、gt、ge崇摄、lt擎值、le。

@Override
public Children in(boolean condition, R column, Collection<?> coll) {
    return doIt(condition, () -> columnToString(column), IN, inExpression(coll));
}

public Children notIn(boolean condition, R column, Collection<?> coll)

public Children inSql(boolean condition, R column, String inValue)

public Children notInSql(boolean condition, R column, String inValue)

public Children groupBy(boolean condition, R... columns)

public Children orderBy(boolean condition, boolean isAsc, R... columns)
    
public Children eq(boolean condition, R column, Object val)

public Children ne(boolean condition, R column, Object val)

public Children gt(boolean condition, R column, Object val)

public Children ge(boolean condition, R column, Object val)

public Children lt(boolean condition, R column, Object val)

public Children le(boolean condition, R column, Object val)

...

/**
 * 普通查詢條件
 *
 * @param condition  是否執(zhí)行
 * @param column     屬性
 * @param sqlKeyword SQL 關(guān)鍵詞
 * @param val        條件值
 */
protected Children addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) {
    return doIt(condition, () -> columnToString(column), sqlKeyword, () -> formatSql("{0}", val));
}

SQL 片段函數(shù)接口

lambda 這么好用的秘訣在于 SQL 片段函數(shù)接口:ISqlSegment逐抑,我們?cè)?doIt 方法找到 ISqlSegment 對(duì)象參數(shù)鸠儿,翻開(kāi) ISqlSegment 源碼,發(fā)現(xiàn)它真實(shí)的廬山真面目厕氨,原來(lái)是基于 Java 8 的函數(shù)接口 @FunctionalInterface 實(shí)現(xiàn)进每!

ISqlSegment 就是對(duì) where 中的每個(gè)條件片段進(jìn)行組裝。

/**
 * 對(duì)sql片段進(jìn)行組裝
 *
 * @param condition   是否執(zhí)行
 * @param sqlSegments sql片段數(shù)組
 * @return children
 */
protected Children doIt(boolean condition, ISqlSegment... sqlSegments) {
    if (condition) {
        expression.add(sqlSegments);
    }
    return typedThis;
}
   
@FunctionalInterface
public interface ISqlSegment extends Serializable {

    /**
     * SQL 片段
     */
    String getSqlSegment();
}

從 MergeSegments 類中命斧,我們找到 getSqlSegment 方法田晚,其中代碼片段

sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment()

這段代碼表明,一條完整的 where 條件 SQL 語(yǔ)句国葬,最終由 normal SQL 片段,groupBy SQL 片段,having SQL 片段,orderBy SQL 片段拼接而成贤徒。

@Getter
@SuppressWarnings("serial")
public class MergeSegments implements ISqlSegment {

    private final NormalSegmentList normal = new NormalSegmentList();
    private final GroupBySegmentList groupBy = new GroupBySegmentList();
    private final HavingSegmentList having = new HavingSegmentList();
    private final OrderBySegmentList orderBy = new OrderBySegmentList();

    @Getter(AccessLevel.NONE)
    private String sqlSegment = StringPool.EMPTY;
    @Getter(AccessLevel.NONE)
    private boolean cacheSqlSegment = true;

    public void add(ISqlSegment... iSqlSegments) {
        List<ISqlSegment> list = Arrays.asList(iSqlSegments);
        ISqlSegment firstSqlSegment = list.get(0);
        if (MatchSegment.ORDER_BY.match(firstSqlSegment)) {
            orderBy.addAll(list);
        } else if (MatchSegment.GROUP_BY.match(firstSqlSegment)) {
            groupBy.addAll(list);
        } else if (MatchSegment.HAVING.match(firstSqlSegment)) {
            having.addAll(list);
        } else {
            normal.addAll(list);
        }
        cacheSqlSegment = false;
    }

    @Override
    public String getSqlSegment() {
        if (cacheSqlSegment) {
            return sqlSegment;
        }
        cacheSqlSegment = true;
        if (normal.isEmpty()) {
            if (!groupBy.isEmpty() || !orderBy.isEmpty()) {
                sqlSegment = groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
            }
        } else {
            sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
        }
        return sqlSegment;
    }
}

三芹壕、Mybatis-Plus Lambda 表達(dá)式實(shí)戰(zhàn)

01 環(huán)境準(zhǔn)備

1. Maven 依賴

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
</dependency>

2. 實(shí)體(表)以及 Mapper 表映射文件

  • Base 實(shí)體
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@SuperBuilder(toBuilder = true)
@Data
public class BaseEntity {

    @TableField(value = "created_tm", fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdTm;

    @TableField(value = "created_by", fill = FieldFill.INSERT)
    private String createdBy;

    @TableField(value = "modified_tm", fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime modifiedTm;

    @TableField(value = "modified_by", fill = FieldFill.INSERT_UPDATE)
    private String modifiedBy;
}
  • 用戶賬號(hào)實(shí)體:UserEntity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@SuperBuilder(toBuilder = true)
@Data
@TableName("sys_user")
public class UserEntity extends BaseEntity{
    private Long userId;
    private String userName;
    private Integer sex;
    private Integer age;
    private String mobile;
}

Mapper 操作類

List<UserDTO> selectUsers();

UserEntity selectByIdOnXml(long userId);

@Results(id = "userResult", value = {
        @Result(property = "user_id", column = "userId", id = true),
        @Result(property = "userName", column = "user_name"),
        @Result(property = "sex", column = "sex"),
        @Result(property = "mobile", column = "mobile"),
        @Result(property = "age", column = "age")
})
@Select("select * from sys_user where user_id = #{id}")
UserEntity selectByIdOnSelectAnnotation(@Param("id") long id);

@SelectProvider(type = UserSqlProvider.class, method = "selectById")
@ResultMap("BaseResultMap")
UserEntity selectByIdOnSelectProviderAnnotation(long id);

@Select("select * from sys_user where user_id = #{id} and user_name=#{userName}")
@ResultMap("BaseResultMap")
UserEntity selectByIdOnParamAnnotation(@Param("id") long id, @Param("userName") String uerName);

Mapper 表映射文件

<mapper namespace="com.dunzung.mybatisplus.query.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.dunzung.mybatisplus.query.entity.UserEntity">
        <id column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="sex" property="sex"/>
        <result column="age" property="age"/>
        <result column="mobile" property="mobile"/>
    </resultMap>
    
    <resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
        <association property="card" column="{userId,user_id}"
                     select="com.dunzung.mybatisplus.query.mapper.CardMapper.selectCardByUserId"/>
        <collection property="orders" column="{userId,user_id}"
                    select="com.dunzung.mybatisplus.query.mapper.OrderMapper.selectOrders"/>
    </resultMap>
    
    <select id="selectUsers" resultMap="RelationResultMap">
        select * from sys_user
    </select>
    
    <select id="selectByIdOnXml" resultMap="BaseResultMap">
        select * from sys_user where user_id = #{userId}
   </select>
    
</mapper>
  • 訂單實(shí)體:OrderEntity
@Data
@TableName("sys_user_card")
public class CardEntity {
    private Long cardId;
    private String cardCode;
    private Long userId;
}

Mapper 操作類

@Mapper
public interface OrderMapper extends BaseMapper<OrderEntity> {
}

Mapper 表映射文件

<mapper namespace="com.dunzung.mybatisplus.query.mapper.OrderMapper">
    <resultMap id="BaseResultMap" type="com.dunzung.mybatisplus.query.entity.OrderEntity">
        <id column="order_id" property="orderId"/>
        <result column="order_name" property="orderName"/>
        <result column="user_id" property="userId"/>
        <result column="price" property="price"/>
        <result column="created_tm" property="createdTm"/>
    </resultMap>

    <select id="selectOrders" resultMap="BaseResultMap">
         select * from biz_order where user_id = #{userId}
    </select>

</mapper>
  • 身份證實(shí)體:CardEntity
@Data
@TableName("biz_order")
public class OrderEntity {
    private Long orderId;
    private String orderName;
    private Integer userId;
    private Date createdTm;
    private Integer price;
}

Mapper 操作類

@Mapper
public interface CardMapper extends BaseMapper<CardEntity> {
}

Mapper 表映射文件

<mapper namespace="com.dunzung.mybatisplus.query.mapper.CardMapper">
    <resultMap id="BaseResultMap" type="com.dunzung.mybatisplus.query.entity.CardEntity">
        <id column="card_id" property="cardId"/>
        <result column="card_code" property="cardCode"/>
        <result column="user_id" property="userId"/>
    </resultMap>
    <select id="selectCardByUserId" resultMap="BaseResultMap">
        select * from sys_user_card where user_id = #{userId}
    </select>
</mapper>

02 Lambda 基礎(chǔ)篇

lambda 構(gòu)建復(fù)雜的查詢條件構(gòu)造器:LambdaQueryWrapper

LambdaQueryWrapper 四種不同的 lambda 構(gòu)造方法

  • 方式一 使用 QueryWrapper 的成員方法方法 lambda 構(gòu)建 LambdaQueryWrapper
LambdaQueryWrapper<UserEntity> lambda = new QueryWrapper<UserEntity>().lambda();
  • 方式二 直接 new 出 LambdaQueryWrapper
LambdaQueryWrapper<UserEntity> lambda = new  LambdaQueryWrapper<>();
  • 方式三 使用 Wrappers 的靜態(tài)方法 lambdaQuery 構(gòu)建 LambdaQueryWrapper 推薦
LambdaQueryWrapper<UserEntity> lambda = Wrappers.lambdaQuery();
  • 方式四:鏈?zhǔn)讲樵?/li>
List<UserEntity> users = new LambdaQueryChainWrapper<UserEntity>(userMapper)
            .like(User::getName, "雨").ge(User::getAge, 20).list();

筆者推薦使用 Wrappers 的靜態(tài)方法 lambdaQuery 構(gòu)建 LambdaQueryWrapper 條件構(gòu)造器。

Debug 調(diào)試

為了 Debug 調(diào)試方便接奈,需要在 application.yml 啟動(dòng)文件開(kāi)啟 Mybatis-Plus SQL 執(zhí)行語(yǔ)句全棧打印:

#mybatis
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

執(zhí)行效果如下:

1 等值查詢:eq

@Test
public void testLambdaQueryOfEq() {
    //eq查詢
    //相當(dāng)于 select * from sys_user where user_id = 1
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getUserId, 1L);
    UserEntity user = userMapper.selectOne(lqw);
    System.out.println("eq查詢::" + user.getUserName());
}

eq 查詢等價(jià)于原生 sql 的等值查詢踢涌。

select * from sys_user where user_id = 1

2 范圍查詢 :in

@Test
public void testLambdaQueryOfIn() {  
    List<Long> ids = Arrays.asList(1L, 2L);
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();  
    lqw.in(UserEntity::getUserId, ids);
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("in查詢::" + u.getUserName()));
}

in 查詢等價(jià)于原生 sql 的 in 查詢

select * from sys_user where user_id in (1,2)  

3 通配符模糊查詢:like

@Test
public void testLambdaQueryOfLikeAll() {
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .like(UserEntity::getUserName, "dun");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like全包含關(guān)鍵字查詢::" + u.getUserName()));
}

like 查詢等價(jià)于原生 sql 的 like 全通配符模糊查詢。

select * from sys_user where sex = 0 and user_name like '%dun%'

4 右通配符模糊查詢:likeRight

@Test
public void testLambdaQueryOfLikeRight() { 
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .likeRight(UserEntity::getUserName, "dun");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like Right含關(guān)鍵字查詢::" + u.getUserName()));
}

likeRight 查詢相當(dāng)于原生 sql 的 like 右通配符模糊查詢序宦。

select * from sys_user where sex = 0 and user_name like 'dun%'

5 左通配符模糊查詢:likeLeft

@Test
public void testLambdaQueryOfLikeLeft() {  
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .likeLeft(UserEntity::getUserName, "zung");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like Left含關(guān)鍵字查詢::" + u.getUserName()));
}

likeLeft 查詢相當(dāng)于原生 sql 的 like 左通配符模糊查詢睁壁。

select * from sys_user where sex = 0 and user_name like '%zung'

6 條件判斷查詢

條件判斷查詢類似于 Mybatis 的 if 標(biāo)簽,第一個(gè)入?yún)?boolean condition 表示該條件是否加入最后生成的 sql 中互捌。

@Test
public void testLambdaQueryOfBoolCondition() {
    UserEntity condition = UserEntity.builder()
            .sex(1)
            .build();
    //eq 或 like 條件判斷查詢
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(condition.getSex() != null, UserEntity::getSex, 0L)
            // 滿足 bool 判斷潘明,是否進(jìn)查詢按字段 userName 查詢
            .like(condition.getUserName() != null, UserEntity::getUserName, "dun");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like查詢::" + u.getUserName()));
}

7 利用 or 和 and 構(gòu)建復(fù)雜的查詢條件

@Test
public void testLambdaQueryOfOr_And() {  
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .and(wrapper->wrapper.eq(UserEntity::getUserName,"dunzung")
                    .or().ge(UserEntity::getAge, 50));
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like查詢::" + u.getUserName()));
}

上面實(shí)例查詢等價(jià)于原生 sql 查詢:

select * from sys_user where sex = 0 and (use_name = 'dunzung' or age >=50)

8 善于利用分頁(yè)利器 PageHelpler

@Test
public void testLambdaPage() {
    //PageHelper分頁(yè)查詢
    //相當(dāng)于 select * from sys_user limit 0,2
    int pageNumber = 0;
    int pageSize = 2;
    PageHelper.startPage(pageNumber + 1, pageSize);
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.orderByAsc(UserEntity::getAge)
          .orderByDesc(UserEntity::getMobile);
    List<UserEntity> userList = userMapper.selectList(lqw); 
    userList.forEach(u -> System.out.println("page分頁(yè)查詢::" + u.getUserName()));
}

上面實(shí)例查詢等價(jià)于原生 sql 分頁(yè)查詢:

select * from sys_user order by age desc,mobile desc limit 0,2

另外,Mybatis-Plus 自帶分頁(yè)組件秕噪,BaseMapper 接口提供兩種分頁(yè)方法來(lái)實(shí)現(xiàn)物理分頁(yè)钳降。

  • 第一個(gè)返回實(shí)體對(duì)象允許 null
  • 第二個(gè)人返回 map 對(duì)象多用于在指定放回字段時(shí)使用,避免為指定字段 null 值出現(xiàn)
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);

注意巢价,Mybatis-Plus 自帶分頁(yè)組件時(shí)牲阁,需要配置 PaginationInterceptor 分頁(yè)插件。

@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

9 更新條件構(gòu)造器:LambdaUpdateWrapper

@Test
public void testLambdaUpdate() {
    LambdaUpdateWrapper<UserEntity> luw = Wrappers.lambdaUpdate();
    luw.set(UserEntity::getUserName, "dunzung01")
            .set(UserEntity::getSex, 1);
    luw.eq(UserEntity::getUserId, 1);
    userMapper.update(null, luw);
}

03 進(jìn)階篇

1. Association

Association 標(biāo)簽適用于表和表之間存在一對(duì)一的關(guān)聯(lián)關(guān)系壤躲,如用戶和身份證存在一個(gè)人只會(huì)有一個(gè)身份證號(hào),反過(guò)來(lái)也成立备燃。

@Test
public void testOnAssociationTag() {
    List<UserDTO> userList = userMapper.selectUsers();
    userList.forEach(u -> System.out.println(u.getUserName()));
}

XML配置

<resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
  <association property="card" column="{userId,user_id}"
               select="com.dunzung.mybatisplus.query.mapper.CardMapper.selectCardByUserId"/>
</resultMap>

2. Collection

Collection 標(biāo)簽適用于表和表之間存在一對(duì)多的關(guān)聯(lián)關(guān)系碉克,如用戶和訂單存在一個(gè)人可以購(gòu)買多個(gè)物品,產(chǎn)生多個(gè)購(gòu)物訂單并齐。

@Test
public void testOnCollectionTag() {
    List<UserDTO> userList = userMapper.selectUsers();
    userList.forEach(u -> System.out.println(u.getUserName()));
}

XML配置

<resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
  <collection property="orders" 
      column="{userId,user_id}" 
      select="com.dunzung.mybatisplus.query.mapper.OrderMapper.selectOrders"/>
  </resultMap>

注意 Association 和 Collection 先后關(guān)系漏麦,在編寫 ResultMap 時(shí),association 在前况褪,collection 標(biāo)簽在后撕贞。

 <resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
        <association property="card" column="{userId,user_id}"
                     select="com.dunzung.mybatisplus.query.mapper.CardMapper.selectCardByUserId"/>
        <collection property="orders" column="{userId,user_id}"
                    select="com.dunzung.mybatisplus.query.mapper.OrderMapper.selectOrders"/>
    </resultMap>

如果二者顛倒順序會(huì)提示錯(cuò)誤。

3. 元對(duì)象字段填充屬性值:MetaObjectHandler

MetaObjectHandler元對(duì)象字段填充器的填充原理是直接給 entity 的屬性設(shè)置值测垛,提供默認(rèn)方法的策略均為:

如果屬性有值則不覆蓋捏膨,如果填充值為 null 則不填充秉宿,字段必須聲明 TableField 注解跋破,屬性 fill 選擇對(duì)應(yīng)策略,該聲明告知 Mybatis-Plus 需要預(yù)留注入 SQL字段青扔。 TableField 注解則是指定該屬性在對(duì)應(yīng)情況下必有值锯七,如果無(wú)值則入庫(kù)會(huì)是 null链快。

自定義填充處理器 MyMetaObjectHandler 在 Spring Boot 中需要聲明 @Component 或 @Bean 注入,要想根據(jù)注解 FieldFill.xxx眉尸,如:

@TableField(value = "created_tm", fill = FieldFill.INSERT)
private LocalDateTime createdTm;

@TableField(value = "modified_tm", fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime modifiedTm;

和字段名以及字段類型來(lái)區(qū)分必須使用父類的 setInsertFieldValByName 或者 setUpdateFieldValByName 方法域蜗,不需要根據(jù)任何來(lái)區(qū)分可以使用父類的 setFieldValByName 方法 巨双。

/**
 * 屬性值填充 Handler
 *
 * @author 猿芯
 * @since 2021/3/30
 */
@Component
public class FillMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setInsertFieldValByName("createdTm", LocalDateTime.now(), metaObject);
        this.setInsertFieldValByName("createdBy", MvcContextHolder.getUserName(), metaObject);
        this.setFieldValByName("modifiedTm", LocalDateTime.now(), metaObject);
        this.setFieldValByName("modifiedBy", MvcContextHolder.getUserName(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setUpdateFieldValByName("modifiedTm", LocalDateTime.now(), metaObject);
        this.setUpdateFieldValByName("modifiedBy", MvcContextHolder.getUserName(), metaObject);
    }
}

一般 FieldFill.INSERT 用父類的 setInsertFieldValByName 方法更新創(chuàng)建屬性(創(chuàng)建人、創(chuàng)建時(shí)間)值霉祸;FieldFill.INSERT_UPDATE 用父類的 setUpdateFieldValByName 方法更新修改屬性(修改人炉峰、修改時(shí)間)值;如果想讓諸如 FieldFill.INSERT 或 FieldFill.INSERT_UPDATE 任何時(shí)候不起作用脉执,用父類的 setFieldValByName 設(shè)置屬性(創(chuàng)建人疼阔、創(chuàng)建時(shí)間、修改人半夷、修改時(shí)間)值即可婆廊。

微信搜公眾號(hào)「猿芯」,后臺(tái)私信回復(fù) 1024 免費(fèi)領(lǐng)取 SpringCloud巫橄、SpringBoot淘邻,微信小程序、Java面試湘换、數(shù)據(jù)結(jié)構(gòu)宾舅、算法等全套視頻資料。

4. 自定義SQL

使用 Wrapper 自定義 SQL 需要 mybatis-plus 版本 >= 3.0.7 彩倚,param 參數(shù)名要么叫 ew筹我,要么加上注解 @Param(Constants.WRAPPER) ,使用 ${ew.customSqlSegment} 不支持 Wrapper 內(nèi)的 entity生成 where 語(yǔ)句帆离。

注解方式

@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

XML配置

List<MysqlData> getAll(Wrapper ew);
<select id="getAll" resultType="MysqlData">
 SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>

四蔬蕊、Mybatis-Plus lambda 表達(dá)式的優(yōu)勢(shì)與劣勢(shì)

通過(guò)上面豐富的舉例詳解以及剖析 lambda 底層實(shí)現(xiàn)原理,想必大家會(huì)問(wèn):” lambda 表達(dá)式似乎只支持單表操作哥谷?”

據(jù)我對(duì) Mybatis-Plus 官網(wǎng)的了解岸夯,目前確實(shí)是這樣。依筆者實(shí)際運(yùn)用經(jīng)驗(yàn)來(lái)看们妥,其實(shí)程序員大部分開(kāi)發(fā)的功能基本上都是針對(duì)單表操作的猜扮,Lambda 表達(dá)式的優(yōu)勢(shì)在于幫助開(kāi)發(fā)者減少在 XML 編寫大量重復(fù)的 CRUD 代碼,這點(diǎn)是非常重要的 nice 的监婶。很顯然旅赢,Lambda 表達(dá)式對(duì)于提高程序員的開(kāi)發(fā)效率是不言而喻的,我想這點(diǎn)也是我作為程序員非常喜歡 Mybatis-Plus 的一個(gè)重要原因压储。

微信搜公眾號(hào)「猿芯」鲜漩,后臺(tái)私信回復(fù) 1024 免費(fèi)領(lǐng)取 SpringCloud、SpringBoot集惋,微信小程序孕似、Java面試、數(shù)據(jù)結(jié)構(gòu)刮刑、算法等全套視頻資料喉祭。

但是养渴,如果涉及對(duì)于多表之間的關(guān)聯(lián)查詢,lambda 表達(dá)式就顯得力不從心了泛烙,因?yàn)?Mybatis-Plus 并沒(méi)有提供類似于 join 查詢的條件構(gòu)造器理卑。

lambda 表達(dá)式優(yōu)點(diǎn):

  1. 單表操作,代碼非常簡(jiǎn)潔蔽氨,真正做到零配置藐唠,如不需要在 xml 或用注解(@Select)寫大量原生 SQL 代碼
  2. 并行計(jì)算
  3. 預(yù)測(cè)代表未來(lái)的編程趨勢(shì)

lambda 表達(dá)式缺點(diǎn):

  1. 單表操作,對(duì)于多表關(guān)聯(lián)查詢支持不好
  2. 調(diào)試?yán)щy
  3. 底層邏輯復(fù)雜

五鹉究、總結(jié)

Mybatis-Plus 推出的 lambda 表達(dá)式致力于構(gòu)建復(fù)雜的 where 查詢構(gòu)造器式并不是銀彈宇立,它可以解決你實(shí)際項(xiàng)目中 80% 的開(kāi)發(fā)效率問(wèn)題,但是針對(duì)一些復(fù)雜的大 SQL 查詢條件支持地并不好自赔,例如一些復(fù)雜的 SQL 報(bào)表統(tǒng)計(jì)查詢妈嘹。

所以,筆者推薦單表操作用 lambda 表達(dá)式绍妨,查詢推薦用 LambdaQueryWrapper润脸,更新用 LambdaUpdateWrapper;多表操作還是老老實(shí)實(shí)寫一些原生 SQL 他去,至于原生 SQL 寫在哪里毙驯? Mapper 文件或者基于注解,如 @Select 都是可以的孤页。

參考

作者:猿芯

來(lái)源:https://www.toutiao.com/i6951307172588388876/

本文首發(fā)于公眾號(hào):Java版web項(xiàng)目尔苦,歡迎關(guān)注獲取更多精彩內(nèi)容

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市行施,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魂那,老刑警劉巖蛾号,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異涯雅,居然都是意外死亡鲜结,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門活逆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)精刷,“玉大人,你說(shuō)我怎么就攤上這事蔗候∨剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵锈遥,是天一觀的道長(zhǎng)纫事。 經(jīng)常有香客問(wèn)我勘畔,道長(zhǎng),這世上最難降的妖魔是什么丽惶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任炫七,我火速辦了婚禮,結(jié)果婚禮上钾唬,老公的妹妹穿的比我還像新娘万哪。我一直安慰自己,他們只是感情好抡秆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布奕巍。 她就那樣靜靜地躺著,像睡著了一般琅轧。 火紅的嫁衣襯著肌膚如雪伍绳。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天乍桂,我揣著相機(jī)與錄音冲杀,去河邊找鬼。 笑死睹酌,一個(gè)胖子當(dāng)著我的面吹牛权谁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播憋沿,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旺芽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了辐啄?” 一聲冷哼從身側(cè)響起采章,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壶辜,沒(méi)想到半個(gè)月后悯舟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砸民,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抵怎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岭参。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡反惕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出演侯,到底是詐尸還是另有隱情姿染,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布蚌本,位于F島的核電站盔粹,受9級(jí)特大地震影響隘梨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舷嗡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一轴猎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧进萄,春花似錦捻脖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至援雇,卻和暖如春矛渴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惫搏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工具温, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筐赔。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓铣猩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親茴丰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子达皿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Mybatis-Plus(MP)在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,簡(jiǎn)化開(kāi)發(fā)贿肩、提高效率峦椰。 本篇是根據(jù)My...
    h2coder閱讀 922評(píng)論 0 1
  • 一、MyBatis vs JPA 1汰规、MyBatis優(yōu)勢(shì) (1)自由控制 SQL語(yǔ)句们何,可手動(dòng)優(yōu)化SQL語(yǔ)句 (2)...
    sdouclzh閱讀 4,916評(píng)論 0 0
  • MyBatis Plus 本文檔基于 MyBatis-Plus 官方文檔編寫,詳情請(qǐng)參見(jiàn) MyBatis-Plus...
    青丶空閱讀 2,084評(píng)論 3 17
  • Mybatis-Plus(簡(jiǎn)稱MP)是一個(gè)Mybatis的增強(qiáng)工具控轿,在Mybatis的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)...
    故里學(xué)Java閱讀 309評(píng)論 0 3
  • MybatisPlus 特性 無(wú)侵入:只做增強(qiáng)不做改變拂封,引入它不會(huì)對(duì)現(xiàn)有工程產(chǎn)生影響茬射,如絲般順滑 損耗小:?jiǎn)?dòng)即會(huì)...
    njitzyd閱讀 586評(píng)論 0 0