【Java架構:基礎技術】一篇文章搞掂:MyBatis-Plus


前言

Mybatis:一個流行的半自動ORM框架。

Mybatis-Plus:一個Mybatis的增強工具,在Mybatis的基礎上只做增強不做改變,為簡化開發、提高效率而生。

本文使用的版本:mybatis-plus 3.0.5

本文使用的環境:Spring Boot(在其他環境下使用請參考官方文檔)、SQLServer 2008

官方文檔:http://mp.baomidou.com

我的例子:https://gitee.com/Louisyzh/MyBatis-Plus-Demos.git

建議數據庫結構:無外鍵,主鍵為同名自增id、或同名的長字符列並由MP生成唯一主鍵(這是當前的一種流行做法,也比較容易和mp集成,如果表結構太復雜,有些地方會有坑)

一、Spring Boot下最簡潔例子

項目結構

1.1、通過Maven加載依賴

Pom文件內包含的依賴和插件解釋如下:

  • Spring Boot自帶依賴和插件(使用Spring Boot初始化器創建項目的話會自帶的依賴)
    • spring-boot-starter-parent
    • spring-boot-starter
    • spring-boot-starter-test
    • spring-boot-maven-plugin
  • 代碼簡化依賴(可有可無,只是個人習慣)
    • lombok
  • mybatis-plus依賴
    • mybatis-plus-boot-starter
  • mysql依賴
    • mysql-connector-java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.louistech</groupId>
    <artifactId>demo02-start-with-mysql</artifactId>
    <version>1.0-SNAPSHOT</version>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </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>
            <scope>provided</scope>
        </dependency>
        <!--MySQL依賴-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
附:POM文件

1.2、創建實體

package com.mybatisplus.demo02.entity;
import lombok.Data;
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

1.3、創建mapper接口

package com.mybatisplus.demo02.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mybatisplus.demo02.entity.User;
public interface UserMapper extends BaseMapper<User> {
}

1.3、主程序上增加mapper的掃描位置

@SpringBootApplication
@MapperScan("com.mybatisplus.demo02.mapper")
public class Demo02Application {
    public static void main(String[] args) {
        SpringApplication.run(Demo02Application.class, args);
    }
}

1.4、配置application.yml文件

配置一個datasource,供mysql連接器與數據庫進行連接

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password:

1.5、創建測試代碼,訪問數據庫讀取數據

@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
    @Autowired
    private UserMapper userMapper;
    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }
}

二、MyBatis Plus配置

對MP進行配置可以分為3個角度去看

  • application.yml配置
  • MyBatis原生配置
  • Java Bean配置

2.1、application.yml配置

來源:在Spring Boot下,MP帶來的mybatis-plus-boot-starter起步依賴中所支持在application.yml中選擇的配置

具體:可以查看官方文檔

注意:其中一些枚舉值,可以Ctrl+左鍵,進去源碼查看具體可選的值

一個最詳盡帶注解application.yml配置:

# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password:

# Logger Config
logging:
  level:
    com.mybatisplus.demo03: debug

#MP所支持的application.yml設置 具體參考官方文檔https://jobob.gitee.io/mp3doc/中設置部分
mybatis-plus:
#  config-location: classpath:mybatis/mybatis-config.xml #如果需要使用原生mybatis設置文件,則這里需要指明具體路徑
#  mapper-locations: classpath:/mybatis/mapper/*DAO.xml #在resource目錄下的寫法
#  mapper-locations: classpath:/com/mpspringboot/mapper/xml/*Mapper.xml #在src/main/java下的寫法(同時配置POM文件中source屬性)
#  mapper-locations: classpath*:/mybatis/mapper/*DAO.xml #Maven多項目模塊下使用classpath*寫法
#  type-aliases-package: com.XX.entity #設置類名別名掃描位置,設置后可使用類名替代全限定類名,多個package用逗號或者分號分隔
#  type-aliases-super-type: java.lang.Object #請和typeAliasesPackage一起使用,設置后僅會掃描路徑下以該類作為父類的域對象 。
#  type-handlers-package: com.XX.handler #設置類型轉換類所在的包位置
#  type-enums-package: com.XX.enums #枚舉字段掃描,支持實體的枚舉字段
#  check-config-location: false #啟動時是否檢查 MyBatis XML 文件的存在,默認不檢查。
#  executor-type: simple #通過該屬性可指定 MyBatis 的執行器,默認值為SIMPLE,MyBatis 的執行器總共有三種:
#                         ExecutorType.SIMPLE:該執行器類型不做特殊的事情,為每個語句的執行創建一個新的預處理語句(PreparedStatement)
#                         ExecutorType.REUSE:該執行器類型會復用預處理語句(PreparedStatement)
#                         ExecutorType.BATCH:該執行器類型會批量執行所有的更新語句
#  configuration-properties: classpath:mybatis/config.properties #指定外部化 MyBatis Properties 配置,通過該配置可以抽離配置,實現不同環境的配置部署。

  #MyBatis-Plus 全局策略配置
  global-config:
    refresh: true #啟動后,修改Target中的XML即可更新對應Mapper的邏輯,用於調試;生產中不要啟動
#    sql-parser-cache: true #是否緩存 Sql 解析,默認不緩存。
#    sql-session: com.xxx.SqlSession #單例重用 SqlSession
#    sql-session-factory: com.xxx.SqlSessionFactory #

    #全局配置中關於DB的設置
    db-config:
      db-type: MYSQL #數據庫類型
      capital-mode: true #是否開啟大寫命名,開啟后生成SQL語句都為大寫;默認不開啟。
#      table-prefix: sys #生成的SQL會在表名上增加此前綴
      table-underline: true #生成的SQL語句中,表名是否自動加入駝峰轉下划線(如SystemUser=>system_user)
      field-strategy: NOT_NULL #字段更新插入策略  0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷"
#                               IGNORED:所有字段都更新和插入
#                               NOT_NULL:只更新和插入非NULL值
#                               NOT_EMPTY:只更新和插入非NULL值且非空字符串
#                               DEFAULT:默認NOT_NULL
      id-type: UUID #主鍵類型  0:"數據庫ID自增", 1:"用戶輸入ID",2:"全局唯一ID (數字類型唯一ID)", 3:"全局唯一ID UUID";
#                               AUTO(0):MP自動決定
#                               NONE(1):生成語句插入null,需要數據庫自增時可以使用
#                               INPUT(2):根據用戶輸入值
#                               ID_WORKER(3):全局唯一ID (數字類型唯一ID)
#                               UUID(4):全局唯一ID UUID
#                               ID_WORKER_STR(5):全局唯一ID (字符型類型唯一ID)
      column-like: false #邏輯刪除字段表示未刪除的值
      logic-delete-value: 1 #邏輯刪除字段表示刪除的值
      logic-not-delete-value: 0 #邏輯刪除字段表示未刪除的值


  #一部分對原生MyBatis所支持的配置,我建議使用config-location加mybatis-config.xml實現比較清晰,不要在這里使用
#  configuration:
#    mapUnderscoreToCamelCase: true #默認true,是否開啟自動駝峰命名規則(camel case)映射,即從經典數據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射。
#    aggressive-lazy-loading: true #當設置為 true 的時候,懶加載的對象可能被任何懶屬性全部加載,否則,每個屬性都按需加載。需要和 lazyLoadingEnabled 一起使用。
#    auto-mapping-unknown-column-behavior: none #MyBatis 自動映射策略,通過該配置可指定 MyBatis 是否並且如何來自動映射數據表字段與對象的屬性,總共有 3 種可選值:
#                                               AutoMappingBehavior.NONE:不啟用自動映射
#                                               AutoMappingBehavior.PARTIAL:只對非嵌套的 resultMap 進行自動映射
#                                               AutoMappingBehavior.FULL:對所有的 resultMap 都進行自動映射
#    auto-mapping-behavior: partial #MyBatis 自動映射時未知列或未知屬性處理策略,通過該配置可指定 MyBatis 在自動映射過程中遇到未知列或者未知屬性時如何處理,總共有 3 種可選值:
#                                  AutoMappingUnknownColumnBehavior.NONE:不做任何處理 (默認值)
#                                  AutoMappingUnknownColumnBehavior.WARNING:以日志的形式打印相關警告信息
#                                  AutoMappingUnknownColumnBehavior.FAILING:當作映射失敗處理,並拋出異常和詳細信息
#    cache-enabled: true #全局地開啟或關閉配置文件中的所有映射器已經配置的任何緩存,默認為 true。
#    call-setters-on-nulls: false #指定當結果集中值為 null 的時候是否調用映射對象的 Setter(Map 對象時為 put)方法,通常運用於有 Map.keySet() 依賴或 null 值初始化的情況。
#                                  通俗的講,即 MyBatis 在使用 resultMap 來映射查詢結果中的列,如果查詢結果中包含空值的列,則 MyBatis 在映射的時候,不會映射這個字段,這就導致在調用到該字段的時候由於沒有映射,取不到而報空指針異常。
#                                  當您遇到類似的情況,請針對該屬性進行相關配置以解決以上問題。
#                                  WARNING
#                                  基本類型(int、boolean 等)是不能設置成 null 的。
#    configuration-factory: com.xxx.SampleConfigurationFactory #指定一個提供 Configuration 實例的工廠類。
                                    #該工廠生產的實例將用來加載已經被反序列化對象的懶加載屬性值,其必須包含一個簽名方法static Configuration getConfiguration()。(從 3.2.3 版本開始)

2.2、MyBatis原生配置

原生MyBatis可以通過一個XML進行屬性的配置,而使用MyBatis-Plus后,可以在application.yml中通過 mybatis-plus  config-location 來進行設置

具體原生的XML應該怎么寫,這里就不介紹了,可以查看官方文檔http://www.mybatis.org/mybatis-3/zh/configuration.html

2.3、Java Bean配置

MyBatis-Plus中有一些功能,可以/需要使用Java Bean配置。

具體在下面的一些例子中能夠體現,或者查看官方文檔http://mp.baomidou.com/guide/

三、分頁插件

需求分析:需要查詢一個關聯表,分頁查詢其關聯結果,並能傳入參數過濾

1、注入分頁插件Bean

在一個@Configuration配置類下或主程序下,創建一個分頁插件Bean

package com.louistech.inventory.config.mybatis;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }
}

 2、

 

 

-------------以下內容未整理----------------

pom文件加入依賴

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0-RC</version>
</dependency>
<!--數據庫驅動,對應自己使用的數據庫即可-->
<dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>sqljdbc4</artifactId>
    <version>4.0</version>
</dependency>
<!--如果生成時設置使用lombok插件,則需要加入插件依賴-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.14</version>
    <scope>provided</scope>
</dependency>

如果mapper.xml文件放在java文件夾下而非resources文件夾下,還需要在POM文件增加

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/mapper/**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

application.yml配置文件設置

spring:
datasource:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://127.0.0.1;database=dbname
username: sa
password: sa
mybatis-plus:
#  mapper-locations: classpath:/com/mpspringboot/mapper/xml/*Mapper.xml #在src/main/java下的寫法(同時配置POM文件中source屬性)
  mapper-locations: classpath:/mybatis/mapper/*Mapper.xml #在resource目錄下的寫法
typeAliasesPackage: com.ltmicro.services.material.entity #實體掃描,多個package用逗號或者分號分隔
# typeEnumsPackage: com.baomidou.springboot.entity.enums #枚舉字段掃描,支持實體的枚舉字段(看官網Spring Boot實例)
global-config:
db-config:
#已經過測試
db-type: SQL_SERVER #數據庫類型
column-underline: false #生成的SQL語句中,字段是否自動加入下划線

#未經過測試
id-type: id_worker #主鍵類型 0:"數據庫ID自增", 1:"用戶輸入ID",2:"全局唯一ID (數字類型唯一ID)", 3:"全局唯一ID UUID";
field-strategy: not_empty #字段策略 0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷"
capital-mode: true
logic-delete-value: 0
logic-not-delete-value: 1
refresh: true #啟動后,修改Target中的XML即可馬上更新,用於調試;生產中不要啟動
configuration:
map-underscore-to-camel-case: true
cache-enabled: false

創建配置類,添加Mapper掃描注解(包括項目的mapper接口和"com.baomidou.mybatisplus.core.mapper"(用於生成的Service層)),以及添加分頁插件Bean

@Configuration
@MapperScan({"com.mpspringboot.mapper","com.baomidou.mybatisplus.core.mapper"})
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }
}

三、API使用

根據自己創建或者是代碼生成器生成,代碼中最少有Entity類、Mapper接口2種類,另外還可以生成Service接口和Service實現,以及XML文件。

Mapper和Service接口預設了很多API用於操作數據庫,在下面介紹。

 

復雜實例

1、創建一個XML,支持分頁,排序,自定義條件過濾,別名排序,別名字段過濾

這個例子中,以下幾點稍微解釋以下

  • 使用IPage參數傳入分頁信息即可分頁
  • 使用Wrapper編寫過濾條件、排序,與XML中and ${ew.sqlSegment}對應,即可傳入過濾條件、排序
  • XML中給了一個默認的orderby,我使用的mybatis-plus(3.0-RC)和JSqlPraser(1.3-SNAPSHOT)版本下,竟然會在Wrapper傳入排序條件時,自動屏蔽這個默認orderby,這個我很高興

XML:

<select id="selectByConditions" resultType="java.util.Map">
    select
    isnull(A.Code,'') as CodeZ,
    A.Name as PName,
    isnull(A.Size,'') as Size
    from
    Product A
    left join
    ProductType B on A.TypeId = B.TypeId
    <where>
        <bind name="searchText" value="conditions.get('searchtext')"/>
        <if test="searchText != null and searchText != ''">
            and
            (
            A.Code like '%'+#{searchText}+'%' or
            A.Size like '%'+#{searchText}+'%'
            )
        </if>
        <bind name="typeFullId" value="conditions.get('typefullid')"/>
        <if test="typeFullId != null and typeFullId != ''">
            and (B.FullRowId = #{typeFullId} or B.FullRowId like
            #{typeFullId}+'.%')
        </if>
        and ${ew.sqlSegment}
    </where>
    order by code
</select>

mapper接口:

List<Map<String, Object>> selectByConditions(IPage page, @Param("ew") Wrapper wrapper, @Param("conditions") Map<String, Object> conditions);

調用:

Map<String, Object> conditions = new HashMap<String, Object>();
//測試【非字段條件】
conditions.put("searchtext", "百威獅 手動釘槍");

QueryWrapper<Product> wrapper = new QueryWrapper<Product>();
wrapper.and(
        (x) -> x
                .like("PName", "百威獅 手動釘槍")
                //【前台字段搜索】【公式字段搜索】
                .like("CodeZ", "227")
);
//【前台排序】
wrapper.orderByDesc("PName");
//【公式字段排序】
wrapper.orderByDesc("CodeZ");

List<Map<String, Object>> pageResult = productMapper.selectByConditions(
        new Page(1, 2),
        wrapper,
        conditions);

實踐問題處理

1、支持別名條件查詢和別名排序

思路:在SQL執行前,把order by和where語句中的使用別名的字段轉換成表字段

方案:通過分頁配置器,注入SQL解析器

創建一個SQL解析器:

package com.mpspringboot.Config;

import com.baomidou.mybatisplus.core.parser.AbstractJsqlParser;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AliasSqlParser extends AbstractJsqlParser {

    @Override
    public void processSelectBody(SelectBody selectBody) {
        if (selectBody instanceof PlainSelect) {
            PlainSelect select = (PlainSelect) selectBody;

            //先找出所有有別名的字段,其真實字段可能是
            // Column(如A.Code as ACode)
            // 或者是Function(如isnull(Code,'') as ACode)
            Map<String, Expression> aliasMap = GetAliasColumns(select);

            //如果Order語句中使用了別名,則從字段中,找到真實字段(字段或公式)並替換
            UpdateOrderByColumnName(aliasMap, select);
            //如果Where語句中使用別名,則從字段中,找到真實字段(字段或公式)並替換
            UpdateWhereColumnName(aliasMap, select, select.getWhere(), select.getWhere(), "");
        }
    }

    //先找出所有有別名的字段,其真實字段可能是
    // Column(如A.Code as ACode)
    // 或者是Function(如isnull(Code,'') as ACode)
    private Map<String, Expression> GetAliasColumns(PlainSelect select) {
        Map<String, Expression> map = new HashMap<String, Expression>();
        List<SelectItem> items = select.getSelectItems();
        for (int i = 0; i < items.size(); i++) {
            SelectItem item = items.get(i);
            if (item instanceof SelectExpressionItem) {
                SelectExpressionItem selectItem = (SelectExpressionItem) item;
                if (null != selectItem.getAlias()) {
                    String alias = selectItem.getAlias().getName();
                    Expression realCol = selectItem.getExpression();
                    map.put(alias, realCol);
                }
            }
        }
        return map;
    }

    //如果Order語句中使用了別名,則從字段中,找到真實字段(字段或公式)並替換
    private void UpdateOrderByColumnName(Map<String, Expression> aliasMap, PlainSelect select) {
        List<OrderByElement> orderbys = select.getOrderByElements();
        if (null == orderbys) return;
        for (int i = 0; i < orderbys.size(); i++) {
            OrderByElement orderby = orderbys.get(i);
            Expression ex = orderby.getExpression();
            if (ex instanceof Column) {
                String orderColumnName = ((Column) ex).getColumnName();
                Expression realCol = aliasMap.get(orderColumnName);
                if (null != realCol)
                    orderby.setExpression(realCol);
            }
        }
    }

    //如果Where語句中使用別名,則從字段中,找到真實字段(字段或公式)並替換
    private void UpdateWhereColumnName(Map<String, Expression> aliasMap, PlainSelect select, Expression exParent, Expression ex, String type) {
        if (ex instanceof Parenthesis) {
            UpdateWhereColumnName(aliasMap, select, ex, ((Parenthesis) ex).getExpression(), "P");
        }
        if (ex instanceof BinaryExpression) {
            UpdateWhereColumnName(aliasMap, select, ex, ((BinaryExpression) ex).getLeftExpression(), "L");
            UpdateWhereColumnName(aliasMap, select, ex, ((BinaryExpression) ex).getRightExpression(), "R");
        }
        if (ex instanceof Column) {
            String orderColumnName = ((Column) ex).getColumnName();
            Expression realCol = aliasMap.get(orderColumnName);
            if (null != realCol) {
                if (type.equals("P"))
                    ((Parenthesis) exParent).setExpression(realCol);
                if (type.equals("L"))
                    ((BinaryExpression) exParent).setLeftExpression(realCol);
                if (type.equals("R"))
                    ((BinaryExpression) exParent).setRightExpression(realCol);
            }
        }
    }

    @Override
    public void processInsert(Insert insert) {
    }

    @Override
    public void processUpdate(Update update) {
    }

    @Override
    public void processDelete(Delete delete) {
    }

}
View Code

加載SQL解析器:

@Configuration
@MapperScan({"com.mpspringboot.mapper","com.baomidou.mybatisplus.core.mapper"})
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();

        // 加載解析器
        List<ISqlParser> sqlParserList = new ArrayList<>();
        sqlParserList.add(new AliasSqlParser());
        paginationInterceptor.setSqlParserList(sqlParserList);

        return paginationInterceptor;
    }
}

2、下載源碼,修改后安裝到本地Maven倉庫

使用Idea,用Gradle插件中的Install安裝即可,建議所有組件都安裝一次

3、更改jsqlparser源碼,應用到mybatis-plus中

mybatis-plus中使用jsqlparser1.2版本,在分頁插件使用sql解析器時,並且在使用SQLSERVER數據庫下,XMLmapper使用isnull函數時,會報錯

所以我們需要替換jsqlparser版本,而這個組件的源碼比較奇葩,需要以下步驟才能進行maven install

我下載的是1.3-SNAPSHOT版本,直接install后即可解決該問題,不需要修改源碼

3.1、先執行jjtree-javacc,讓jsqlparser從JSqlParserCC.jjt中,生成幾個類(這也是一種定制方式,可以自行修改這個文件,從而定制化)

3.2、運行maven-install即可

 


免責聲明!

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



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