mybatis-plus(入门实例)

pom.xml

        <!-- 数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- mybatis-plus-->
        <!-- mybatis-plus 是自己开发的并非官方的, 这是旧版,最好使用新版-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

注意事项:

    如果是用mybatis-plus 尽量不要同时导入mybatis的jar,因为可能会存在版本的差异,会出现差异!

数据库配置(与mybatis的配置区别不大):

# mysql 5 驱动不同com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=zhurunze783
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8

# mysql 8 驱动不同,需要增加时区的配置 serverTimezone=GMT%2B8
传统的方式(使用mybatis的方式):

pojo-dao(连接mybatis,配置mapper.xml文件)-server-controller

使用mybatis-plus之后
  • pojo
  • mapper接口
  • 使用
例如:

pojo:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Long id;
    private String name;
    private Integer age;
    private String email;

}
mapper接口:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mystudy.pojo.User;
import org.springframework.stereotype.Repository;

// 在对应的mapper上面继承基本的接口 BaseMapper
@Repository //代表持久层
public interface UserMapper extends BaseMapper<User> {
    // 所有的CRUD操作都已经编写完成

}
主启动类(必须扫描到mapper接口的文件夹)
//扫描我们的mapper文件夹
@MapperScan("com.mystudy.mapper")
@SpringBootApplication
public class MybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }

}
测试:
@SpringBootTest
class MybatisPlusApplicationTests {

    //继承了BaseMapper,所有的方法都来自自己的父类,我们也可以编写自己的扩展方法
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        //参数是一个Wrapper,条件构造器,这里我们先不使用,设置为null

        //查询全部用户
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

}

配置日志

# 配置日志(配置简单的控制台输出)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

CRUD扩展

1. insert(插入操作)

    @Test
    //测试插入
    public void testInsert(){
        User user = new User();
        user.setName("哈哈哈哈");
        user.setAge(3);
        user.setEmail("626243980@qq.com");
        int result = userMapper.insert(user); //帮我们自动生成ID
        System.out.println(result); //受影响的行数
        System.out.println(user); //发现ID会自动回填
    }

打印结果(发现ID自动回填):
在这里插入图片描述

主键自动生成策略(分布式系统唯一id生成):

主键生成策略汇总:https://www.cnblogs.com/haoxinyue/p/5208136.html

主键全局唯一策略(默认):使用雪花算法

在这里插入图片描述

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。
雪花算法几乎可以保证全球唯一

主键自增策略:

1.在实体类字段上设置 自增注解@TableId(type = IdType.AUTO)
在这里插入图片描述
2.数据库字段一定要是自增的
在这里插入图片描述
3.测试即可(发现自增1)
在这里插入图片描述

其余的源码解释:
public enum IdType {
    AUTO(0),	// 数据库id自增
    NONE(1),	// 未设置主键
    INPUT(2),	//手动输入(必须要自己写ID)
    ID_WIORKER(3),//默认的全局唯一id
    UUID(4),	//全局唯一id uuid
    ID_WORKER_STR(5);//ID_WIORKER 字符串表示法
}

2. update(更新操作)

测试:

    @Test
    //测试更新
    public void testUpadate(){
        User user = new User();
        // 通过条件自动拼接动态sql
        user.setId(5L);
        user.setName("哈哈哈哈");
        user.setAge(3);
        user.setEmail("626243980@qq.com");
        // 注意:updateById 但是参数是一个 对象
        int i = userMapper.updateById(user);
        System.out.println(i);
    }

在这里插入图片描述

@Test
    //测试更新
    public void testUpadate(){
        User user = new User();
        // 通过条件自动拼接动态sql
        user.setId(5L);
        user.setAge(2);
        // 注意:updateById 但是参数是一个 对象
        int i = userMapper.updateById(user);
        System.out.println(i);
    }

修改其中的一个参数
在这里插入图片描述
执行的是动态Sq

3.自动填充

创建时间、修改时间,这些操作都是数据库自动化完成的,我们不希望手动更新!
阿里巴巴开发手册 : 所有的数据库表(创建和修改): gmt_create、gmt_modified 几乎所有的表都要配置!,需要自动化实现

方式一: 数据库级别(工作中不建议):

1、在表中新增字段 create_time.update_time
create_time:
在这里插入图片描述
update_time:

再次测试插入方法,我们需要先把实体类同步

    private Date createTime;
    private Date updateTime;

执行修改方法后,自动更新了更改时间:
在这里插入图片描述

方式二: 代码级别
  1. 删除数据库的默认值和更新的操作
    在这里插入图片描述
  2. 在实体类字段属性上需要增加注解
    //执行插入操作的时候,自动填充当前时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //执行更新和插入操作的时候,自动填充和更新当前值
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

  1. 编写处理器来处理这个注解:
    在这里插入图片描述
@Slf4j
@Component // 一定不要忘记将处理器加入到IOC容器当中
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill");
        //setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
        this.setFieldValByName("createTime",new Date(), metaObject);
        this.setFieldValByName("updateTime",new Date(), metaObject);
    }

    // 更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill");
        this.setFieldValByName("updateTime",new Date(), metaObject);
    }
}

  1. 测试插入和更新(结果正确,且并没有改变数据库的内容)

乐观锁(具体等搞定JUC并发再来总结)

乐观锁: 顾名思义十分乐观,它总是认为不会出现问题,无论干什么都不去上锁!如果出现问题,再次更新值测试

version、new version(换版本进行更新值)
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败
例如:
乐观锁:  1.先查询,获得版本号() version = 1 
		2.执行时,如果当前的版本号发生变化,则执行失败(为了线程安全)
		----------------------A线程-----------------------
		update user set name = "kuangshen", version = version + 1
		where id = 2 and version = 1
		------B线程: 抢先完成,这个时候version变为2,会导致A修改失败-----
		update user set name = "kuangshen", version = version + 1
		where id = 2 and version = 1
测试Mybatis-plus的乐观锁插件

1.设置数据库
在这里插入图片描述
2.实体类增加对应的字段

    @Version //乐观锁Version注解
    private Integer version;

3.注册组件(新版已经弃用,不需要在注册即可)
在这里插入图片描述
4.测试
乐观锁成功案例(单线程的情况下):
自己将version作为条件进行更新和修改version 的值

//测试乐观锁成功案例!
    @Test
    public void testOptimisticLockerInterceptor(){
        //1.查询用户信息
        User user = userMapper.selectById(1L);
        //2.修改用户信息
        user.setName("酷虎");
        user.setEmail("626243980@qq.com");
        //3.执行更新操作
        userMapper.updateById(user);
    }

在这里插入图片描述
失败案例

    //测试乐观锁失败案例!(多线程情况下)

    @Test
    public void testOptimisticLockerInterceptor2(){
        // 线程 1
        User user = userMapper.selectById(1L);
        user.setName("酷虎111");
        user.setEmail("626243980@qq.com");

        // 模拟另外一个线程执行了插队操作
        User user2 = userMapper.selectById(1L);
        user2.setName("酷虎222");
        user2.setEmail("6262439800@qq.com");
        userMapper.updateById(user2);

        // 可以采用自旋锁来尝试多次提交
        userMapper.updateById(user); // 如果没有乐观锁则一定会覆盖插队线程的值
    }

在这里插入图片描述

悲观锁

悲观锁: 顾名思义十分悲观,它总是认为总是出现问题,无论干什么都去上锁!再去操作

4.查询操作

1. 批量查询:
    // 测试查询多个用户
    @Test
    public void testSelectBatchIds(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }

结果:
在这里插入图片描述

2. 按照条件查询(多条件)之一:使用map操作
    // 条件查询 map
    @Test
    public void testSelectByBatchIds(){
        HashMap<String, Object> map = new HashMap<>();
        //自定义查询
        map.put("name","哈哈哈哈");

        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

结果:
在这里插入图片描述

 // 条件查询 map
    @Test
    public void testSelectByBatchIds(){
        HashMap<String, Object> map = new HashMap<>();
        //自定义查询
        map.put("name","哈哈哈哈");
        map.put("age",3);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

在这里插入图片描述

3.分页查询

分页在网站使用的十分之多

  1. 原始的limit进行分页
  2. pageHelper第三方插件
  3. MyBatis-plus其实也内置了分页插件,可以直接使用
如何使用:
  1. 配置分页插件(是一个拦截器,最新版有更新的方法):
    在这里插入图片描述
    // 分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
  1. 直接使用page对象即可
    //测试分页查询
    @Test
    public void testPage(){
        // 参数为 current:当前页:1 size:页面大小:5个数据
        Page<User> userPage = new Page<>(1,5);
        userMapper.selectPage(userPage, null);
        //通过userPage.getRecords拿到具体的内容
        List<User> users = userPage.getRecords();
        users.forEach(System.out::println);
    }
  1. 查询结果
    在这里插入图片描述
  2. 基本是用
        // 参数为 current:当前页:1 size:页面大小:5个数据
        // 使用了分页插件操作后,所有分页操作也变得简单了
        Page<User> userPage = new Page<>(1,5);
        userMapper.selectPage(userPage, null);
        //通过userPage.getRecords拿到具体的内容
        List<User> users = userPage.getRecords();
        users.forEach(System.out::println);
        //userPage.getTotal:获得记录的总数
        System.out.println(userPage.getTotal());
        //是否有下一页
        userPage.hasNext();
        //是否有上一页
        userPage.hasPrevious();

删除操作

    //测试删除
    @Test
    public void testDeleteById(){
        userMapper.deleteById(1357165282440564738L);
    }

    //通过ID批量删除
    @Test
    public void testDeleteBatchId(){
        userMapper.deleteBatchIds(Arrays.asList(1357165282440564739L,1357165282440564740L));
    }

    //通过条件批量删除
    @Test
    public void testDeleteMap(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","哈哈哈哈");
        map.put("age",15);
        map.put("id",1357165282440564740L);
        userMapper.deleteByMap(map);

    }
逻辑删除

物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除,而是通过一个变量让它失效! deleted = 0 ⇒ deleted = 1

逻辑删除除的实例:

管理员可以查看被删除的记录,防止数据丢失,类似于回收站!

测试:
  1. 在数据库表中增加一个deleted字段
    在这里插入图片描述

  2. 实体类中增加相应字段

    @TableLogic //逻辑删除字段
    private Integer deleted;
  1. 在config中增加相应的字段(新版本已经不需要该操作):
    // 逻辑删除字段
    @Bean
    public ISqlInjector sqlInjector(){
        return new LogicSqlInjector();
    }

application.propertis中配置

# 配置逻辑删除
# 如果把这个值删除了,则这个值设置为1
mybatis-plus.global-config.db-config.logic-delete-value=1
# 如果这个值没有删除,则这个值设置为0
mybatis-plus.global-config.db-config.logic-not-delete-value=0
  1. 测试删除
    执行删除操作:
    在这里插入图片描述
    本质上是执行的更新操作
    在这里插入图片描述
  2. 查看结果:记录依旧在数据库,但是deleted的值改变了
    在这里插入图片描述
  3. 再执行查询这条用户的数据的时候,就会查不到当前的用户
    在这里插入图片描述
    在这里插入图片描述

性能分析插件

我们在平时的开发中,会遇到一些慢sql
Mybatis-plus也提供了性能分析插件,如果超过了这个时间就停止运行
作用:

  1. 导入插件
    pom.xml
        <!-- sql执行分析插件-->
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>

application.properties

#3.2.1以上使用
#modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 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
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
  1. 测试使用即可

条件构造器

十分重要: Wrapper
用于写一些复杂的sql就可以使用它来代替

测试一: 复杂的条件查询
//继承了BaseMapper,所有的方法都来自自己的父类,我们也可以编写自己的扩展方法
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
       // 查询name不为空,并且邮箱不为空,年龄>=12的用户
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper
                .isNotNull("name")
                .isNotNull("email")
                //age >= 12
                .ge("age",12);
        userMapper.selectList(queryWrapper).forEach(System.out::println); //和我们刚才学习的map对比学习
    }
测试二:按照条件查询单条数据
    @Test
    void test2(){
        // 查询名字为哈哈哈哈哈的
        QueryWrapper<User> wapper = new QueryWrapper<>();
        wapper.eq("name","哈哈哈哈哈");
        // 查询一个数据
        User user = userMapper.selectOne(wapper);
        // 如果出现多个摸棱两可的数据,则要使用List或者map
        System.out.println(user);

    }
测试3: 查询区间
    @Test
    void test3(){
        // 查询年龄在 20 ~ 30 岁之间的用户
        QueryWrapper<User> wapper = new QueryWrapper<>();
        wapper.eq("name","哈哈哈哈哈");
        // 查询区间
        wapper.between("age",20,30);

        Integer count = userMapper.selectCount(wapper);// 查询符合的数量
        System.out.println(count);
    }
测试4:模糊查询
 // 模糊查询
    @Test
    void test4(){
        QueryWrapper<User> wapper = new QueryWrapper<>();
        wapper
                //不包含 notLike: name中不包含:%e%
                .notLike("name","e")
                // 左 和 右(t,指的是当前传入的元素,这里就是以t开头的email)
                    // 左: % t(通配符在左边)
                    // 右: t %(通配符在右边)
                .likeRight("email","t");

        List<Map<String, Object>> maps = userMapper.selectMaps(wapper);// 查询符合的数量
        maps.forEach(System.out::println);
    }
测试5:内嵌查询
//内嵌查询
    @Test
    void test5(){
        QueryWrapper<User> wapper = new QueryWrapper<>();

        // id 在子查询中查出来
        wapper.inSql("id","select id from user where id < 3");
        List<Object> objects = userMapper.selectObjs(wapper);// 查询符合的数量
        // 实际执行的sql语句:Preparing:
                                //SELECT id,name,age,email,deleted,version,create_time,update_time FROM user
                                //  WHERE deleted=0 AND id IN (select id from user where id < 3)
        objects.forEach(System.out::println);
    }
测试6:排序查询
// 排序查询
@Test
void test6(){
    QueryWrapper<User> wapper = new QueryWrapper<>();
    // 通过id降序排序
    wapper.orderByDesc("id");
    List<User> users = userMapper.selectList(wapper);
    users.forEach(System.out::println);
}

代码自动生成器: 常用配置加注解(太强了!)

// 代码自动生成器
public class MyCode {
    public static void main(String[] args) {
        // 需要构建一个代码生成器对象
        AutoGenerator mpg = new AutoGenerator();

        // 配置策略

        // 1.全局配置
        GlobalConfig gc = new GlobalConfig();
        //输出目录
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
            // 配置作者名字
        gc.setAuthor("泽泽泽");
        //是否打开资源管理器(就是是否打开文件夹)
        gc.setOpen(false);
        //是否覆盖之前生成的代码
        gc.setFileOverride(false);
        //去掉Service的I前缀
        gc.setServiceName("%sService");
        // 设置主键的生成策略
        gc.setIdType(IdType.ID_WORKER);
        // 只是时间的日期
        gc.setDateType(DateType.ONLY_DATE);
        // 是否生成swagger
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        // 2.设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("zhurunze783");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        //3. 包的配置
        PackageConfig pcg = new PackageConfig();
        // 设置模块(为测试模块)
        pcg.setModuleName("test2");
        pcg.setParent("com.mystudy");
        pcg.setEntity("entity");
        pcg.setMapper("mapper");
        pcg.setService("service");
        pcg.setController("controller");
        mpg.setPackageInfo(pcg);

        //4. 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // 设置映射的表名
        strategy.setInclude("user");
        // 直接支持下划线转驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true); //自动生成lombok
        // 逻辑删除字段
        strategy.setLogicDeleteFieldName("deleted");

        // 自动填充配置
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(createTime);
        tableFills.add(updateTime);
        strategy.setTableFillList(tableFills);

        // 乐观锁配置
        strategy.setVersionFieldName("version");
        // 开启驼峰命名的配置
        strategy.setRestControllerStyle(true);
        // localhost:8080/hello_id_2(下划线命名)
        strategy.setControllerMappingHyphenStyle(true);
        mpg.setStrategy(strategy);


        mpg.execute(); //执行
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值