美文网首页
三、基本CRUD操作

三、基本CRUD操作

作者: runewbie | 来源:发表于2019-10-26 16:39 被阅读0次

    上一节我们完成了mybatis-plus的集成,也已经在mp01中添加相关的实体类的对应的数据库表,这一节我们来实现基于mybatis-plus的CRUD操作。

    首先按照上一节的操作,新建一个mp02Module,可以将mp01中的内容全部复制过来,同时进行一下修改:

    修改 Employee实体类:

    /**
     * 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文件,添加下面的内容:

            <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文件如下:

    <?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>,代码如下:

     /**
     * @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操作了:

    1、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);
    }
    

    2、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);
    }
    

    3、select操作

    /**
    * 通用查询操作
    */
    @Test
    public void testCommonSelect() {
        // 1、通过id查询
        Employee employee = employeeMapper.selectById(14);
        System.out.println(employee);
    
        // 2、通过多个列查询 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);
    
        // 3、通过多个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);
    
        // 4、通过 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);
    
        // 5、分页查询 selectPage 使用分页查询需要在 applicationContext.xml 中添加 分页查询插件 配置
        IPage<Employee> employeeIPage = employeeMapper.selectPage(new Page<>(2, 1), null);
        System.out.println("employeeIPage:" + employeeIPage.getRecords());
    }
    

    注意:

    1、columnMap中使用的是数据库中的字段名,而不是实体类的字段名

    2、分页查询操作需要在 applicationContext.xml 中添加分页插件,完整代码可以看前面的完整配置:

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

    4、delet操作

    /**
    * 通用删除操作
    */
    @Test
    public void testCommonDelete() {
        // 1、根据id删除
        Integer result = employeeMapper.deleteById(15);
        System.out.println("result:" + result);
    
        // 2、根据 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);
    
        // 3、批量删除
        List<Integer> idList = new ArrayList<Integer>();
        idList.add(18);
        idList.add(19);
        Integer deleteResult = employeeMapper.deleteBatchIds(idList);
        System.out.println("deleteResult:" + deleteResult);
    }
    

    注意:columnMap中使用的是数据库中的字段名,而不是实体类的字段名

    5、完整测试代码

    public class TestMp {
        private ApplicationContext ioc = new
                ClassPathXmlApplicationContext("applicationContext.xml");
    
        private EmployeeMapper employeeMapper = ioc.getBean("employeeMapper", EmployeeMapper.class);
    
        /**
         * 通用删除操作
         */
        @Test
        public void testCommonDelete() {
            // 1、根据id删除
            Integer result = employeeMapper.deleteById(15);
            System.out.println("result:" + result);
    
            // 2、根据 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);
    
            // 3、批量删除
            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() {
            // 1、通过id查询
            Employee employee = employeeMapper.selectById(14);
            System.out.println(employee);
    
            // 2、通过多个列查询 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);
    
            // 3、通过多个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);
    
            // 4、通过 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);
    
            // 5、分页查询 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

    6、Mybatis-Plus启动注入SQL原理分析

    6.1、问题:

    在我们使用 BaseMapper 提供的方法来进行CRUD操作的时候,有没有想过为什么我们能直接使用这些方法,而这些方法又是什么时候加载到容器中呢?

    xxxMapper 继承了 BaseMapper, BaseMapper 中提供了通用的 CRUD 方法,这些方法来源于 BaseMapper, 有方法就必须有 SQL, 因为 MyBatis 最终还是需要通过SQL 语句操作数据。以为 Mybatis-Plus 是在MyBatis 的基础上做了增强,所以我们有必要了解一些前置知识:

    MyBatis 源码中比较重要的一些对象, MyBatis 框架的执行流程 Configuration:MyBatis 的全局配置对象 MappedStatement:MappedStatement保存所有的SQL方法和执行语句 ……..

    6.2、通过现象看到本质:

    通过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 中又有两个重要的对象 ConfigurationMappedStatements

    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 个方法给大家使用, 可以极其方便的实现单一、批量、分页等操作。 极大的减少开发负担。

    综上,基于 mybatis-plus 的CRUD演示就完成了,现有一个需求,我们需要分页查询 tbl_employee 表中,年龄在 18~50 之间性别为男且姓名为 xx 的所有用户,这时候我们该如何实现上述需求呢?

    MyBatis : 需要在 SQL 映射文件中编写带条件查询的 SQL,并基于 PageHelper 插件完成分页. 实现以上一个简单的需求,往往需要我们做很多重复单调的工作。 普通的 Mapper能够解决这类痛点吗? MP: 依旧不用编写 SQL 语句, MP 提供了功能强大的条件构造器 AbstractWrapper。下面我们就可以进入到下一节AbstractWrapper条件构造器了。

    源代码

    相关示例完整代码:mybatis-plus-in-action

    相关文章

      网友评论

          本文标题:三、基本CRUD操作

          本文链接:https://www.haomeiwen.com/subject/okcevctx.html