mybatis - 通用mapper



title: 玩轉spring-boot-mybatis
date: 2019-03-11 19:36:57
type: "mybatis"
categories: mybatis #分類名
tags: mybatis

作為持久層的ORM框架,目前在國內主流之一就是MyBatis,學會用它,用好它肯定是必備的功課

我會主要從下面幾個方面入整理本篇博客

  1. 快速搭建快發環境
  2. 常見的注解
  3. 怎么玩?

一. 快速搭建開發環境

小插曲,添加測試模塊的時候,引入junit模塊和spring-boot-text-starter模塊有先順序,不然ide會報錯...

坐標

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/><!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--通用mapper啟動器-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.3</version>
        </dependency>
        <!--分頁助手-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.5</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!--測試-->
        <!--先添加 junit  再添加下面的text  stater-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>text</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
  • 另外插一嘴---mysql連接的版本適配

我現在用的雲主機docker上的官方版mysql,版本比較新,因此我的調整版本到 8以上,不然會報錯說什么

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could

配置文件:

  • 主要是配置數據庫的連接,如果我們使用的是通用mapper,Mybatis可以做到零配置
server:
  port: 8089
spring:
  application:
    name: text-mybatis
  datasource:
    url: jdbc:mysql://211.159.XXX.XXX:8888/changwu?serverTimezone=UTC&useUnijava=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 2424zcw..
    driver-class-name: com.mysql.jdbc.Driver
#輸出sql
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

啟動類

  • 在這里告訴通用mapper我們的mapper包路徑
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.changwu.mapper")
public class MybatisApp {
    public static void main(String[] args) {
        SpringApplication.run(MybatisApp.class);
    }
}

編寫Mapper

  • 讓我們自己的Mapper繼承通用mapper,泛型是實體類名
  • 給它實體類名,就相當與告訴它了我們的表里的字段,不讓他怎么會知道如何動態生成sql ?
import tk.mybatis.mapper.common.Mapper;
public interface MyMapper extends Mapper<MyBrand> {}

ok,到現在環境就搭建好了


二. 常見的注解

  • 這里的注解主要是作用在實體類上的注解,當我們擼起袖子寫代碼的時候,被給它難住嘍,尷尬

情景1: 對於實體類

標記他對應那張表

import javax.persistence.Table;
@Table(name = "tb_brand")

情景2: 對於主鍵

大多數情況主鍵一般都叫id,bigint類型,沒什么超級特殊的情況我們都希望他可以自己增長,下面兩個注解都可以做到這件事,但是前提表id屬性必須設置成 autoincreament , 不然報錯說,id不能不寫

  • 注解1:
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/*
*  使用的是jpa的策略生成器
*JPA提供的四種標准用法為TABLE,SEQUENCE,IDENTITY,AUTO. 
* TABLE:使用一個特定的數據庫表格來保存主鍵。 
* SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列
* IDENTITY:主鍵由數據庫自動生成(主要是自動增長型) 
* AUTO:主鍵由程序控制。
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) 
  • 注解2:

直接使用Mybatis的原生注解

@Id
@KeySql(useGeneratedKeys = true)
private Long id;

情景3:

我們都知道springMVC會把前端發送過來的json轉化為我們的javaBean,因此我們的javaBean里面的屬性名和前端發送的那個對象的屬性名要意義對應的,這是規范!

設想,加入前端一頓收集數據,把商品的品牌信息和庫存信息都發送過來了,只有關於品牌的javabean,數據接受不全怎么辦? 沒關系,下面的注解可以搞定

  • @Transient (意味短暫的)告訴mapper 他不是PO(持久層對象,Persistence Object)的屬性,而且,springMvc還會把信息關於庫存的信息,封裝進去
@Transient

情景4:

我們的pojo屬性名,和數據庫中的關鍵字重名怎么辦?

@Column() 可以解決 數據庫中的字段為數據庫的關鍵字的問題

@Column(name="`numeric`")  //
private Boolean numeric;   //是否是數字類型

情景5 :

數據表中tingint(1) 對應的javaBean的屬性咋寫?

tingint(1) // 它是boolean的同義詞

情景5:

關於VO , 如何選擇性的返回給前端javaBean的屬性?就比如說我們查詢有沒有這個用戶,總不至於把密碼,私人信息一塊返回給前端吧?

import com.fasterxml.jackson.annotation.JsonIgnore;
@JsonIgnore

它是jackson的注解

  <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>RELEASE</version>
        <scope>compile</scope>
    </dependency>

三. 怎么玩?

 下面就是介紹常用的mapper的API

一. CRUD

  • 兩種新增
//會有選擇性的新增, 智能判斷 brand里面有沒有空的字段,有值,用這個值,沒有值而使用數據庫的默認值
insertSelective(brand) 

// 假如前端提交過來的 brand里面的數據是全的,用詞方法
brandMapper.insert(brand);
  • 簡單查詢
/**
 * 根據實體中的屬性值進行查詢,查詢條件使用等號
 * @param record
 * @return
 */
@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
List<T> select(T record);


/**
 * 根據實體中的屬性值進行查詢,查詢條件使用等號
 * @param record
 * @return
 */
@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
List<T> select(T record);


/**
 * 查詢全部結果
 * @return
 */
@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
List<T> selectAll();


/**
 * 根據實體中的屬性進行查詢,只能有一個返回值,有多個結果是拋出異常,查詢條件使用等號
 * @param record
 * @return
 */
@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
T selectOne(T record);


/**
 * 根據實體中的屬性查詢總數,查詢條件使用等號
 * @param record
 * @return
 */
@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
int selectCount(T record);
  • 簡單刪除
/**
 * 根據主鍵字段進行刪除,方法參數必須包含完整的主鍵屬性
 * @param key
 * @return
 */
@DeleteProvider(type = BaseDeleteProvider.class, method = "dynamicSQL")
int deleteByPrimaryKey(Object key);


/**
 * 根據實體屬性作為條件進行刪除,查詢條件使用等號
 * @param record
 * @return
 */
@DeleteProvider(type = BaseDeleteProvider.class, method = "dynamicSQL")
int delete(T record);

  • 如何玩修改?

修改主要用到下面這個api

/**
 * 根據主鍵更新屬性不為null的值
 * @param record
 * @return
 */
@UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL")
int updateByPrimaryKeySelective(T record);

看到這個方法有沒有覺得很爽? 它根據主鍵,修改不為null的屬性,意思就是說,我們把不需要修改的地方設置為null就好了
,這樣看,前面需要我們一頓整,整啥呢? 結合實際的業務邏輯,把前端給交過來的數據最新的數據放到我們的bean中直接修改,把不需要修改的屬性設置為null,有屬性可能需要直接刪除,有的屬性可能要去別的表中查詢(別忘了加上事務 @Transactional )

/**
 * 根據主鍵更新實體全部字段,null值會被更新
 * @param record
 * @return
 */
@UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL")
int updateByPrimaryKey(T record);

這個方法和上面的神似

二 .分頁,過濾(模糊查詢),搜索

API

mapper.selectByExample(example)

參照下面原生的sql.拼接查詢條件

 select * from 表名
        where name like '% X %' or  letter== 'x'
        order by  id desc

邏輯

public VO queryBrandByPage(Integer page, String key, Integer rows, String sortBy, Boolean desc) {
    //分頁  -- 使用分頁助手,在我們真正查詢之前,調用這個下面的方法,開啟分頁查詢,他很智能,會用mybatis的攔截器
    // 對接下來要執行查詢的sql進行攔截,自動的在其后面拼接  limit語句
    PageHelper.startPage(page,rows); //當前頁碼,每頁顯示的數目

    //過濾 --key     (StringUtils用的是comment lang3 下面的)
    // 過濾條件,key是前端用戶傳遞進來的,可能僅僅是一個 小米,  也可能是 小 ---  模糊查詢
   /*   select * from tb_brand
    where name like '% X %' or  letter== 'x'
    order by  id desc
    */
    Example example = new Example(Brand.class);
    if(StringUtils.isNotBlank(key)){  //不為空,過濾
       example.createCriteria().orLike("name","%"+key+"%").andEqualTo("letter",key.toUpperCase());
      //  example.createCriteria().orLike("name","%"+key+"%").or
    }

    //排序
    if(StringUtils.isNotBlank(sortBy)) { //傳遞進來的排序不為空,設置我們的排序條件
        // 上面的 order by 可以幫我們生成, 但是后面的 id  desc 需要我們自己寫
        // String orderByClause = "id desc" ; 寫死了
        String orderByClause = sortBy + (desc ? " DESC " : " ASC ");  //坑坑坑   注意要加上 空格 不然拼接完了 就是 orderBy idASC 而不是orderBy id ASC
        example.setOrderByClause(orderByClause);
    }

    //查詢 獲取到list ,其實是  當前頁的數據 page對象
    List<Brand> list = brandMapper.selectByExample(example);
    if (CollectionUtils.isEmpty(list)){
        throw new Exception ;
    }

   // 解析List
    PageInfo<Brand> pageInfo = new PageInfo<>(list);

    return new PageResult<Brand>(pageInfo.getTotal(),list);
}

三 .自定義sql

Mybatis只能針對單表為我們生成sql,如果我們的需求是跨表操作,比如說涉及到兩張表,我們就得去mapper里面自己寫sql

原生sql

select * from tb_brand b
inner join tb_category_brand cb on b.id=cb.brand_id  -- 去笛卡爾積
where cb.category_id= ? ;

mapper

?用#{id} 取代

@Select("select * from tb_brand b inner join tb_category_brand cb on b.id=cb.brand_id where cb.category_id=#{cid}")
  List<Brand>  queryBrandByCid(@Param("cid") Long cid);

四 .拓展包--批量操作

  • 批量查詢,批量刪除

注意他的包啊!

import tk.mybatis.mapper.additional.idlist.IdListMapper;
public interface Mapper extends Mapper<Category> , IdListMapper<Category,Long> {}
/**
 * 根據主鍵字符串進行查詢,類中只有存在一個帶有@Id注解的字段
 * @param idList
 * @return
 */
@SelectProvider(type = IdListProvider.class, method = "dynamicSQL")
List<T> selectByIdList(@Param("idList") List<PK> idList);

/**
 * 根據主鍵字符串進行刪除,類中只有存在一個帶有@Id注解的字段
 * @param idList
 * @return
 */
@DeleteProvider(type = IdListProvider.class, method = "dynamicSQL")
int deleteByIdList(@Param("idList") List<PK> idList);

  • 批量插入:
import tk.mybatis.mapper.additional.insert.InsertListMapper;
public interface BaseMapper<T> extends InsertListMapper<T> {}


@RegisterMapper
public interface InsertListMapper<T> {
@InsertProvider(
    type = InsertListProvider.class,
    method = "dynamicSQL"
)
int insertList(List<? extends T> var1);
}

抽取出一個baseMapper, 添加 @RegisterMapper 它才會被掃描到生效

import tk.mybatis.mapper.additional.idlist.IdListMapper;
import tk.mybatis.mapper.additional.insert.InsertListMapper;
import tk.mybatis.mapper.annotation.RegisterMapper;
import tk.mybatis.mapper.common.Mapper;

@RegisterMapper
public interface BaseMapper<T> extends IdListMapper<T,Long>,Mapper<T>, InsertListMapper<T> {
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM