ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

springboot-mybatisplus第一章节

2021-11-05 01:00:51  阅读:160  来源: 互联网

标签:章节 mybatisplus springboot private class version user com public


SpringBoot整合Mybatis-Plus篇

1、概述

因为mybatis-plus不是官方开发的,所以没有提供对应的starter。但是民间有大神,有着对应的提供,那么先去官网上找一下:

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.4</version>
        </dependency>

然后分析一下pom依赖:

可以看到这里引入了mybatis-spring的整合包,注解等等。以及对应的jdbc操作等等。书库连接池使用的是Hikari等

就代表着我们不需要配置任何的东西就已经可以来操作数据库了。

2、自动配置分析

那么去看一下对应的maven依赖中的autoconfig,找到spring.factories目录下的自动配置

# Auto Configure
org.springframework.boot.env.EnvironmentPostProcessor=\
  com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

那么直接看最后一个:

  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

看下源码:

@Configuration(proxyBeanMethods = false)
// 导入mybatis就有这两个类
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
// 如果只有一个数据库连接池的类
@ConditionalOnSingleCandidate(DataSource.class)
// 配置文件中和类属性进行绑定
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {

继续看看类中配置了什么:

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

这段代码就是配置SqlSessionFactory,然后将容器中的DataSource导入进来,然后下面在进行疯狂的set操作。

SqlSessionTemplate的配置,也就是SqlSession的配置

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
        return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

AutoConfiguredMapperScannerRegistrar的注册,也就是配置的mapper包的路径

@Configuration(proxyBeanMethods = false)
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        logger.debug(
            "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }
}

看一下配置文件:


/**
 * Configuration properties for MyBatis.
 *
 * @author Eddú Meléndez
 * @author Kazuki Shimizu
 */
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = Constants.MYBATIS_PLUS)
public class MybatisPlusProperties {

    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();

    /**
     * Location of MyBatis xml config file.  全局配置文件
     */
    private String configLocation;

    /**
     * Locations of MyBatis mapper files.
     *  mapper映射接口的文件的位置,这里默认的是/mapper路径下的所有的xml文件
     * @since 3.1.2 add default value
     */
    private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};

    /**
     * Packages to search type aliases. (Package delimiters are ",; \t\n")  别名操作
     */
    private String typeAliasesPackage;

    /**
     * The super class for filtering type alias.
     * If this not specifies, the MyBatis deal as type alias all classes that searched from typeAliasesPackage.
     */
    private Class<?> typeAliasesSuperType;

    /**
     * Packages to search for type handlers. (Package delimiters are ",; \t\n")
     */
    private String typeHandlersPackage;

    /**
     * Indicates whether perform presence check of the MyBatis xml config file.  是否需要来进行检查全局配置文件
     */
    private boolean checkConfigLocation = false;

    /**
     * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
     */
    private ExecutorType executorType;

    /**
     * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
     * <p>
     * 如果设置了这个,你会至少失去几乎所有 mp 提供的功能
     */
    private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;

    /**
     * Externalized properties for MyBatis configuration.
     */
    private Properties configurationProperties;

    /**
     * A Configuration object for customize default settings. If {@link #configLocation}
     * is specified, this property is not used.
     * TODO 使用 MybatisConfiguration    mybatis-plus的全局配置
     */
    @NestedConfigurationProperty
    private MybatisConfiguration configuration;

    /**
     * TODO 枚举包扫描
     */
    private String typeEnumsPackage;

配置文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp?serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: 123456



# mybatisplus几乎零配置

mybatis-plus:
  configuration:
    # 添加控制台日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、配置逻辑删除

首先需要在yaml中来进行指定:

mybatis-plus:
  global-config:
    db-config:
	  # 要被删除的实体类中的字段
      logic-delete-field: deleted
      # 定义数据库中逻辑删除的数据的值
      logic-delete-value: 1
      # 定义数据库中未删除的值
      logic-not-delete-value: 0

看一个测试:

    @Test
    public void testFindAll(){
        List<User2> users = user2Mapper.selectList(null);
        users.forEach(System.out::println);
    }

查看对应的sql:

SELECT id,name,age,email,manager_id,create_time,update_time,version,deleted FROM user2 WHERE deleted=0

可以发现,这里已经自动的给我们添加上了对应的逻辑删除的值,而且对应着配置文件中的,只是会去查询未被删除的值,而不会再次去进行查询没有删除的值。

查看上面的SQL语句,我们可以看到,在进行select的字段里面有一个字段deleted,但是这个字段通常来说,是不需要出现在select后面的字段中的,因为不需要。

所以如果有需要,那么在实体类上加上注解:

@TableField(select = false)
private Integer deleted;

那么再次执行之后,就看不到这个值了。

看一下对应的SQL语句:

SELECT id,name,age,email,manager_id,create_time,update_time,version FROM user2 WHERE deleted=0

可以看到这里已经没有了对应的deleted字段了。

在全局配置文件中,配置上了逻辑删除的字段的值。对于多个表来说,我们会将重复的字段设置成一样的。

但是像这种需要来进行单独配置的来说,那么就需要加上@TableField注解即可。

再来看一个测试:

    @Test
    public void testDeleteById(){
        int i = user2Mapper.deleteById(1);
        System.out.println(i);
    }

然后查看对应的SQL语句:

UPDATE user2 SET deleted=1 WHERE id=1 AND deleted=0

可以看到这里做的是update语句,而不是delete语句。因为这里将deleted修改成了我们需要去进行修改的值了。

总结

配置了逻辑删除之后带来的影响:

逻辑删除 select insert update delete
无影响 追加where条件,过滤掉已经删除的数据 追加where条件,过滤掉已经删除的字段 转换成update语句

注意,上述的影响,只针对mp自动注入的SQL生效。 如果是自己手动添加的自定义SQL,则不会生效。比如:

public interface User2Mapper extends BaseMapper<User2> {
    
     @Select("select * from user2")
 	 List<User2> selectRaw();
    
}

调用这个selectRaw,则mp的逻辑删除不会生效。

逻辑删除可在application.yml中进行全局配置,也可在实体类中用@TableLogic进行局部配置。

4、自动填充

表中常常会有“新增时间”,“修改时间”,“操作人” 等字段。比较原始的方式,是每次插入或更新时,手动进行设置。mp可以通过配置,对某些字段进行自动填充,食用示例如下

  1. 在实体类中的某些字段上,通过@TableField设置自动填充;
@Data
public class User2 {
 private Long id;
 private String name;
 private Integer age;
 private String email;
 private Long managerId;

 @TableField(fill = FieldFill.INSERT)
 private LocalDateTime createTime;
 
 @TableField(fill = FieldFill.UPDATE)
 private LocalDateTime updateTime;
 
 private Integer version;
 @TableField(select = false)
 private Integer deleted;
}

2、然后再添一个组件到容器中去

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 注意第二个参数要填写实体类中的字段名称,而不是表的列名称  
    @Override
    public void insertFill(MetaObject metaObject) {
        strictFillStrategy(metaObject,"createTime", LocalDateTime::now);
    }
    // 注意第二个参数要填写实体类中的字段名称,而不是表的列名称  
    @Override
    public void updateFill(MetaObject metaObject) {
        strictFillStrategy(metaObject,"updateTime", LocalDateTime::now);
    }
}

然后来进行测试:

    @Test
    public void testInsert() {
        User2 user = new User2();
        user.setId(8L);
        user.setName("王一蛋");
        user.setAge(29);
        user.setEmail("yd@baomidou.com");
        user.setManagerId(2L);
        user2Mapper.insert(user);
    }

可以看到数据库中插入时间进行了更新

然后测试一下修改的:

    @Test
    public void testUpdate() {
        User2 user = new User2();
        user.setId(8L);
        user.setName("王一蛋");
        user.setAge(30);
        user.setEmail("yd@baomidou.com");
        user.setManagerId(2L);
        user2Mapper.updateById(user);
    }

查看一下数据库,发现对应的事件也是修改成功的

注意,自动填充仅在该字段为空时会生效,若该字段不为空,则直接使用已有的值

5、乐观锁插件

当出现并发操作时,需要确保各个用户对数据的操作不产生冲突,此时需要一种并发控制手段。悲观锁的方法是,在对数据库的一条记录进行修改时,先直接加锁(数据库的锁机制),锁定这条数据,然后再进行操作;而乐观锁,正如其名,它先假设不存在冲突情况,而在实际进行数据操作时,再检查是否冲突。乐观锁的一种通常实现是版本号 ,在MySQL中也有名为MVCC的基于版本号的并发事务控制。

在读多写少的场景下,乐观锁比较适用,能够减少加锁操作导致的性能开销,提高系统吞吐量。

在写多读少的场景下,悲观锁比较使用,否则会因为乐观锁不断失败重试,反而导致性能下降。

乐观锁的实现如下:

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果oldVersion与数据库中的version不一致,就更新失败

这种思想和CAS(Compare And Swap)非常相似。

乐观锁的实现步骤如下:

配置乐观锁插件

@Configuration
public class MybatisPlusConfig {
    /**
     * 3.4.0以后的mp版本,推荐用如下的配置方式
     * @return
     */
/*    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }*/

    /**
     * 旧版mp可以采用如下方式。注意新旧版本中,新版的类,名称带有Inner,旧版的不带,不要配错了
     * @return
     */
    @Bean
    public OptimisticLockerInterceptor opLocker() {
        return new OptimisticLockerInterceptor();
    }
}

写一个单元测试:

    @Test
    public void testOpLocker() {
        int version = 1; // 假设这个version是先前查询时获得的
        User2 user = new User2();
        user.setId(8L);
        user.setEmail("version@baomidou.com");
        user.setVersion(version);
        int i = user2Mapper.updateById(user);
    }

看一下对应的SQL语句:

UPDATE user2 SET email='version@baomidou.com', update_time='2021-11-05T00:34:08.190', version=2 WHERE id=8 AND version=1 AND deleted=0

可以看到,在插入的时候首先来进行查询对应的version的值,如果是符合的,那么就开始进行操作。

当UPDATE返回了1,表示影响行数为1,则更新成功。反之,由于WHERE后面的version与数据库中的不一致,匹配不到任何记录,则影响行数为0,表示更新失败。更新成功后,新的version会被封装回实体对象中。

注意,乐观锁插件仅支持updateById(id)update(entity, wrapper)方法

注意:如果使用wrapper,则wrapper不能复用! 示例如下

    @Test
    public void testOpLockerTwo() {
        User2 user = new User2();
        user.setId(8L);
        user.setVersion(1);
        user.setAge(2);
        // 第一次使用
        LambdaQueryWrapper<User2> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User2::getName, "王一蛋");
        user2Mapper.update(user, wrapper);
        // 第二次复用
        user.setAge(3);
        user2Mapper.update(user, wrapper);
    }

查看下对应的SQL如下所示:

 Consume Time:20 ms 2021-11-05 00:38:48
 Execute SQL:UPDATE user2 SET age=2, update_time='2021-11-05T00:38:47.079', version=2 WHERE deleted=0 AND (name = '王一蛋' AND version = 1)

 Consume Time:19 ms 2021-11-05 00:38:48
 Execute SQL:UPDATE user2 SET age=3, update_time='2021-11-05T00:38:47.079', version=3 WHERE deleted=0 AND (name = '王一蛋' AND version = 1 AND version = 2)

可以看到在第二次复用wrapper时,拼接出的SQL中,后面WHERE语句中出现了2次version,是有问题的。

6、性能分析插件

该插件会输出SQL语句的执行时间,以便做SQL语句的性能分析和调优。

注:3.2.0版本之后,mp自带的性能分析插件被官方移除了,而推荐使用第三方性能分析插件

1、引入maven依赖

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.1</version>
</dependency>

2、修改application.yaml

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver #换成p6spy的驱动
    url: jdbc:p6spy:mysql://localhost:3306/mp?serverTimezone=Asia/Shanghai #url修改
    username: root
    password: root

3、在src/main/resources资源目录下添加spy.properties

#spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 真实JDBC driver , 多个以逗号分割,默认为空。由于上面设置了modulelist, 这里可以不用设置driverlist
#driverlist=com.mysql.cj.jdbc.Driver
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
#若要日志输出到文件, 把上面的appnder注释掉, 或者采用下面的appender, 再添加logfile配置
#不配置appender时, 默认是往文件进行输出的
#appender=com.p6spy.engine.spy.appender.FileLogger
#logfile=log.log
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
# 执行时间设置, 只有超过这个执行时间的才进行记录, 默认值0, 单位毫秒
executionThreshold=10

随便执行一条SQL语句,可以看到控制台会打印出来对应的日志:

 Consume Time:19 ms 2021-11-05 00:38:48
 Execute SQL:UPDATE user2 SET age=3, update_time='2021-11-05T00:38:47.079', version=3 WHERE deleted=0 AND (name = '王一蛋' AND version = 1 AND version = 2)

8、代码生成器

详细的去官网或者博客找一篇看看就行了,这里不再来进行赘述。

7、快速开始操作

mybatis-plus提供了两个接口,BaseMapper,也就是mapper接口;另外一个IService,则是service接口

二者最大的区别在于service中支持了大量的批量操作。

mp最强大的一点在于提供了wapper,可以非常的构造出来where条件。从这里可以看到这里是拼接where条件的。

那么只要知道了这个宗旨,那么后面的比较好理解了。

AbstractWrapper接口本身已经提供了很多的方法用于构建where条件。

而QueryWrapper接口主要用作查询;

UpdateWrapper接口用来进行set方法,用于进行set值的;

这块官方文档说明的还算详细。

在条件构造的过程中,有存在着一个boolean类型的condition参数,用来决定最终的条件是否需要添加到where条件中去。

lambda条件构造器,支持lambda表达式。

接下来就没有什么好说的了,具体的就可以来参考文档操作了。

参考文档:https://mp.weixin.qq.com/s/SBkYZrBbGEgBe09erNr7tg

标签:章节,mybatisplus,springboot,private,class,version,user,com,public
来源: https://www.cnblogs.com/likeguang/p/15511385.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有