3、基本CRUD操做

上一節咱們完成了mybatis-plus的集成,也已經在mp01中添加相關的實體類的對應的數據庫表,這一節咱們來實現基於mybatis-plus的CRUD操做。java

首先按照上一節的操做,新建一個mp02 的 Module,能夠將mp01中的內容所有複製過來,同時進行一下修改:mysql

修改 Employee實體類:git

/**
 * mybatis-plus默認會使用實體類的小寫類名做爲表名
 */
@Data
@ToString
//@TableName(value = "tbl_employee")  // ==> 全局的表前綴策略配置
public class Employee {

    /**
     * @TableId:
     *      value: 指定表中的主鍵的列名,若是實體屬性名與列名一致,能夠省略不指定
     *      type:指定主鍵策略
     * 設置主鍵自增
     */
//    @TableId(value = "id", type = IdType.AUTO) // ==> 全局表主鍵生成策略

    private Integer id;
    @TableField(value = "last_name")
    private String lastName;
    private String email;
    private Integer gender;
    private Integer age;

    // 當前字段是否在數據庫中存在,若是不存在則忽略該字段插入到數據庫中
    @TableField(exist = false)
    private Double salary;
}
複製代碼

修改applicationContext.xml文件,添加下面的內容:github

<property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!-- 別名處理 -->
        <property name="typeAliasesPackage" value="com.mp.beans"></property>
        <!-- 注入配置-->
        <!--<property name="configuration" ref="configuration"></property>-->
        <!-- 注入全局配置策略-->
        <property name="globalConfig" ref="globalConfiguration"></property>

        <property name="plugins">
            <!-- 分頁查詢插件 -->
            <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
                <property name="dialectType" value="mysql" />
            </bean>
        </property>

    </bean>

    <!--這個等於Mybatis的全局配置文件,若是在MybatisSqlSessionFactoryBean裏面已經配置了configLocation屬性(外部加載Mybatis全局配置文件),就不能再配置configuration屬性-->
    <bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
        <!--開啓駝峯命名-->
        <property name="mapUnderscoreToCamelCase" value="true"/>
        <!--日誌打印SQL語句-->
        <property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
    </bean>

    <!-- 定義mybatis-plus全局策略配置-->
    <bean id="globalConfiguration" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
        <!-- 全局主鍵策略-->
        <property name="dbConfig" ref="dbConfig"></property>
    </bean>
    <!-- 這裏-->
    <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
        <!-- 全局表主鍵生成策略 -->
        <property name="idType" value="AUTO"></property>
        <!-- 全局的表前綴策略配置 -->
        <property name="tablePrefix" value="tbl_"></property>
    </bean>
複製代碼

完整的xml文件以下:spring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring
        http://mybatis.org/schema/mybatis-spring-1.2.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <!-- 數據源 -->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="dataSource"
          class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 事務管理器 -->
    <bean id="dataSourceTransactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 基於註解的事務管理 -->
    <tx:annotation-driven
            transaction-manager="dataSourceTransactionManager"/>

    <!-- 配置 SqlSessionFactoryBean
        mybatis提供的:org.mybatis.spring.SqlSessionFactoryBean
        mybatis-plus提供的:3.2.0 com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean
                2.3 com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
     -->
    <bean id="sqlSessionFactoryBean"
          class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!-- 別名處理 -->
        <property name="typeAliasesPackage" value="com.mp.beans"></property>
        <!-- 注入配置-->
        <!--<property name="configuration" ref="configuration"></property>-->
        <!-- 注入全局配置策略-->
        <property name="globalConfig" ref="globalConfiguration"></property>

        <property name="plugins">
            <!-- 分頁查詢插件 -->
            <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
                <property name="dialectType" value="mysql" />
            </bean>
        </property>

    </bean>

    <!--這個等於Mybatis的全局配置文件,若是在MybatisSqlSessionFactoryBean裏面已經配置了configLocation屬性(外部加載Mybatis全局配置文件),就不能再配置configuration屬性-->
    <bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
        <!--開啓駝峯命名-->
        <property name="mapUnderscoreToCamelCase" value="true"/>
        <!--日誌打印SQL語句-->
        <property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
    </bean>

    <!-- 定義mybatis-plus全局策略配置-->
    <bean id="globalConfiguration" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
        <!-- 全局主鍵策略-->
        <property name="dbConfig" ref="dbConfig"></property>
    </bean>
    <!-- 這裏-->
    <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
        <!-- 全局表主鍵生成策略 -->
        <property name="idType" value="AUTO"></property>
        <!-- 全局的表前綴策略配置 -->
        <property name="tablePrefix" value="tbl_"></property>
    </bean>

    <!--
    配置 mybatis 掃描 mapper 接口的路徑
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage"
                  value="com.mp.mapper"></property>
    </bean>
</beans>
複製代碼

在 mp02\src\main\java\com\mp\mapper\EmployeeMapper.java 路徑下新建 EmployeeMapper.java mapper文件,EmployeeMapper 類須要繼承 BaseMapper<T>,代碼以下:sql

/**
 * @description: mapper接口
 * 實現方式:
 * 基於 Mybatis
 *      須要編寫 EmployeeMapper 接口,並手動編寫 CRUD 方法
 *      提供 EmployeeMapper.xml 映射文件,並手動編寫每一個方法對應的 SQL 語句.
 *
 * 基於 MP
 *      只須要建立 EmployeeMapper 接口, 並繼承 BaseMapper 接口.這就是使用 MP
 *      須要完成的全部操做,甚至不須要建立 SQL 映射文件。
 *      BaseMapper<T>:泛型指定的就是當前mapper接口所操做的實體類型
 */
public interface EmployeeMapper extends BaseMapper<Employee> {

    // 使用 mybatis 插入數據時獲取主鍵值
    //   Integer  insertEmployee(Employee employee );
    //   <insert useGeneratedKeys="true" keyProperty="id" > SQL...</insert>
}
複製代碼

修改mp02的pom.xml文件:數據庫

<?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">
    <parent>
        <artifactId>mybatis-plus-in-action</artifactId>
        <groupId>com.demo.mybatis-plus</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mp02</artifactId>

    <dependencies>
        <!-- mp 依賴
            mybatis-plus 會自動維護mybatis 以及 mybatis-spring相關的依賴
            Mybatis 及 Mybatis-Spring 依賴請勿加入項目配置,以避免引發版本衝突!!!Mybatis-Plus 會自動幫你維護!
         -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>${mybatis.plus.version}</version>
        </dependency>
        <!--junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <!-- log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
複製代碼

上面的準備工做作完了,下面咱們能夠來進行CRUD操做了:apache

一、insert操做

/**
* 通用 插入操做
*/
@Test
public void testCommonInsert() {
    // 初始化employee 對象
    Employee employee = new Employee();
    employee.setLastName("MP");
    employee.setEmail("mp@github.com");
    employee.setGender(1);
    employee.setAge(22);

    employee.setSalary(2000.0);

    // 插入到數據庫 insert方法在插入時, 會根據實體類的每一個屬性進行非空判斷,只有非空的屬性對應的字段纔會出現到SQL語句中
    int ret = employeeMapper.insert(employee);
    System.out.println("result:" + ret);

    // 獲取當前數據在數據庫中的主鍵值
    Integer key = employee.getId();
    System.out.println("key:" + key);
}
複製代碼

二、update操做

/**
* 通用更新操做
*/
@Test
public void testCommonUpdate() {
    // 初始化employee 對象
    Employee employee = new Employee();
    employee.setId(19);
    employee.setLastName("MP");
    employee.setEmail("mybatis-plus@github.com");
    employee.setGender(0);
    // employee.setAge(33); // 沒有傳的字段不會被更新

    // 單個更新
    Integer result = employeeMapper.updateById(employee);
    System.out.println("result:" + result);

    // 批量更新, 若是 Wrapper 爲空,則所有更新
    result = employeeMapper.update(employee, null);
    System.out.println("result:" + result);
}
複製代碼

三、select操做

/**
* 通用查詢操做
*/
@Test
public void testCommonSelect() {
    // 一、經過id查詢
    Employee employee = employeeMapper.selectById(14);
    System.out.println(employee);

    // 二、經過多個列查詢 id+lastName
    Employee employee1 = new Employee();
    employee1.setId(13);
    employee1.setLastName("White");
    Wrapper<Employee> employeeWrapper = new QueryWrapper<Employee>(employee1);
    Employee selectOne = employeeMapper.selectOne(employeeWrapper);
    System.out.println(selectOne);

    // 三、經過多個id進行查詢
    List<Integer> idList = new ArrayList<Integer>();
    idList.add(11);
    idList.add(12);
    idList.add(13);
    idList.add(14);
    List<Employee> employeeList = employeeMapper.selectBatchIds(idList);
    System.out.println("employeeList:" + employeeList);

    // 四、經過 selectMaps 查詢
    Map<String, Object> columnMap = new HashMap<String, Object>();
    columnMap.put("last_name", "White");
    columnMap.put("gender", 0);
    List<Employee> selectByMap = employeeMapper.selectByMap(columnMap);
    System.out.println("selectByMap:" + selectByMap);

    // 五、分頁查詢 selectPage 使用分頁查詢須要在 applicationContext.xml 中添加 分頁查詢插件 配置
    IPage<Employee> employeeIPage = employeeMapper.selectPage(new Page<>(2, 1), null);
    System.out.println("employeeIPage:" + employeeIPage.getRecords());
}
複製代碼

注意:緩存

一、columnMap中使用的是數據庫中的字段名,而不是實體類的字段名mybatis

二、分頁查詢操做須要在 applicationContext.xml 中添加分頁插件,完整代碼能夠看前面的完整配置:

<property name="plugins">
    <!-- 分頁查詢插件 -->
    <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
        <property name="dialectType" value="mysql" />
    </bean>
</property>
複製代碼

四、delet操做

/**
* 通用刪除操做
*/
@Test
public void testCommonDelete() {
    // 一、根據id刪除
    Integer result = employeeMapper.deleteById(15);
    System.out.println("result:" + result);

    // 二、根據 deleteByMap 條件刪除
    Map<String, Object> columnMap  = new HashMap<>();
    columnMap.put("last_name","MP");
    columnMap.put("email","mybatis-plus@github.com");
    Integer ret = employeeMapper.deleteByMap(columnMap);
    System.out.println("ret:" + ret);

    // 三、批量刪除
    List<Integer> idList = new ArrayList<Integer>();
    idList.add(18);
    idList.add(19);
    Integer deleteResult = employeeMapper.deleteBatchIds(idList);
    System.out.println("deleteResult:" + deleteResult);
}
複製代碼

注意:columnMap中使用的是數據庫中的字段名,而不是實體類的字段名

五、完整測試代碼

public class TestMp {
    private ApplicationContext ioc = new
            ClassPathXmlApplicationContext("applicationContext.xml");

    private EmployeeMapper employeeMapper = ioc.getBean("employeeMapper", EmployeeMapper.class);

    /**
     * 通用刪除操做
     */
    @Test
    public void testCommonDelete() {
        // 一、根據id刪除
        Integer result = employeeMapper.deleteById(15);
        System.out.println("result:" + result);

        // 二、根據 deleteByMap 條件刪除
        Map<String, Object> columnMap  = new HashMap<>();
        columnMap.put("last_name","MP");
        columnMap.put("email","mybatis-plus@github.com");
        Integer ret = employeeMapper.deleteByMap(columnMap);
        System.out.println("ret:" + ret);

        // 三、批量刪除
        List<Integer> idList = new ArrayList<Integer>();
        idList.add(18);
        idList.add(19);
        Integer deleteResult = employeeMapper.deleteBatchIds(idList);
        System.out.println("deleteResult:" + deleteResult);
    }

    /**
     * 通用查詢操做
     */
    @Test
    public void testCommonSelect() {
        // 一、經過id查詢
        Employee employee = employeeMapper.selectById(14);
        System.out.println(employee);

        // 二、經過多個列查詢 id+lastName
        Employee employee1 = new Employee();
        employee1.setId(13);
        employee1.setLastName("White");
        Wrapper<Employee> employeeWrapper = new QueryWrapper<Employee>(employee1);
        Employee selectOne = employeeMapper.selectOne(employeeWrapper);
        System.out.println(selectOne);

        // 三、經過多個id進行查詢
        List<Integer> idList = new ArrayList<Integer>();
        idList.add(11);
        idList.add(12);
        idList.add(13);
        idList.add(14);
        List<Employee> employeeList = employeeMapper.selectBatchIds(idList);
        System.out.println("employeeList:" + employeeList);

        // 四、經過 selectMaps 查詢
        Map<String, Object> columnMap = new HashMap<String, Object>();
        columnMap.put("last_name", "White");
        columnMap.put("gender", 0);
        List<Employee> selectByMap = employeeMapper.selectByMap(columnMap);
        System.out.println("selectByMap:" + selectByMap);

        // 五、分頁查詢 selectPage 使用分頁查詢須要在 applicationContext.xml 中添加 分頁查詢插件 配置
        IPage<Employee> employeeIPage = employeeMapper.selectPage(new Page<>(2, 1), null);
        System.out.println("employeeIPage:" + employeeIPage.getRecords());
    }

    /**
     * 通用更新操做
     */
    @Test
    public void testCommonUpdate() {
        // 初始化employee 對象
        Employee employee = new Employee();
        employee.setId(19);
        employee.setLastName("MP");
        employee.setEmail("mybatis-plus@github.com");
        employee.setGender(0);
//        employee.setAge(33);

        // 單個更新
        Integer result = employeeMapper.updateById(employee);
        System.out.println("result:" + result);

        // 批量更新, 若是 Wrapper 爲空,則所有更新
        result = employeeMapper.update(employee, null);
        System.out.println("result:" + result);
    }

    /**
     * 通用 插入操做
     */
    @Test
    public void testCommonInsert() {
        // 初始化employee 對象
        Employee employee = new Employee();
        employee.setLastName("MP");
        employee.setEmail("mp@github.com");
        employee.setGender(1);
        employee.setAge(22);

        employee.setSalary(2000.0);

        // 插入到數據庫 insert方法在插入時, 會根據實體類的每一個屬性進行非空判斷,只有非空的屬性對應的字段纔會出現到SQL語句中
        int ret = employeeMapper.insert(employee);
        System.out.println("result:" + ret);

        // 獲取當前數據在數據庫中的主鍵值
        Integer key = employee.getId();
        System.out.println("key:" + key);
    }

    @Test
    public void testEnvironment() throws Exception {
        DataSource ds = ioc.getBean("dataSource", DataSource.class);
        Connection conn = ds.getConnection();
        System.out.println(conn);
    }
}
複製代碼

完成上面的操做後,mp02的代碼結構以下所示:

mp02-01.png

六、Mybatis-Plus啓動注入SQL原理分析

6.一、問題:

在咱們使用 BaseMapper 提供的方法來進行CRUD操做的時候,有沒有想過爲何咱們能直接使用這些方法,而這些方法又是何時加載到容器中呢?

xxxMapper 繼承了 BaseMapper, BaseMapper 中提供了通用的 CRUD 方法,這些方法來源於 BaseMapper, 有方法就必須有 SQL, 由於 MyBatis 最終仍是須要經過SQL 語句操做數據。覺得 Mybatis-Plus 是在MyBatis 的基礎上作了加強,因此咱們有必要了解一些前置知識:

MyBatis 源碼中比較重要的一些對象, MyBatis 框架的執行流程 Configuration:MyBatis 的全局配置對象 MappedStatement:MappedStatement保存全部的SQL方法和執行語句 ……..

6.二、經過現象看到本質:

經過debug日誌咱們能夠看到,在咱們執行 employeeMapper.deleteById(id)方法以前,Mybatis-Plus已經幫咱們把 BaseMapper 中內置的方法就加載到了MappedStatement中。

x01.png

咱們在 employeeMapper.deleteById(id)方法中打下斷點,能夠看到

A. employeeMapper 的本質是com.baomidou.mybatisplus.core.override.MybatisMapperProxy,若是是mybatis 則看到的是 org.apache.ibatis.binding.MapperProxy

B. MapperProxy 中兩個重要對象 sqlSession –>SqlSessionFactory

C. SqlSessionFacotry 中又有兩個重要的對象 Configuration→ MappedStatements

x03.png

每個 mappedStatement 都表示 Mapper 接口中的一個方法與 Mapper 映射文件中的一個 SQL。

x04.png

Mybatis-Plus 在啓動就會挨個分析 xxxMapper 中的方法,而且將對應的 SQL 語句處理好,保存到 configuration 對象中的 mappedStatements 中. D. 本質: Configuration: MyBatis 或者 MP 全局配置對象

MappedStatement:一個 MappedStatement 對象對應 Mapper 配置文件中的一個select/update/insert/delete 節點,主要描述的是一條 SQL 語句 SqlMethod : 枚舉對象 , MP 支持的 SQL 方法 TableInfo: 數據庫表反射信息 ,能夠獲取到數據庫表相關的信息 SqlSource: SQL 語句處理對象 MapperBuilderAssistant: 用於緩存、 SQL 參數、查詢方劑結果集處理等,經過 MapperBuilderAssistant 將每個 mappedStatement添加到 configuration 中的 mappedstatements 中 。

在下圖所示的位置處打斷點,能夠觀測到MP加載內置方法的整個過程,咱們在後邊自定義全局操做添加自定義方法的時候,還會碰到AbstractMethod對象和AbstractSqlInjector對象,

x05-MapperFactoryBean.png

x06-MybatisMapperRegistry.png

x07-MybatisMapperAnnotationBuilder.png

x08-AbstractSqlInjector.png

x09-AbstractMethod.png

x10-Insert.png

x11-AbstractMethod-addMappedStatement.png

x12-MapperBuilderAssistant-addMappedStatement.png

x13-MybatisConfiguration-addMappedStatement.png

Mybatis-Plus啓動注入SQL的執行流程入下入所示(中間省略若干非關鍵步驟):

xx-01.png

xx-02.png

以上是基本的 CRUD 操做, 你所見,咱們僅僅須要繼承一個 BaseMapper 便可實現大部分單表 CRUD 操做。 BaseMapper 提供了多達 17 個方法給你們使用, 能夠極其方便的實現單1、批量、分頁等操做。 極大的減小開發負擔。

綜上,基於 mybatis-plus 的CRUD演示就完成了,現有一個需求,咱們須要分頁查詢 tbl_employee 表中,年齡在 18~50 之間性別爲男且姓名爲 xx 的全部用戶,這時候咱們該如何實現上述需求呢?

MyBatis : 須要在 SQL 映射文件中編寫帶條件查詢的 SQL,並基於 PageHelper 插件完成分頁. 實現以上一個簡單的需求,每每須要咱們作不少重複單調的工做。 普通的 Mapper可以解決這類痛點嗎? MP: 依舊不用編寫 SQL 語句, MP 提供了功能強大的條件構造器 AbstractWrapper。下面咱們就能夠進入到下一節AbstractWrapper條件構造器了。

源代碼

相關示例完整代碼:mybatis-plus-in-action

相關文章
相關標籤/搜索