首页 > 其他分享 >MyBatis-Plus个人笔记

MyBatis-Plus个人笔记

时间:2022-10-11 17:45:12浏览次数:94  
标签:System 笔记 public blog println Plus MyBatis id out

第一章 MyBatis-Plus入门

1)MP简介

2)开发环境准备

第一步 pom.xml导入依赖:

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

第二步 yml配置数据源:

  1. 驱动类spring boot 2.0(内置jdbc5驱动),驱动类使用:driver-class-name: com.mysql.jdbc.Driver

    驱动类spring boot 2.1及以上(内置jdbc8驱动),驱动类使用: driver-class-name: com.mysql.cj.jdbc.Driver 否则运行测试用例的时候会有 WARN 信息

  2. 连接地址url MySQL5.7版本的url:

    jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false 
    

    MySQL8.0版本的url:

    serverTimezone是时区配置,可以选择UTCAsia/Shanghai或如下写法等。

    jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
    

    否则运行测试用例报告如下错误: java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more

# 配置数据源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    # ?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # 中国上海
    # ?serverTimezone=UTC		# 协调世界时	
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

第三步 造pojo和dao

实体类domain:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
    private Long id;
    private String title;
    private String author;
    private String createTime;
    private Integer views;
}

dao/mapper:

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型

BaseMapper源码地址:点击打开文件夹

public interface UserMapper extends BaseMapper<User> {
}

在启动类中注册:@MapperScan("com.yang.dao")

@SpringBootApplication
//扫描mapper接口所在的包
@MapperScan("com.yang.dao")
public class Springboot0103MybatisplusApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot0103MybatisplusApplication.class, args);
    }
}

tips:快速复制包地址

第四步 测试:

@SpringBootTest(classes = Springboot0103MybatisplusApplication.class)
public class BlogDaoTest {
    @Autowired
    private BlogDao blogDao;

    @Test
    public void getBlogById(){
        System.out.println( blogDao.selectById(1));
        List<Blog> list = blogDao.selectList(null);
        //使用list中的forEach方法
        list.forEach(System.out::println);
    }
}

其他设置:

# 使用mp自带的功能,打印日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3)CRUD

3.1)BaseMapper的CRUD

也就是直接通过dao层(mapper层)继承BaseMapper接口进行基本CRUD

增:

MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id

@Test
public void insertBlog(){
    Blog blog = new Blog();
    blog.setTitle("操作系统");
    blog.setAuthor("张三");
    blog.setCreateTime(new Date());
    blog.setViews(9999);
    int result = blogDao.insert(blog);
    System.out.println(result);
}

删:

  • 通过id删除记录:

    @Test
    public void testDeleteById(){
        //根据id删除
        //DELETE FROM blog WHERE id=?
        blogDao.deleteById(6);
    }	
    
  • 通过id批量删除:

    @Test
    public void testDeleteBatchIds(){
        //通过id批量删除记录
        //DELETE FROM blog WHERE id IN ( ? , ? , ? )
        List<Long> listId = Arrays.asList(3L,4L,5L);	//因为数据类型是Long 所以要加L
        int result = ublogDao.deleteBatchIds(listId);
        System.out.println("受影响行数:"+result);
    }
    
  • 通过map条件删除记录

        @Test
        public void deleteBlog(){
            //根据map集合中所设置的条件删除记录
            //DELETE FROM blog WHERE title = ? AND author = ?
            Map<String,Object> map = new HashMap<>();
            map.put("title","javaSE");
            map.put("author","杨昱杰");
            blogDao.deleteByMap(map);
        }
    

改:

@Test
public void updateBlog(){
    //UPDATE blog SET title=?, author=?, create_time=?, views=? WHERE id=?
    Blog blog = new Blog(1566338320572059649L, "稀里糊涂", "佚名", new Date(), 666);
    System.out.println("受影响的行数:\t"+blogDao.updateById(blog));
}

查:

默认方式
  • 通过map条件查询用户信息&根据多个id查询多个用户信息

  • 根据id查询全部查询请参考如上

    @Test
    public void testSelectByMap(){
        //通过map条件查询用户信息
        //SELECT * FROM blog WHERE title = ? AND author = ?
        Map<String, Object> map = new HashMap<>();
        map.put("title", "操作系统");
        map.put("author", "张三");
        List<User> list = userMapper.selectByMap(map);
        list.forEach(System.out::println);
        
        //根据多个id查询多个用户信息
        //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
        List<Long> idList = Arrays.asList(4L, 5L);
        List<User> list = userMapper.selectBatchIds(idList);
        list.forEach(System.out::println);
    }
    
测试自定义功能

第一步 dao/mapper

@Repository
public interface BlogDao extends BaseMapper<Blog> {
    /**
     * 根据id查询用户信息为map集合
     * @param id
     * @return
     */
    public Map<String,Object> selectBlogByMapId(Long id);
}

第二步 mybatis-mapper.xml :

注意事项:mybatis-mapper.xml必须放在resources目录下的mapper文件夹下

<?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.yang.dao.BlogDao">
    <select id="selectBlogByMapId" resultType="map">
        select id,title,author,create_time,views from blog where id=#{id};
    </select>
</mapper>

第三步 测试

@Test
public void selectBlogByMapIdTest(){
    System.out.println(blogDao.selectBlogByMapId(2L));
}

tips: IDEA自定义mybats映射文件(xml)模板

mapper 模板:

这里特别注意: namespace需指定数据层(dao)中的接口类

<?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="">

</mapper>

补充:这里不需要核心配置文件

cfg 模板(核心配置文件):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>

3.2)Service CRUD 接口⭐

3.2.1)常用接口开头参数:

参数开头 行为
Save 添加 & 修改 (带Id)
Remove 删除
Update 修改
Get 根据条件查询
List 查询所有或多个
Page 分页
Count 数据条数

官网直达

说明:

  • 通用 Service CRUD 封装IService 接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper 为条件构造器

MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑 详情查看源码IService和ServiceImpl

3.2.2)实现

第一步 创建Service接口和实现类:

service层接口:

注意:要继承IService<T>

/**
* UserService继承IService模板提供的基础功能
*/
public interface BlogService extends IService<Blog> {
}

service层impl实现类:

注意:IService有自己的实现类ServiceImpl<M extends BaseMapper<T>, T>,所以service层实现类可以先继承ServiceIpml再实现自己的BlogService接口

/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现
* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@Service
public class BlogServiceImpl extends ServiceImpl<BlogDao, Blog> implements BlogService {
}

/**
=====> dao层(就是接着上面写的dao) <=====
@Repository
public interface BlogDao extends BaseMapper<Blog> {
}
*/

第二步 测试:

@SpringBootTest
public class BlogServiceTest {
    @Autowired
    private BlogService service;

    @Test
    void testGetCount(){
        //查询有多少条数据:SELECT COUNT( * ) FROM blog
        System.out.println("总共有:"+service.count()+" 条数据");
       //批量添加:INSERT INTO blog ( id, title, author, create_time, views ) VALUES ( ?, ?, ?, ?, ? )
        List<Blog> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Blog blog = new Blog();
            blog.setTitle("设计模式"+i);
            blog.setAuthor("Bob"+i);
            blog.setCreateTime(new Date());
            blog.setViews(i*7);
            list.add(blog);
        }
        System.out.println("插入是否成功:"+service.saveBatch(list));
        System.out.println("总共有:"+service.count()+" 条数据");
    }
}

第二章 M-P常用注解

@TableName

经过以上的测试,在使用MyBatis-Plus实现基本的CRUD时,我们并没有指定要操作的表,只是在 Mapper/dao接口继承BaseMapper时,设置了泛型Blog,而操作的表为blog表 由此得出结论,MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决 定,且默认操作的表名和实体类型的类名一致

如果表名和实体类不一致则会报错(首字母大小写不敏感,所以是怎么大小写没关系)

在实体类上添加@TableName("数据库表名"),标识实体类对应的表,即可成功执行SQL语句

//设置实体类所对应的数据库表名
@TableName("blog")
public class Blog {
   //....
}

Tips: 这里还有另一种方式让实体类找到表: 全局配置

这里的tb_是指的是数据库的前缀,他们的匹配规则是:前缀 + 定义的实体类类名 所以实体类类名还是要遵循数据表的取名规范去定义。

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_

@TableId

1)@TableId与他的两个属性

@TableId具有两个属性:value,type

@TableId

经过以上的测试,MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认 基于雪花算法的策略生成id。

若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主 键列吗? 我们实体类中的属性id改为uid,将表中的字段id也改为uid,测试添加功能

value:

标注数据库表主键名

注意: @TableId注解只是将该字段指定为主键,如果数据库字段和属性名不一致,仍然会报错。但是可以通过 @TableId注解的Value属性指定数据库表名。

用法:@TableId(value="数据库主键字段名")

type:

设置主键以何种方式自增,用法:@TableId(type = IdType.AUTO)

常用的主键策略:(还有其他策略,值列举两种)

描述
IdType.ASSIGN_ID(默 认) 基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
IdType.AUTO 使用数据库的自增策略,注意,该类型请确保数据库设置了id自增, 否则无效
public class Blog {
    //将属性所对应的字段指定为主键
    @TableId(value="id",type = IdType.AUTO)
    private Long id;
	....
}

全局配置策略:

mybatis-plus:
	configuration:
		# 配置MyBatis日志
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
	global-config:
		db-config:
			# 配置MyBatis-Plus操作表的默认前缀
			table-prefix: t_
             # 配置MyBatis-Plus的主键策略
             id-type: auto

@TableField

  • 若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格 例如实体类属性userName,表中字段user_name 此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格 相当于在MyBatis中配置
  • 若实体类中的属性和表中的字段不满足情况1 例如实体类属性name,表中字段username 此时需要在实体类属性上使用@TableField("username")设置属性所对应的字段名
@TableField("create_time")
private Date createTime;

@TableLogic

逻辑删除 :

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库 中仍旧能看到此条数据记录 使用场景:可以进行数据恢复

实现逻辑删除:

第一步 数据库中创建逻辑删除状态列,设置默认值为0

第二步 实体类中添加逻辑删除属性

@TableLogic
private Integer isDelete;

第三步 测试

测试删除功能,真正执行的是修改 UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0 测试查询功能,被逻辑删除的数据默认不会被查询 SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0

第三章 条件构造器和常用接口⭐

1)条件构造器简介

  • Wrapper : 条件构造抽象类,最顶端父类
    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询条件封装
      • UpdateWrapper : Update 条件封装
      • AbstractLambdaWrapper : 使用Lambda 语法
        • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper

2)QueryWrapper 使用

2.1)组装查询条件

/**
* 查找作者名字中带“o”,展现量在60-555之间和标题不为空的用户信息
* SELECT id,title,author,create_time,views,is_delete FROM blog WHERE is_delete=0
* AND (author LIKE ? AND views BETWEEN ? AND ? AND author IS NOT NULL)
*/
@Test
public void getQueryWrapper01(){
    //条件构造器
    QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("author","o")
        .between("views",60,555)   //ge >= ;  le <=   between [x~X]之间
        .isNotNull("author");
    List<Blog> resultList = service.list(queryWrapper);
    resultList.forEach(System.out::println);
}

部分参数说明:

  • ge :>= 用法:.ge(“字段名”,num)
  • le :<= 用法:.ge(“字段名”,num)
  • between : [x~X]之间 用法:.between("字段名",x,X)

2.2)组装排序条件

    /**
     * 根据views降序排序,若views相同则按照id升序排序,且没有views和title为空的不参与排序
     * SELECT id,title,author,create_time,views,is_delete FROM blog WHERE is_delete=0 
     * AND (views IS NOT NULL AND title <> ?) ORDER BY views DESC,id ASC
     */
    @Test
    public void getQueryWrapper02(){
        QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("views")	//倒叙	
                .orderByAsc("id")	//升序
                .isNotNull("views")	//不是null
                .ne("title","");	//不等于空
        List<Blog> resultList = service.list(queryWrapper);
        resultList.forEach(System.out::println);
    }

注意:isNotNull这里有个小坑

如果数据库表字段是空而不是null,那么使用isNotNull就会失效,需要使用.ne("字段名","")解决

原因是:空值也就是在字段中存储NULL值,空字符串就是字段中存储空字符(’’)。

区别:

1)占用空间区别:
空字符串(’’)的长度是0,是不占用空间的;而的NULL的长度是NULL,其实它是占用空间的。

NULL columns require additional space in the row to record whether their values are NULL.
NULL列需要行中的额外空间来记录它们的值是否为NULL。

2)插入/查询方式区别:

NULL值查询使用is null/is not null查询,而空字符串(’’)可以使用=或者!=、<、>等算术运算符。

3)COUNT 和 IFNULL函数

使用 COUNT(字段) 统计会过滤掉 NULL 值,但是不会过滤掉空字符串。

总结:

  1. 空字符串不占空间,NULL值占空间。当字段不为NULL时,也可以插入空字符串。

  2. 当使用 IS NOT NULL 或者 IS NULL 时,只能查出字段中没有不为NULL的或者为 NULL 的,不能查出空字符串。

  3. 判断NULL 用IS NULL 或者 is not null,SQL 语句函数中可以使用IFNULL()函数来进行处理,判断空字符用 =’‘或者<>’'来进行处理。

  4. 在进行count()统计某列的记录数的时候,如果采用的NULL值,会被系统自动忽略掉,但是空字符串是会进行统计到其中的。

2.3)组装删除条件

    /**
     * 删除title为空的用户信息
     * UPDATE blog SET is_delete=1 WHERE is_delete=0 AND (title = ? AND title IS NULL)
     */
    @Test
    void getQueryWrapper03(){
        QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("title","");    //等于“”
        queryWrapper.isNull("title");
        System.out.println(service.remove(queryWrapper));
    }

2.4)使用QueryWrapper修改

先定义QueryWrapper查询条件,修改时需要传两个值:对象queryWrapper

/**
* UPDATE blog SET title=?, author=? WHERE is_delete=0 AND (views <= ? AND author LIKE ? OR title = ?)
* 将views小于666并且author包含2或title为空的用户信息修改
*/
@Test
void getUpdateWrapper01(){
    QueryWrapper<Blog> queryWrapper = new QueryWrapper<>();
    queryWrapper.le("views",666)
        .like("author","2")
        .or()
        .eq("title","");
    Blog blog = new Blog();
    blog.setAuthor("张三");
    blog.setTitle("java编程思想");
    System.out.println(service.update(blog,queryWrapper));
}

3)Mybatis-plus 分页插件

3.1)分页插件

复习SQL Limit:

# limit 起始位置,返回的条数
SELECT * FROM qinglan_tb LIMIT 0,4

要检索查询返回的行的一部分,请使用LIMITOFFSET子句。 以下说明了这些子句的语法:

SELECT 
 column_list
FROM
 table1
ORDER BY column_list
LIMIT row_count OFFSET offset;
SQL

在这个语法中,

  • row_count确定将返回的行数。
  • OFFSET子句在开始返回行之前跳过偏移行。 OFFSET子句是可选的。 如果同时使用LIMITOFFSET子句,OFFSET会在LIMIT约束行数之前先跳过偏移行

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

第一步 添加配置类

@MapperScan("com.yang.dao")	//可以将主类中的注解移到此处
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

第二步 测试

@Test
void testPage(){
    //设置分页参数
    Page<AccountPojo> page = new Page<>(1,3);
    service.page(page,null);
    //获取分页数据
    List<AccountPojo> list = page.getRecords();
    list.forEach(System.out::println);
    System.out.println("当前页:"+page.getCurrent());
    System.out.println("每页显示的条数:"+page.getSize());
    System.out.println("总记录数:"+page.getTotal());
    System.out.println("总页数:"+page.getPages());
    System.out.println("是否有上一页:"+page.hasPrevious());
    System.out.println("是否有下一页:"+page.hasNext());
}

3.2)xml自定义分页

第一步 BlogMapper中定义接口方法

这里依旧使用的是mybatis-plus的分页插件,所以需要将Page放置于第一个参数

/**
*通过views查询用户信息分页
* @param page  mybatis-plus所提供的分页对象,必须位于第一个参数的位置
* @param views
* @return
* 这里依旧使用的是mybatis-plus的分页插件,所以需要将Page放置于第一个参数
*/
Page<Blog> selectPageVo(@Param("page") Page<Blog> page, @Param("views")Integer views);

第二步 BlogMapper.xml中编写SQL

<!--Page<Blog> selectPageVo(@Param("page") Page<Blog> page, @Param("views")Integer views);-->
<select id="selectPageVo" resultType="Blog">
    select * from blog where views > #{views}
</select>

这里resultType使用了类型别名Blog,所以在配置文件中需要配置一下:

mybatis-plus:
# 配置类型别名所对应的包
type-aliases-package: com.yang.domain

第三步 测试

    @Test
    public Object testSelectPageVo(){
        Page<Blog> page = new Page<>(1, 3);
        blogDao.selectPageVo(page,80);
        System.out.println(page);
        return page;
    }

3.3)乐观锁

场景模拟

一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。

数据表创建(t_product):

CREATE TABLE t_product(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
	price INT(11) DEFAULT 0 COMMENT '价格',
	version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
	PRIMARY KEY(id)
)
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

代码实现冲突:

@Test
void testProduct01(){
    //小李查询的商品价格
    Product productLi = productDao.selectById(1);
    System.out.println("小李查询的商品价格:"+productLi.getPrice());
    //小王查询的商品价格
    Product productWang = productDao.selectById(1);
    System.out.println("小王查询的商品价格:"+ productWang.getPrice());

    //小李将商品价格+50
    productLi.setPrice(productLi.getPrice()+50);
    productDao.updateById(productLi);

    //小王将商品价格-30
    productWang.setPrice(productWang.getPrice()-30);
    productDao.updateById(productWang);

    //老板查询的商品价格
    Product productBoss = productDao.selectById(1);
    System.out.println("老板查询的商品价格:"+ productBoss.getPrice());
}

执行代码后,price为70

Mybatis-Plus实现乐观锁:

第一步 修改实体类

添加 @Version注解,表示该字段为版本号

@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    @Version
    private Integer version;
}

第二步 添加乐观锁插件配置

@MapperScan("com.yang.dao")
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

第三步 测试

和上面测试代码一样,但是数据输出price为150,Version变为1,小李的操作执行了,小王的没有执行

第四步 优化测试

@Test
void testProduct01(){
    //小李查询的商品价格
    Product productLi = productDao.selectById(1);
    System.out.println("小李查询的商品价格:"+productLi.getPrice());
    //小王查询的商品价格
    Product productWang = productDao.selectById(1);
    System.out.println("小王查询的商品价格:"+ productWang.getPrice());

    //小李将商品价格+50
    productLi.setPrice(productLi.getPrice()+50);
    int result1 = productDao.updateById(productLi);
    System.out.println("小李修改的结果:"+result1);

    //小王将商品价格-30
    productWang.setPrice(productWang.getPrice()-30);
    int result2 = productDao.updateById(productWang);
    System.out.println("小王修改的结果:"+result1);
    
    //优化地方
    if (result2 == 0){
        //失败重试,重新获取version并更新
        Product productWangNew = productDao.selectById(1);
        productWangNew.setPrice(productWangNew.getPrice()-30);
        productDao.updateById(productWangNew);
    }

    //老板查询的商品价格
    Product productBoss = productDao.selectById(1);
    System.out.println("老板查询的商品价格:"+ productBoss.getPrice());
}
//老板查询的商品价格:120
//version:2

3.4)通用枚举

表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用MyBatis-Plus的通用枚举 来实现

第一步 创建通用枚举类型

在com.yang.enums包下创建枚举类,注意属性要打上 @EnumValue注解,并创建get方法和构造方法

@Getter
public enum  SexEnum {
    MALE(1,"男"),
    FEMALE(0,"女");

    @EnumValue
    private Integer sex;
    private String sexName;

    SexEnum(Integer sex, String sexName) {
        this.sex = sex;
        this.sexName = sexName;
    }
}

在实体类中添加枚举属性:

private SexEnum sex;

第二步 配置扫描通用枚举

注意:mybatisplus从 3.5.2版本 开始无需配置扫描通用枚举了

mybatis-plus:
  # 配置扫描通用枚举
  # mybatisplus从 3.5.2版本 开始无需配置扫描通用枚举了
  type-enums-package: com.yang.enums

第三步 测试

@Test
public void insertBlog(){
    Blog blog = new Blog();
    blog.setTitle("操作系统");
    blog.setAuthor("李四");
    blog.setCreateTime(new Date());
    blog.setViews(11);
    blog.setSex(SexEnum.MALE);
    System.out.println(SexEnum.FEMALE);  //FEMALE
    int result = blogDao.insert(blog);
    System.out.println(result);
}

第四章 代码生成器

第一步 导入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

第二步 创建生成类

public class FastAutoGeneratorTest {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false", "root", "root")
            .globalConfig(builder -> {
                builder.author("atguigu") // 设置作者
                        // .enableSwagger() // 开启 swagger 模式
                        .fileOverride() // 覆盖已生成文件
                        .outputDir("D://mybatis_plus"); // 指定输出目录
            })
            .packageConfig(builder -> {
                builder.parent("com.yang") // 设置父包名
                        .moduleName("mybatisplus") // 设置父包模块名
                        .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
        // 设置mapperXml生成路径
            })
            .strategyConfig(builder -> {
                builder.addInclude("t_product") // 设置需要生成的表名
                        .addTablePrefix("t_", "c_"); // 设置过滤表前缀
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
            .execute();
    }

}

生成的效果

第五章 多数据源

适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景,其他场景类似 场景说明: 我们创建两个库,分别为:mybatis_plus(以前的库不动)与mybatis_plus_1(新建),将 mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例 分别获取用户数据与商品数据,如果获取到说明多库模拟成功

第一步 创建数据库及表

创建数据库mybatis_plus_1和表product

CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus_1`;
CREATE TABLE product
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
# 添加一条数据
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
# 删除mybatis_plus库product表(我这里是mybatis)
use mybatis_plus;
DROP TABLE IF EXISTS product;

第二步 引入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>	

第三步 配置多数据源

说明:注释掉之前的数据库连接,添加新配置

注意:?后需根据上面所诉补全

spring:
    # 配置数据源信息
    datasource:
        dynamic:
        # 设置默认的数据源或者数据源组,默认值即为master
        primary: master
            # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
            strict: false
            datasource:
                master:
                    url: jdbc:mysql://localhost:3306/mybatis_plus?.....
                    driver-class-name: com.mysql.cj.jdbc.Driver
                    username: root
                    password: root
                slave_1:
                    url: jdbc:mysql://localhost:3306/mybatis_plus_1?....
                    driver-class-name: com.mysql.cj.jdbc.Driver
                    username: root
                    password: root

第四步 创建service

注意:此处省略了实体类和mapper

用户Service

public interface UserService extends IService<User> {
}
@DS("master") //指定所操作的数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

商品Service

public interface ProductService extends IService<Product> {
}
@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}

第五步 测试

@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@Test
public void testDynamicDataSource(){
    System.out.println(userService.getById(1L));
    System.out.println(productService.getById(1L));
}

结果:

  1. 都能顺利获取对象,则测试成功
  2. 如果我们实现读写分离,将写操作方法加上主库数据源,读操作方法加上从库数据源,自动切 换,是不是就能实现读写分离?

第六章 MyBatisX插件

1)MyBatisX插件简介

MyBatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率

但是在真正开发过程中,MyBatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表 联查,我们就需要自己去编写代码和SQL语句,我们该如何快速的解决这个问题呢,这个时候可 以使用MyBatisX插件

MyBatisX一款基于 IDEA 的快速开发插件,为效率而生。

MyBatisX插件用法:https://baomidou.com/pages/ba5b24/

安装:

2)MyBatisX代码快速生成

第一步 创建一个正常的springboot+mybatis-plus项目

第二步 使用IDEA连接数据库

  1. 点击Database连接数据库

  1. 选择一个数据表,右键选择第一项

  1. MyBatisX的一些配置

  1. 完成

3)使用MyBatisX插件CRUD

案例:在mapper接口中,定义方法名listAll(会有提示),然后按下Alt+Enter键就会自动补齐语句和生成SQL在ProductMapper.xml中

@Repository
public interface ProductMapper extends BaseMapper<Product> {
    List<Product> listAll();
    int insertAll(Product product);
}

生成的SQL语句

测试:

@Test
void contextLoads() {
    System.out.println(mapper.selectList(null));
    Product product = new Product();
    product.setName("iPhone13");
    product.setPrice(5999);
    product.setVersion(0);
    mapper.insertAll(product);
    System.out.println(mapper.selectList(null));
}

效果:

标签:System,笔记,public,blog,println,Plus,MyBatis,id,out
From: https://www.cnblogs.com/fwdba/p/16780025.html

相关文章

  • tracer ftrace笔记(10)—— trace内容分析
    基于Linux-5.10一、trace打印格式1.在把rawtrace数据格式化成用户可读的形式时,trace数据分成两部分:一部分是common信息,一部分是用户自定义的trace信息,这两部分分......
  • Mybatis批量操作
    foreach标签foreach元素的属性主要有item,index,collection,open,separator,closeitem表示集合中每一个元素进行迭代时的别名index指定一个名字,用于表示在迭代过程中,每次迭代到......
  • Mybatis通用Mapper(springboot环境下使用)
    ​​mapper3通用接口大全​​​:​​https://gitee.com/free/Mapper.git​​1、添加pom文件依赖<!--mapper--><dependency><groupId>tk.mybatis</groupId><artifact......
  • git 笔记:Windows与Ubuntu中git commit编辑完成后的保存退出方法。
    WindowsWindows下gitcommit后会进入vim界面。输入小写字母i,此时进入编辑模式,可以输入你想输入的内容。按下esc键,此时退出编辑模式,输入英文语法下的冒号:,再输入wq即......
  • tracer ftrace笔记(9)—— TRACE_EVENT 初始化
    基于Linux-5.10一、内核启动时遍历trace_event_call初始化流程1.TRACE_EVENT()多次展开定义了各种实现函数以后,最终把本event的所有的函数和数据存放在一个类型为s......
  • jpa整合mybatis模板解析、hibernate整合mybatis模板解析
    jpa整合mybatis模板解析、hibernate整合mybatis模板解析jpa是hibernate的封装,主要用于spring全家桶套餐。hibernate难以编写复杂的SQL。例如一个订单查询,查询条件有时间......
  • drf学习笔记
    今日内容概要内置认证类,权限类,频率类django的配置文件每个配置项的作用过滤类的其他使用全局异常处理接口文档今日内容详细内置认证类,权限类,频率类内置的认......
  • 多线程学习笔记(Java深度调试日常学习更新)
    1、单CPU时多线程不带来性能上的提升(有可能在线程切换时导致性能下降)因为多线程中,当一个线程被阻塞时,另一个线程可以继续利用CPU,这样的场景可以充分及时利用CPU2、锁的本......
  • (笔记)两行代码实现Windows和Ubuntu20.04虚拟机文件复制(超精简)
     因工作需要在Windows系统中安装虚拟机,在VMware中装了Ubuntu20.04,发现文件复制粘贴无法实现拖拽,找了很多方法,都存在各种问题和不方便,操作太麻烦,终于找到两行代码实现两系......
  • IT项目管理学习笔记3(项目时间管理)
    IT项目管理学习笔记3(项目时间管理)项目时间管理的主要过程计划进度管理(planningschedulemanagement)定义活动(definingactivities)排序活动(sequenceactivit......