注:当前没有使用spring-boot

  1. 使用mybatis-plus一般用于做单表的增删改查,如果希望去做多表的连查,也可以自定义方法,自定义SQL去执行。
  2. [MyBatis-Plus (opens new window)](https://github.com/baomidou/mybatis-plus)(简称 MP)是一个 [MyBatis (opens new window)](http://www.mybatis.org/mybatis-3/)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
  3. 对于单表的增删改查,mybatis-plus封装了对应的方法,直接调用即可实现对应的功能,无需编写SQL语句。

入门案例

1、导入依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-jdbc</artifactId>
  5. <version>5.2.8.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-context</artifactId>
  10. <version>5.2.8.RELEASE</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>com.alibaba</groupId>
  14. <artifactId>druid</artifactId>
  15. <version>1.1.22</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>mysql</groupId>
  19. <artifactId>mysql-connector-java</artifactId>
  20. <version>5.1.43</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.projectlombok</groupId>
  24. <artifactId>lombok</artifactId>
  25. <version>1.18.18</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>com.baomidou</groupId>
  29. <artifactId>mybatis-plus</artifactId>
  30. <version>3.0.5</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>junit</groupId>
  34. <artifactId>junit</artifactId>
  35. <version>4.12</version>
  36. </dependency>
  37. </dependencies>

2、创建mapper层接口,接口需要继承BaseMapper接口,BaseMapper接口的泛型指定了当前mapper处理的数据类型

  1. public interface UserMapper extends BaseMapper<User> {
  2. }

3、编写实体类User

  1. @Data
  2. @TableName("t_user")//如果表名与类名不一致,使用@TableName指定当前实体类映射的数据库表名
  3. public class User {
  4. @TableId(type = IdType.AUTO)//指定主键生成策略
  5. private Long id;
  6. private String name;
  7. private Integer age;
  8. private String email;
  9. @TableField(fill = FieldFill.INSERT)//指定自动填充策略
  10. private Date gmtCreate;
  11. @TableField(fill = FieldFill.INSERT_UPDATE)
  12. private Date gmtModified;
  13. }

4、mybatisplus通过MetaObjectHandler来处理自动填充,需要实现MetaObjectHandler接口

  1. public class AutoFillUtil implements MetaObjectHandler {
  2. /**
  3. * 新增时自动填充
  4. * @param metaObject
  5. */
  6. @Override
  7. public void insertFill(MetaObject metaObject) {
  8. this.setFieldValByName("gmtCreate",new Date(),metaObject);
  9. this.setFieldValByName("gmtModified",new Date(),metaObject);
  10. }
  11. /**
  12. * 修改时自动填充
  13. * @param metaObject
  14. */
  15. @Override
  16. public void updateFill(MetaObject metaObject) {
  17. this.setFieldValByName("gmtModified",new Date(),metaObject);
  18. }
  19. }

5、编写spring的配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
  7. <context:component-scan base-package="com.woniuxy"/>
  8. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  9. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  10. <property name="url" value="jdbc:mysql:///mybatisplus?characterEncoding=utf-8&amp;useSSL=false"/>
  11. <property name="username" value="root"/>
  12. <property name="password" value="root"/>
  13. </bean>
  14. <!-- 配置mybatisplus的sqlsessionfactorybean -->
  15. <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
  16. <property name="dataSource" ref="dataSource"/>
  17. <!--指定mybatisplus的全局配置-->
  18. <property name="globalConfig" ref="globalConfig"/>
  19. </bean>
  20. <!--进行mybatisplus全局配置-->
  21. <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
  22. <!--配置metaObjectHandler,用于实现自动填充-->
  23. <property name="metaObjectHandler">
  24. <bean class="com.woniuxy.utils.AutoFillUtil"/>
  25. </property>
  26. </bean>
  27. <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  28. <property name="basePackage" value="com.woniuxy.mapper"/>
  29. </bean>
  30. </beans>

6、测试

  1. public class MybatisPlusTest {
  2. private UserMapper userMapper;
  3. @Before
  4. public void init(){
  5. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  6. userMapper=applicationContext.getBean("userMapper",UserMapper.class);
  7. }
  8. @Test
  9. public void test(){
  10. List<User> users = userMapper.selectList(null);
  11. System.out.println(users);
  12. }
  13. @Test
  14. public void testAdd(){
  15. User entity = new User();
  16. entity.setName("王五");
  17. entity.setAge(35);
  18. entity.setEmail("3@3.3");
  19. System.out.println(entity);
  20. userMapper.insert(entity);
  21. System.out.println(entity);
  22. }
  23. }

乐观锁
  1. 乐观锁 : 对一切操作保持乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,

再次更新值测试 。

  1. 悲观锁:对一切操作保持悲观,它总是认为会出现问题,无论干什么都会上锁!再去操作!
  2. 乐观锁实现方式:

1.数据库表中应该有version字段,用于记录当前数据的版本

2.从数据库中取出记录时,获取当前 version

3.更新时,带上获得的version

4.执行更新时, set version = newVersion where version = oldVersion ,如果version不对,就更新失败。

实现步骤:

1、修改数据库表,增加version字段

2、修改实体类,增加version属性,且在属性上加@Version注解

3、修改spring配置文件,在MybatisSqlSessionFactoryBean的plugins中增加加上乐观锁插件。

  1. <!-- 配置mybatisplus的sqlsessionfactorybean -->
  2. <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
  3. <property name="dataSource" ref="dataSource"/>
  4. <!--指定mybatisplus的全局配置-->
  5. <property name="globalConfig" ref="globalConfig"/>
  6. <!--配置乐观锁插件-->
  7. <property name="plugins">
  8. <array>
  9. <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
  10. </array>
  11. </property>
  12. <!-- 配置mybatis的日志记录,输出在控制台 -->
  13. <property name="configuration">
  14. <bean class="com.baomidou.mybatisplus.core.MybatisConfiguration">
  15. <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
  16. </bean>
  17. </property>
  18. </bean>

3、测试

  1. /**
  2. * 测试乐观锁
  3. */
  4. @Test
  5. public void testUpdate(){
  6. //线程1:想把ID为6的人改成张三丰
  7. User user1 = userMapper.selectById(6L);//根据id查询记录
  8. user1.setName("张三丰");
  9. //线程2插队了:把ID为6的人改成张无忌
  10. User user2=userMapper.selectById(6L);
  11. user2.setName("张无忌");
  12. //线程2插队成功,执行了修改
  13. userMapper.updateById(user2);//根据ID修改数据库记录。
  14. userMapper.updateById(user1);//线程1开始执行修改
  15. }

逻辑删除
  1. 逻辑删除:如果配置了逻辑删除,则此时的删除操作都不会真正去执行delete语句,而是执行update
  2. 注意:逻辑删除操作在mybatisplus高版本中,已被移除。

实现步骤

1、数据库表中添加字段deleted 默认值给个0,表示未删除状态,

2、在实体类中添加属性deleted,属性上加@TableLogic注解,表示启用逻辑删除

3、修改spring配置文件,在globalConfig配置sqlInjector,值使用LogicSqlInjector

  1. <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
  2. <!--配置metaObjectHandler,用于实现自动填充-->
  3. <property name="metaObjectHandler">
  4. <bean class="com.woniuxy.utils.AutoFillUtil"/>
  5. </property>
  6. <!--配置逻辑删除-->
  7. <property name="sqlInjector">
  8. <bean class="com.baomidou.mybatisplus.extension.injector.LogicSqlInjector"/>
  9. </property>
  10. </bean>

4、测试

  1. /**
  2. * 测试逻辑删除
  3. */
  4. @Test
  5. public void testLogicDelete(){
  6. userMapper.deleteById(1402517259338924034L);
  7. }

分页插件
  1. 配置分页插件:mybatis-plus自带分页插件,不需要单独导入外部依赖。

实现步骤

1、配置分页插件

  1. <!-- 配置mybatisplus的sqlsessionfactorybean -->
  2. <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
  3. <property name="dataSource" ref="dataSource"/>
  4. <!--指定mybatisplus的全局配置-->
  5. <property name="globalConfig" ref="globalConfig"/>
  6. <!--配置插件-->
  7. <property name="plugins">
  8. <array>
  9. <!--分页插件-->
  10. <bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/>
  11. <!--乐观锁插件-->
  12. <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
  13. </array>
  14. </property>
  15. <!-- 配置mybatis在控制台输出SQL -->
  16. <property name="configuration">
  17. <bean class="com.baomidou.mybatisplus.core.MybatisConfiguration">
  18. <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
  19. </bean>
  20. </property>
  21. </bean>

2、创建page对象,用于封装分页查询后得到的结果,并设置分页查询的条件,调用selectPage方法完成分页查询

  1. /**
  2. * 测试分页查询
  3. */
  4. @Test
  5. public void testPageQuery(){
  6. //创建page对象,该对象创建时可以设置分页条件,
  7. // 最终使用该对象来接收分页查询后得到的结果
  8. Page<User> userPage = new Page<>(2,3);
  9. //调用分页查询方法
  10. userMapper.selectPage(userPage,null);
  11. System.out.println(userPage.getTotal());//获取总记录数
  12. System.out.println(userPage.getCurrent());//获取当前页码
  13. System.out.println(userPage.getSize());//获取每页记录数
  14. List<User> records = userPage.getRecords();//获取分页查询后的数据
  15. records.forEach(System.out::println);
  16. }

条件构造器

AbstractWrapper

说明:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
**

API 用法 举例1 举例2
allEq 全部eq(或个别isNull) allEq({id:1,name:"老王",age:null})—->id = 1 and name = '老王' and age is null allEq({id:1,name:"老王",age:null}, false)—->id = 1 and name = '老王'
eq 等于 = eq("name", "老王")—->name = '老王'
ne 不等于 <> ne("name", "老王")—->name <> '老王'
gt 大于 > gt("age", 18)—->age > 18
lt 小于 < lt("age", 18)—->age < 18
le 小于等于 <= le("age", 18)—->age <= 18
between BETWEEN 值1 AND 值2 between("age", 18, 30)—->age between 18 and 30
notBetWeen NOT BETWEEN 值1 AND 值2 notBetween("age", 18, 30)—->age not between 18 and 30
like LIKE ‘%值%’ like("name", "王")—->name like '%王%'
notLike NOT LIKE ‘%值%’ notLike("name", "王")—->name not like '%王%'
likeLeft LIKE ‘%值’ likeLeft("name", "王")—->name like '%王'
likeRight LIKE ‘值%’ likeRight("name", "王")—->name like '王%'
isNull 字段 IS NULL isNull("name")—->name is null
isNotNull 字段 IS NOT NULL isNotNull("name")—->name is not null
in 字段 IN (value.get(0), value.get(1), …) in("age",{1,2,3})—->age in (1,2,3)
in 字段 IN (v0, v1, …) in("age", 1, 2, 3)—->age in (1,2,3)
notIn 字段 NOT IN (value.get(0), value.get(1), …) notIn("age",{1,2,3})—->age not in (1,2,3)
notIn 字段 NOT IN (v0, v1, …) notIn("age", 1, 2, 3)—->age not in (1,2,3)
inSql 字段 IN ( sql语句 ) inSql("age", "1,2,3,4,5,6")—->age in (1,2,3,4,5,6) inSql("id", "select id from table where id < 3")—->id in (select id from table where id < 3)
notInSql 字段 NOT IN ( sql语句 ) notInSql("age", "1,2,3,4,5,6")—->age not in (1,2,3,4,5,6) notInSql("id", "select id from table where id < 3")—->id not in (select id from table where id < 3)
groupBy 分组:GROUP BY 字段, … groupBy("id", "name")—->group by id,name
orderByAsc 排序:ORDER BY 字段, … ASC orderByAsc("id", "name")—->order by id ASC,name ASC
orderByDesc 排序:ORDER BY 字段, … DESC orderByDesc("id", "name")—->order by id DESC,name DESC
orderBy 排序:ORDER BY 字段, … orderBy(true, true, "id", "name")—->order by id ASC,name ASC
having HAVING ( sql语句 ) having("sum(age) > 10")—->having sum(age) > 10 having("sum(age) > {0}", 11)—->having sum(age) > 11
func func 方法(主要方便在出现if…else下调用不同方法能不断链 func(i -> if(true) {i.eq(“id”, 1)} else {i.ne(“id”, 1)})
or 拼接 OR eq("id",1).or().eq("name","老王")—->id = 1 or name = '老王' 注意事项:
主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)
or OR 嵌套 or(i -> i.eq("name", "李白").ne("status", "活着"))—->or (name = '李白' and status <> '活着')
and AND 嵌套 and(i -> i.eq("name", "李白").ne("status", "活着"))—->and (name = '李白' and status <> '活着')
nested 正常嵌套 不带 AND 或者 OR nested(i -> i.eq("name", "李白").ne("status", "活着"))—->(name = '李白' and status <> '活着')
apply 拼接 sql apply("id = 1")—->id = 1 apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")—->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
last 无视优化规则直接拼接到 sql 的最后 last(“limit 1”) 注意事项:
只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
exists 拼接 EXISTS ( sql语句 ) exists("select id from table where age = 1")—->exists (select id from table where age = 1)
notExists 拼接 NOT EXISTS ( sql语句 ) notExists("select id from table where age = 1")—->not exists (select id from table where age = 1)

QueryWrapper

说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

API 用法 案例
select 以上方法分为两类.
第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准

- 例: select("id", "name", "age")
- 例: select(i -> i.getProperty().startsWith("test"))

UpdateWrapper

说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

API 用法 案例
set SQL SET 字段
- 例: set("name", "老李头")
- 例: set("name", "")—->数据库字段值变为空字符串
- 例: set("name", null)—->数据库字段值变为null
setSql 设置 SET 部分 SQL
- 例: setSql("name = '老李头'")
lambda 获取 LambdaWrapper QueryWrapper中是获取LambdaQueryWrapper
UpdateWrapper中是获取LambdaUpdateWrapper

链式调用 lambda 式

  1. // 区分:
  2. // 链式调用 普通
  3. UpdateChainWrapper update();
  4. // 链式调用 lambda 式。注意:不支持 Kotlin
  5. LambdaUpdateChainWrapper lambdaUpdate();
  6. // 等价示例:
  7. query().eq("id", value).one();
  8. lambdaQuery().eq(Entity::getId, value).one();
  9. // 等价示例:
  10. update().eq("id", value).remove();
  11. lambdaUpdate().eq(Entity::getId, value).remove();