mybaits(7)之mybatis-plus1

学习mybaits-plus,首先要明确 MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

也就是说已经mybatis的配置都没有发生改变,都可以使用,这是在这个基础上做了增强。

官网地址:https://mp.baomidou.com/guide/quick-start.html#

目录

1.搭建mybatis-plus环境

1.1原来mybatis环境

1.2 mybaits和mybatis-plus中的区别

1.3 来个完整mybatis-plus配置

2. mybatis-plus的使用

3. 注解

4. mybaits-plus配置

5 插件及扩展

5.1 分页插件

5.2 乐观锁插件

5.3 SQL执行分析插件,避免出现全表更新和删除

5.4  非法sql插件

5.5 公共字段填充(这个还是很实用的)

5.6 自定义sql的实现方法

第一:通过xml方式(就是mybatis的方式)

第二:通过Sql注入器方式



1.搭建mybatis-plus环境

1.1原来mybatis环境

<?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"
       xsi:schemaLocation="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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--spring扫描注解,排除@Controller注解-->
    <context:component-scan base-package="com.hww" use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
    </context:component-scan>

    <!--加载properties文件-->
    <context:property-placeholder location="classpath:config.properties"></context:property-placeholder>

    <!--数据源
        有三种方式:
        1. 从JNDI获得DataSource,JndiObjectFactoryBean
        2. 使用DriverManagerDataSource获得DataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用
        3. 从第三方连接池中获取DataSource
            Apache的DBCP
            C3P0
            ali的 com.alibaba.druid.pool.DruidDataSource
    -->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"></property>
        <property name="url" value="${db.url}"></property>
        <property name="username" value="${db.username}"></property>
        <property name="password" value="${db.password}"></property>
    </bean>

    <!--spring 事务管理器-->
    <bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--spring 事务管理器,开启注解-->
    <tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>

    <!--sqlSession
        这里面整合了原先的mybatis配置,可以将原来在mybatis中的配置,都配置在sqlSessionFactoryBean中
    -->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations" value="classpath:com/hww/dao/*.xml"></property>
    </bean>
    <!--mapper扫描器
    2.1如果Mapper.xml与Mapper.class在同一个包下且同名,spring 中MapperScannerConfigurer 扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
    2.2如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。(如idea中 maven 默认不打包java文件夹下的xml文件,未在pom.xml中配置resource的情况下)
    此时spring是通过识别mapper.xml中的
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hww.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
    </bean>

</beans>

 

1.2 mybaits和mybatis-plus中的区别

那么mybaits和mybatis-plus中的区别在哪里呢。这需要将mybaits中的SqlSessionFactoryBean替换成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="configuration" ref="mybatisConfiguration"></property>
    </bean>

1.3 来个完整mybatis-plus配置

<?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"
       xsi:schemaLocation="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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--spring扫描注解,排除@Controller注解-->
    <context:component-scan base-package="com.hww" use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
    </context:component-scan>

    <!--加载properties文件-->
    <context:property-placeholder location="classpath:config.properties"></context:property-placeholder>

    <!--数据源
        有三种方式:
        1. 从JNDI获得DataSource,JndiObjectFactoryBean
        2. 使用DriverManagerDataSource获得DataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用
        3. 从第三方连接池中获取DataSource
            Apache的DBCP
            C3P0
            ali的 com.alibaba.druid.pool.DruidDataSource
    -->
   <!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"></property>
        <property name="url" value="${db.url}"></property>
        <property name="username" value="${db.username}"></property>
        <property name="password" value="${db.password}"></property>
    </bean>

    <!--spring 事务管理器-->
    <bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--spring 事务管理器,开启注解-->
    <tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>

    <!--sqlSession
        这里面整合了原先的mybatis配置,可以将原来在mybatis中的配置,都配置在sqlSesion中
    -->
    <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>
    </bean>
    <!--mapper扫描器
    2.1如果Mapper.xml与Mapper.class在同一个包下且同名,spring 中MapperScannerConfigurer 扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
    2.2如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。(如idea中 maven 默认不打包java文件夹下的xml文件,未在pom.xml中配置resource的情况下)
    此时spring是通过识别mapper.xml中的
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hww.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
    </bean>
</beans>

2. mybatis-plus的使用

在mybaits中,我们需要定义mapper接口以及方法,同时还需要和mapper接口相同的sql映射文件。

而在mybatis-plus中,可以直接让mapper接口继承BaseMapper<T>类,此时就不需要编写sql映射文件,也不需要定义方法,就能够完成简单的数据库curd

  • mybatis-plus配置,见上1.3
  • mapper接口(实现BaseMapper)
package com.hww.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.hww.bean.Client;
import org.apache.ibatis.annotations.Select;

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

public interface ClientDao extends BaseMapper<Client> {

}

以下是 BaseMapper<T>类,定义了一些curd的操作方法。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> wrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}
  • bean(注意注解@TableName,@TableId)

这里需要增加一个知识点,此时此时我们看到,这里配置了两个注解@TableName,@TableId

这就是mybatis-plus中提供的一些注解。虽然利用mybatis-plus会简化一些curd,但是这些简化还需要配合在实体bean的注解。

具体的详解见注解章节。

package com.hww.bean;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

@TableName("t_ecif_client")
public class Client {

    @TableId
    private Integer clientId;
    private String clientName;
    private String clientNameEn;

    public Integer getClientId() {
        return clientId;
    }

    public void setClientId(Integer clientId) {
        this.clientId = clientId;
    }

    public String getClientName() {
        return clientName;
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public String getClientNameEn() {
        return clientNameEn;
    }

    public void setClientNameEn(String clientNameEn) {
        this.clientNameEn = clientNameEn;
    }

    @Override
    public String toString() {
        return "Client{" +
                "clientId='" + clientId + '\'' +
                ", clientName='" + clientName + '\'' +
                ", clientNameEn='" + clientNameEn + '\'' +
                '}';
    }
}

 

此时我们只需要一个mybatis-plus配置,mapper接口,实体bean,其他的和mybatis一样。就可以测试了

  • test
package com.test;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.hww.bean.Client;
import com.hww.dao.ClientDao;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class MyTest {

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

    @Test
    public void test(){
        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        List<Client> clients = clientDao.selectList(null);
        System.out.println(clients);
    }
    @Test
    public void test2(){
        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        Client client = new Client();
        client.setClientId(3);
        client.setClientName("ccc");
        client.setClientNameEn("");
        //if判断为空,则不更新。
//        int update = clientDao.updateById(client);

        //if判断为空,则不更新。
        UpdateWrapper<Client> wrapper = new UpdateWrapper<Client>();
        wrapper.eq("client_id",3);
        int update1 = clientDao.update(client, wrapper);

    }


}

总结:

这里比mybatis好的地方就是不需要配置sql映射文件也可以操作curd。当然可能不是只有这么一点好的操作。

 

 

3. 注解

  • @TableName 表名注解,用来加到实体bean的类上,表示这个实体类对应的表名称以及一些属性

  • @TableId 主键注解,用来加到实体bean的主键字段上,用来映射数据库表的主键

  • @TableField 字段注解(非主键),用来加到实体bean的其他属性上,用来映射数据库表中的表字段

  • @KeySequence 序列主键策略 oracle

目前常用的就是这些。

当实体类中没有配置@TableId 时,当使用一些xxxbyId的方法时是会报错的(eg:updateById,deleteById),因为mybatis找不到主键是哪个。

当实体类的名称和数据库表名称不相同时,如果没有用@TableName指定表名称是,因为mybatis找不到表

同理,如果数据库表字段和和实体bean中字段不相同时,如果不同 @TableField指定也是会报错的。(当然标准的下划线形式和标准驼峰不配置@TableField也没问题,但是这就需要必须严格按照标准,应为在mybatis-plus中,mapUnderscoreToCamelCase配置默认是true的。

 

4. mybaits-plus配置

  • sqlSessionFactory配置,配置数据源等常用配置,本部分配置包含了大部分用户的常用配置,其中一部分为 MyBatis 原生所支持的配置

  • configuration配置

mybatis和spring整合时发现,除了mybatis-config.xml中的settings配置不能够整合在spring.xml中之外,其他的所有的配置均可以移到spring.xml中配置。

那么mybatis-plus在这个基础上,也可以将settings中的配置在spring中进行配置,利用 configuration配置,可以实现将mybatis-config.xml的配置移植到spring中。

本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形式进行配置。

这里要注意configuration配置和configLocation(这里指的mybatis-config.xml)配置不能够同时配置。见图。这两个配置是不能够一起配置的。如果一起配置会报错。

  • globalConfig配置

除了configuration配置配置外,又提供了globalConfig配置。配置一些全局的配置等

  • dbConfig配置

MyBatis-Plus 全局策略中的 DB 策略配置

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    <property name="configuration" ref="configuration"/> <!--  非必须  -->
    <property name="globalConfig" ref="globalConfig"/> <!--  非必须  -->
    ......
</bean>

<bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
    ......
</bean>

<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
    <property name="dbConfig" ref="dbConfig"/> <!--  非必须  -->
    ......
</bean>

<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
    ......
</bean>

完整的配置

<?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"
       xsi:schemaLocation="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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--spring扫描注解,排除@Controller注解-->
    <context:component-scan base-package="com.hww" use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
    </context:component-scan>

    <!--加载properties文件-->
    <context:property-placeholder location="classpath:config.properties"></context:property-placeholder>

    <!--数据源
        有三种方式:
        1. 从JNDI获得DataSource,JndiObjectFactoryBean
        2. 使用DriverManagerDataSource获得DataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用
        3. 从第三方连接池中获取DataSource
            Apache的DBCP
            C3P0
            ali的 com.alibaba.druid.pool.DruidDataSource
    -->
   <!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"></property>
        <property name="url" value="${db.url}"></property>
        <property name="username" value="${db.username}"></property>
        <property name="password" value="${db.password}"></property>
    </bean>

    <!--spring 事务管理器-->
    <bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--spring 事务管理器,开启注解-->
    <tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>

    <!--sqlSession
        这里面整合了原先的mybatis配置,可以将原来在mybatis中的配置,都配置在sqlSesion中
    -->
    <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="configuration" ref="mybatisConfiguration"></property>
        <property name="globalConfig" ref="globalConfig"></property>
    </bean>
    <!--mapper扫描器
    2.1如果Mapper.xml与Mapper.class在同一个包下且同名,spring 中MapperScannerConfigurer 扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
    2.2如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。(如idea中 maven 默认不打包java文件夹下的xml文件,未在pom.xml中配置resource的情况下)
    此时spring是通过识别mapper.xml中的
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hww.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
    </bean>

    <!--Configuration配置-->
    <bean id="mybatisConfiguration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
        <!--日志-->
        <property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
        <!--标准驼峰转换-->
        <property name="mapUnderscoreToCamelCase" value="true"></property>
    </bean>

    <!--globalConfig全局配置-->
    <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
        <!--banner,mybatis启动时logo-->
        <property name="banner" value="false"></property>
        <property name="dbConfig" ref="dbConfig"></property>
    </bean>

    <!--db策略-->
    <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
        <!--sql数据库schema名称-->
        <property name="schema" value="test"></property>
    </bean>
</beans>

总结配置:

mybatis-plus有四个基本配置,分别是以下四种,注意层级依赖。

1. SqlSessionFactory

    1.1 configuration

    1.2 globalConfig

        1.2.1dbConfig

配置好配置后,将SqlSessionFactory注入到扫描器中,MapperScannerConfigurer。这样就实现了mybatis-plus,同时和spring做了整合。

 

5 插件及扩展

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

5.1 分页插件

PaginationInterceptor

    <bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configuration" ref="mybatisConfiguration"></property>
        <property name="globalConfig" ref="globalConfig"></property>
        <property name="plugins">
            <array>
                <bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"></bean>
            </array>
        </property>
    </bean>

使用时,一般需要指定每页条数+当前页

 @Test
    public void test3(){

        Page<Client> page = new Page();
        page.setSize(10);//每页数
        page.setCurrent(2);//第几页
        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        //这里返回的并不是记录数,而是一个page对象。可以从page对象中获取
        Page pageRs = clientDao.selectPage(page, null);
        //获取记录数
        List<Client> records = pageRs.getRecords();
        System.out.println("======================");
        System.out.println(records);
        System.out.println("当前页:"+pageRs.getCurrent());
        System.out.println("每页条数:"+pageRs.getSize());
        System.out.println("总记录数"+pageRs.getTotal());
        System.out.println("总页码:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());

    }

5.2 乐观锁插件

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

取出记录时,获取当前version 更新时,带上这个version 执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败

插件

 <property name="plugins">
            <array>
                <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"></bean>
            </array>
        </property>

测试

/**
     * 乐观锁插件
     */
    @Test
    public void test4(){

        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        Client client = new Client();
        client.setClientId(3);
        client.setClientName("李四");
        client.setClientNameEn("lisi1111223333");
        client.setVersion(2);
        int i = clientDao.updateById(client);
        System.out.println(i);
    }

对应的实体bean中需要加@Version注解 

public class Client {

    @TableId
    private Integer clientId;
    private String clientName;
    private String clientNameEn;
    @Version
    private Integer version;

原理说明:

当加上version注解后,每一次更新,version都会加1。

也就是每次更新都会自动加上   update client set id= Id,...version=version+1 where id=id and version=version;

也就是说每次更新version都会自动加1,并且增加条件version=version,判断取出的version和数据库中的version是否相同,如果相同,就更更新成功。如果不相同,则本次不更新。

比如说 A和B都更新同 一条数据,当A和B去查询时,此时version=0,B先更新,此时version=1。当A更新的时候发现此时的version=1,此时A的更新就失效。只能等下次A在去数据库中获取数据,拿到version,再去做更新。

但是如果update的时候,不带这个version,此时就能够更新。

 

ClientDao clientDao = (ClientDao)context.getBean("clientDao");
Client client = new Client();
client.setClientId(3);
client.setClientName("李四");
client.setClientNameEn("lisi1111223333");
int i = clientDao.updateById(client);
System.out.println(i);

5.3 SQL执行分析插件,避免出现全表更新和删除

配置

<property name="plugins">
            <array>
                <bean class="com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor">
                    <property name="sqlParserList">
                        <list>
                            <bean class="com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser"></bean>
                        </list>
                    </property>
                </bean>
            </array>
        </property>

测试

当操作全表的时候,不论是update还是delete都会报错

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation

    /**
     * sql分析插件
     */
    @Test
    public void test5(){

        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        Client client = new Client();
        client.setClientName("李四");
        //全表更更新
        int i = clientDao.update(client,null);
        System.out.println(i);
    }

5.4  非法sql插件

配置

<property name="plugins">
            <array>
                <bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"></bean>
                <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"></bean>
                <bean class="com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor">
                    <property name="sqlParserList">
                        <list>
                            <bean class="com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser"></bean>
                        </list>
                    </property>
                </bean>
                <bean class="com.baomidou.mybatisplus.extension.plugins.IllegalSQLInterceptor"></bean>
            </array>
        </property>

测试

     /**
     * 非法sql
     */
    @Test
    public void test6(){

        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        QueryWrapper<Client> clientQueryWrapper = new QueryWrapper<Client>();
        clientQueryWrapper.or();
        clientDao.selectList(clientQueryWrapper);
    }

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,必须要有where条件
### The error may exist in com/hww/dao/ClientDao.java (best guess)
### The error may involve com.hww.dao.ClientDao.selectList
### The error occurred while executing a query
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,必须要有where条件
 

5.5 公共字段填充(这个还是很实用的)

  • 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

  • 注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!

    metaobject:元对象,是mybatis提供的一个用于更加方便,更加优雅的访问对象的属性,给对象的属性设置值的一个对象,还会用于包装对象,支持Object,Map,Collection等对象进行包装。本质上metaobject是给对象的属性设置值,最终还是要通过Reflect获取到属性的对应方法的invoker,最终执行。

场景,例如一些技术字段,创建时间create_datetime和更新时间mod_datetime,在数据创建时或插入create_datetime和mod_datetime字段,在数据更新是只更更新mod_datetime字段

自定义MetaObjectHandler类型,实现插入时reate_datetime和mod_datetime都填充当前日期,更新是只更新mod_datetime字段

package com.hww.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;

import java.util.Date;


public class MyMetaObjectHandler implements MetaObjectHandler {

    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createDatetime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
        this.strictInsertFill(metaObject, "modDatetime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
    }

    public void updateFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "modDatetime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
    }
}

实体bean,@TableField 制动fill填充

    @TableField(value = "create_datetime",fill = FieldFill.INSERT)
    private Date createDatetime;
    @TableField(value = "mod_datetime",fill = FieldFill.INSERT_UPDATE)
    private Date modDatetime;

测试

    /**
     * 公共字段填充 插入
     */
    @Test
    public void test7(){

        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        Client client = new Client();
        client.setClientName("西门吹雪");
        client.setClientNameEn("xi");
        client.setClientId(19);
        int insert = clientDao.insert(client);
        System.out.println(insert);
    }

    /**
     * 公共字段填充 更新
     */
    @Test
    public void test8(){

        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        Client client = new Client();
        client.setClientName("西门吹雪");
        client.setClientNameEn("xi0000");
        client.setClientId(19);
        int updateById = clientDao.updateById(client);
        System.out.println(updateById);
    }

5.6 自定义sql的实现方法

 

自定义sql可以有两种方式。

第一:通过xml方式(就是mybatis的方式)

步骤:

1. 自定义一个sql 的xml文件。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hww.dao.ClientDao">
    <select id="selectAll" resultType="com.hww.bean.Client">
        select * from  t_ecif_client
    </select>
</mapper>

2. 自定义一个方法

package com.hww.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.hww.bean.Client;
import com.hww.mapper.MyMapper;
import org.apache.ibatis.annotations.Select;

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

public interface ClientDao extends MyMapper<Client> {

    public List<Client> selectAll();
}

3. 将xml文件配置到spring中

<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="mapperLocations">
            <array>
                <value>classpath*:sql/clientMapper.xml</value>
            </array>
        </property>
    </bean>

4. 测试 

    /**
     * 自动以sql,xml形式
     */
    @Test
    public void test10(){
        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        List<Client> clients = clientDao.selectAll();
        System.out.println(clients);
    }

分析,可以对每一个实体自定义方法,但是每个方法是针对一个具体的Mapper的。不能像BaseMapper中提供的方法,所有的Mapper都可以使用。

那么怎么实现这个方式呢。就是通过sql注入器的方式。

第二:通过Sql注入器方式

全局配置 sqlInjector 用于注入 ISqlInjector 接口的子类,实现自定义方法注入。也就是说我们可以将配置在xml中的文件使用注入的方式注入到全局中,就不需要再编写sql语句。

人话解释:就是自定一个sql,可以将这个sql注入到全局变量中,使用时可以直接使用。就和mybatis-plus中BaseMapper提供的方法一样。可以直接进行使用

其实mybatis-plus中BaseMapper提供的方法就是这中方式实现的。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> wrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}

实现步骤

1.  需要枚举类。BaseMapper里的方式就是通过枚举类的形式,当然不是非得用枚举。也可以用其他的方式。

需要事先定义好自定义的 方法名称,方法描述,需要的基本sql语句

package com.hww.injector;

public enum MySqlMethod {
    /**
     * 删除全部
     */
    DELETE_ALL("deleteAll", "根据 entity 条件删除记录", "<script>\nDELETE FROM %s %s\n</script>");


    private final String method;
    private final String desc;
    private final String sql;

    MySqlMethod(String method, String desc, String sql) {
        this.method = method;
        this.desc = desc;
        this.sql = sql;
    }

    public String getMethod() {
        return method;
    }

    public String getDesc() {
        return desc;
    }

    public String getSql() {
        return sql;
    }
}

2.  定义个类,继承AbstractMethod方法。这方法中,将sql语句完善

package com.hww.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class DeleteAll extends AbstractMethod {
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql;
        MySqlMethod mySqlMethod = MySqlMethod.DELETE_ALL;
        if (tableInfo.isLogicDelete()) {
            sql = String.format(mySqlMethod.getSql(), tableInfo.getTableName(),  tableInfo,
                    sqlWhereEntityWrapper(true,tableInfo));
        } else {
            mySqlMethod = MySqlMethod.DELETE_ALL;
            sql = String.format(mySqlMethod.getSql(), tableInfo.getTableName(),
                    sqlWhereEntityWrapper(true,tableInfo));
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return addUpdateMappedStatement(mapperClass, modelClass, mySqlMethod.getMethod(), sqlSource);
    }
}

3. 定义sql注入器,继承AbstractSqlInject,将

自定义自己的通用方法可以实现接口 ISqlInjector 也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。

package com.hww.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class MyInjector extends AbstractSqlInjector {
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        return Stream.of(new DeleteAll()).collect(Collectors.toList());
    }
}

4. 配置到全局配置中

     <!--globalConfig全局配置-->
    <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
        <property name="sqlInjector" ref="myinject"></property>
    </bean>
    <bean id="myinject" class="com.hww.injector.MyInjector"></bean>

5. 继承BaseMapper,添加自定义的方法
 

package com.hww.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;

public interface MyMapper<T> extends BaseMapper<T> {
    Integer deleteAll();
}

6. Mapper接口此时就继承MyMapper,就可以引用自定义的sql和BaseMapper中的sql了

package com.hww.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.hww.bean.Client;
import com.hww.mapper.MyMapper;
import org.apache.ibatis.annotations.Select;

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

public interface ClientDao extends MyMapper<Client> {

}

7. 进行测试

    /**
     * 自定义sql测试
     */
    @Test
    public void test9(){

        ClientDao clientDao = (ClientDao)context.getBean("clientDao");
        clientDao.deleteAll();
    }

分析,此时这个自定义sql就是全局的。每个mapper接口都可以使用了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis-PlusMybatis的增强工具,在开发中有很多使用场景,其中就包括动态表名插件,也就是说,在Mybatis-Plus中,动态表名插件可以实现一个SQL语句的多表关联查询。 动态表名插件是Mybatis-Plus的一种增强插件,在查询过程中,通过钩子的方式,动态生成SQL语句,以实现动态表名的查询。这种方法不仅可以实现多表关联查询,而且还能避免SQL注入攻击。 使用动态表名插件,需要在Mybatis-Plus的配置文件中添加相应的配置,具体操作步骤如下: 1.在pom.xml文件中引入Mybaits-Plus和相关的依赖。 ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.2</version> </dependency> ``` 2.在application.yml文件中添加Mybatis-Plus相关的配置信息。 ```yml mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.example.model configuration: map-underscore-to-camel-case: true sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 ``` 3.编写Mapper.xml文件,使用动态表名插件。 ```xml <select id="findUserByName" resultMap="user"> SELECT * FROM ${tableName} WHERE name = #{name} </select> ``` 以上就是使用动态表名插件实现多表关联查询的方法,需要注意的是,在使用动态表名插件时,需要特别小心SQL注入攻击。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值