• Mybatis_plus


    快速入门

    使用第三方组件:

    • 导入对应的依赖
    • 研究依赖如何配置
    • 代码如何编写
    • 提高扩展技术能力!

    步骤

    1、创建数据库 mybatis-plus

    2、创建user表

    DROP TABLE IF EXISTS user;
    
    CREATE TABLE user
    (
    	id BIGINT(20) NOT NULL COMMENT '主键ID',
    	name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    	age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    	email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    	PRIMARY KEY (id)
    );
    
    DELETE FROM user;
    
    INSERT INTO user (id, name, age, email) VALUES
    (1, 'Jone', 18, 'test1@baomidou.com'),
    (2, 'Jack', 20, 'test2@baomidou.com'),
    (3, 'Tom', 28, 'test3@baomidou.com'),
    (4, 'Sandy', 21, 'test4@baomidou.com'),
    (5, 'Billie', 24, 'test5@baomidou.com');
    -- 真实开发中, version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified
    

    3、编写项目,初始化项目,使用Springboot初始化

    4、导入依赖

    <!--        数据库驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    <!--        lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    <!--        mybatis-plus-->
    <!--       自己开发的,并非官方的 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.0.5</version>
            </dependency>
    

    使用mybatis-plus 可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus!版本差异

    5、连接数据库!与mybatis相同

    # mysql配置
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=Asia/Shanghai&useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
    

    6、使用了mybatis-plus 之后

    • pojo
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    
    • mapper接口
    //在对应的Mapper上面实现基本的接口 BaseMapper
    @Repository   //代表是持久层
    public interface UserMapper extends BaseMapper<User> {
        //所有的CRUD操作已经编写完成了
        //不需要像以前一样配置大量的配置文件
    }
    

    注意:我们需要在主启动类上去扫描我们的mapper包下的所有接口@MapperScan("com.kuang.mapper")

    • 测试
    @SpringBootTest
    class MybatisPlusApplicationTests {
    
        //继承了BaseMapper,所有的方法都来自自己的父类
        //我们也可以像往常一样编写自己的扩展方法
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void contextLoads() {
            //查询全部的用户
            //参数是一个Wrapper,条件构造器,这里先不用设置为null
            List<User> list = userMapper.selectList(null);
            for (User user : list) {
                System.out.println(user);
            }
        }
    
    }
    

    结果:

    为什么通过短短几部就完成了?

    1、SQL谁来完成的? -->mybatis-plus 完成的

    2、方法哪里来的? -->mybatis-plus 完成的

    配置日志

    所有的sql现在是不可见的,我们希望知道它是如何执行的,所以我们必须看日志!

    # 配置日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 
    

    日志配置完毕,后面的学习就需要注意这个自动生成的SQL

    CRUD扩展

    insert

     //测试插入
        @Test
        public void testInsert(){
            User user = new User();
            user.setName("遇见狂神说");
            user.setAge(12);
            user.setEmail("9923014@qq.com");
            int insert = userMapper.insert(user);
            System.out.println(insert);
            System.out.println(user);
        }
    

    数据库插入的id默认值为:全局唯一id

    主键生成策略

    插入操作

    默认 ID_WORKER 全局唯一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。

    主键自增

    我们需要配置主键自增:

    1、实体类字段上@TableId(type = IdType.AUTO)

    2、数据库字段一定要自增!

    3、再次测试插入即可!

    其他源码解释

    public enum IdType {
        AUTO(0),	//数据库id自增
        NONE(1),	// 未设置主键
        INPUT(2),	// 手动输入
        ID_WORKER(3),	//默认的全局id
        UUID(4),	//全局唯一id
        ID_WORKER_STR(5);	//ID_WORKER 的字符串表示法
    
        private int key;
    
        private IdType(int key) {
            this.key = key;
        }
    
        public int getKey() {
            return this.key;
        }
    }
    

    更新操作

     //测试更新
        @Test
        public void testUpdate(){
            User user = new User();
            //通过条件自动拼接动态sql
            user.setId(6L);
            user.setName("遇见chenfangy");
            user.setAge(18);
            // 注意:updateById 需要传入的参数是一个对象!
            int i = userMapper.updateById(user);
            System.out.println(i);
        }
    

    所有的sql都是自动帮你动态配置的!

    自动填充

    创建时间、修改时间!这些个操作都是一般都是自动化完成的,我们不希望手动更新!

    阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified、几乎所有的表都要配置上!而且需要自动化!

    方式一:数据库级别(不建议这样使用)

    1、在表中新增字段 create_time,update_time

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

        private Date createTime;
        private Date updateTime;
    

    3、再次更新查看结果即可

    方式二:代码级别

    1、删除数据库的默认值、更新操作!(没有设置自动更新)

    2、实体类字段属性需要增加注解

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        //对应数据库中的主键(uuid、自增id、雪花算法、redis、zookeeper)
        @TableId(type = IdType.INPUT)   //IdType.INPUT一旦手动输入id后,就需要自己配置id了
        private Long id;
        private String name;
        private Integer age;
        private String email;
    
        //字段添加填充内容
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    }
    

    3、编写处理器来处理这个注解

    @Slf4j  //日志
    @Component  //一定不要忘记把处理器加到IOC容器中!
    public class MyMetaObjectHandler implements MetaObjectHandler {
        //插入时的填充策略
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill.....");
            //MetaObjectHandler 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);
        }
    }
    

    4、测试插入

    5、测试更新、观察时间即可

    乐观锁

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

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

    当要更新一条记录的时候,希望这条记录没有被别人更新
    乐观锁实现方式:

    • 取出记录时,获取当前version
    • 更新时,带上这个version
    • 执行更新时, set version = newVersion where version = oldVersion
    • 如果version不对,就更新失败
    乐观锁:1、先查询,获得版本号 version = 1
    -- A 
    update user set name  = "kuangshen",version = version + 1
    -- B 线程抢先完成,这个时候 version = 2 , 会导致 A 修改失败!
    update user set name = "kuangshen",version = version + 1
    where id = 2 and version = 1
    

    测试一下MP的乐观锁插件

    1、给数据中增加version字段!

    2、我们实体类加对应的字段

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

    3、注册组件

    /**
     * @author chenfangy
     * @Configuration 被这个注解标注说明是个配置类
     * @MapperScan 扫描我们的mapper文件
     */
    @MapperScan("com.kuang.mapper")
    @EnableTransactionManagement
    @Configuration
    public class MyBatisPlusConfig {
    
        /**
         * 
         *  注册乐观锁插件
         */
        @Bean
        public MybatisPlusInterceptor MybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }
    

    4、测试一下

    //测试乐观锁成功案例
        @Test
        public void testMybatisPlusLock(){
            //1、查询用户的信息
            User user = userMapper.selectById(1L);
            //2、修改用户的信息
            user.setName("小南风");
            user.setAge(18);
            //3、执行更新操作
            userMapper.updateById(user);
        }
        //测试乐观锁失败案例 多线程下
        @Test
        public void testMybatisPlusLock2(){
            //线程1
            User user = userMapper.selectById(1L);
            user.setName("小南风");
            user.setAge(18);
            // 模拟另外一个线程执行了插队操作
            User user2 = userMapper.selectById(1L);
            user2.setName("小南风222");
            user2.setAge(11);
            //3、执行更新操作
            userMapper.updateById(user2);
            // 自旋锁来多次尝试提交!
            userMapper.updateById(user);    //如果没有乐观锁就会覆盖插队线程的值!
        }
    

    查询操作

     //测试查询
        @Test
        public void testSelectById(){
            User user = userMapper.selectById(1L);
            System.out.println(user);
        }
    
        // 测试批量查询
        @Test
        public void testSelectByBatchId(){
            List<User> list = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
            list.forEach(System.out::println);
        }
    
        // 按条件查询之一使用Map使用
        @Test
        public void testSelectByBatchIds(){
            HashMap<String,Object> map = new HashMap<>();
            //自定义要查询
            map.put("name","遇见狂神说");
            map.put("age",12);
    
            List<User> list = userMapper.selectByMap(map);
            for (User user : list) {
                System.out.println(user);
            }
        }
    

    分页查询

    1、原始的limit进行分页

    2、pageHelper 第三方插件

    3、MP其实也内置了分页插件!

    使用方法

    1、配置拦截器组件即可!

    /**
         *
         * @return 分页查询
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    

    2、直接使用Page对象即可!

    //测试分页查询
        @Test
        public void testPage(){
            /**
             * 参数一:当前页
             * 参数二:页面大小
             * 使用的话前端传  pageNo:页码 pagesize:每页几条数据 current :当前页
             */
            Page<User> page = new Page<>(3,5);
            userMapper.selectPage(page,null);
            for (User record : page.getRecords()) {
                System.out.println(record);
            }
            System.out.println("当前页是------>"+page.getCurrent());
            System.out.println(page.getTotal()+"<---总数是");
        }
    

    删除操作

    基本的删除操作:

     // 测试删除
        @Test
        public void testDeleteById(){
            userMapper.deleteById(6L);
        }
    
        // 通过id批量删除
        @Test
        public void testDeleteBatchId(){
     userMapper.deleteBatchIds(Arrays.asList(1331491338792853506L,1331491338792853507L));	//数组中放的是主键user的id
        }
    
        // 通过条件删除
        @Test
        public void testDeleteMap(){
            HashMap<String,Object> map = new HashMap<>();
            map.put("name","遇见狂神说");
            userMapper.deleteByMap(map);
        }
    

    在工作中会遇到逻辑删除的问题!

    逻辑删除

    物理删除:从数据库中直接移除

    逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!delete = 0 --> delete = 1

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

    测试:

    1、在数据表中增加一个deleted字段

    2、pojo实体类中增加属性!

    /**
         *@TableLogic 逻辑删除
         */
        @TableLogic
        private Integer deleted;
    

    3、配置,3.3.0之后官方这样说:

    mybatis-plus:
      global-config:
        db-config:
          logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
    

    4、测试

    当再次去查1号这个用户时:

    条件构造器

    复杂的sql可以使用它来替代

    测试一:

     @Autowired
        private UserMapper userMapper;
    
        @Test
        void contextLoads() {
            //查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper
                    .isNotNull("name")      //查询年龄不为空
                    .isNotNull("email")     //查询邮箱不为空
                    .ge("age",12);      //查询年龄大于等于12
            userMapper.selectList(wrapper).forEach(System.out::println);
    
        }
    

    测试二:

    @Test
        void contextLoads2() {
            //查询名字为Tom的用户
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("name","Tom");
            User user = userMapper.selectOne(wrapper);  //查询一个数据,出现多个结果使用List或Map
            System.out.println(user);
        }
    

    测试三:

    @Test
        void contextLoads3() {
            //查询年龄在20~30岁之间的用户
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.between("age",20,30);   //区间
            Integer count = userMapper.selectCount(wrapper);//查询结果数
            System.out.println("----->"+count);
        }
    

    测试四: 查看输出的SQL进行分析

    @Test
        void contextLoads4() {
            //模糊查询
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // 左和右的区别   %t :以t(t代表某个字符)结尾   t% :以t开头
            wrapper
                    .notLike("name","a")
                    .likeRight("email","t");
    
            List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);//查询结果数
            for (Map<String, Object> map : maps) {
                System.out.println(map);
            }
        }
    

    测试五:

    @Test
        void contextLoads5() {
    
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // id 在子查询中查出来
            wrapper
                    .inSql("id","select id from user where id < 3");
    
            List<Object> list = userMapper.selectObjs(wrapper);
            list.forEach(System.out::println);
    
        }
    

    代码自动生成器

    demo:

    // 代码自动生成器
    public class GeneratorCode {
    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); // 是否覆盖
    gc.setServiceName("%sService"); // 去Service的I前缀
    gc.setIdType(IdType.ID_WORKER);
    gc.setDateType(DateType.ONLY_DATE);
    //gc.setSwagger2(true);  实体属性 Swagger2 注解
    mpg.setGlobalConfig(gc);
    //2、设置数据源
    DataSourceConfig dsc = new DataSourceConfig();
    dsc.setUrl("jdbc:mysql://localhost:3306/库名?
    useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
    dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    dsc.setUsername("root");
    dsc.setPassword("123456");
    dsc.setDbType(DbType.MYSQL);
    mpg.setDataSource(dsc);
    //3、包的配置
    PackageConfig pc = new PackageConfig();
    pc.setModuleName("code");	//主文件下面生成都在这个文件夹内
    pc.setParent("com.kuang");
    pc.setEntity("entity");
    pc.setMapper("mapper");
    pc.setService("service");
    pc.setController("controller");
    mpg.setPackageInfo(pc);
    //4、策略配置
    StrategyConfig strategy = new StrategyConfig();
    strategy.setInclude("address","accountalterrec","catalog","department","user_record","
    user_say"); // 设置要映射的表名  根据需要设置
    strategy.setNaming(NamingStrategy.underline_to_camel);
    strategy.setColumnNaming(NamingStrategy.underline_to_camel);
    //strategy.setEntityLombokModel(true); // 自动lombok;
    strategy.setLogicDeleteFieldName("deleted"); //配置逻辑删除 --MP
    // 自动填充配置
    TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
    TableFill gmtModified = new TableFill("gmt_modified",
    FieldFill.INSERT_UPDATE);
    ArrayList<TableFill> tableFills = new ArrayList<>();
    tableFills.add(gmtCreate);
    tableFills.add(gmtModified);
    strategy.setTableFillList(tableFills);
    // 乐观锁
    strategy.setVersionFieldName("version");
    strategy.setRestControllerStyle(true);
    strategy.setControllerMappingHyphenStyle(true); //
    localhost:8080/hello_id_2
    mpg.setStrategy(strategy);
    mpg.execute(); //执行
    }
    }
    

    感谢狂神老师一路陪伴!狂神老师B站课程链接-->

    频频回头的人注定走不了远路。
  • 相关阅读:
    【iCore4 双核心板_FPGA】例程八:乘法器实验——乘法器使用
    【iCore4 双核心板_ARM】例程十一:DMA实验——存储器到存储器的传输
    【iCore1S 双核心板_FPGA】例程八:触发器实验——触发器的使用
    【iCore4 双核心板_ARM】例程十:RTC实时时钟实验——显示时间和日期
    【iCore4 双核心板_ARM】例程九:ADC实验——电源监控
    WebBrowser的Cookie操作之流量刷新机
    网站受攻击的常用手段
    八爪鱼招标网的百度权重升为2了,独立IP也从0快速发展为1000
    网站添加数据出错,原来是MS SQL Server2008日志文件占据空间过大导致的
    当你的网站被疯狂攻击时你能做什么?
  • 原文地址:https://www.cnblogs.com/CHENFANgY/p/14040872.html
Copyright © 2020-2023  润新知