注:当前没有使用spring-boot
使用mybatis-plus一般用于做单表的增删改查,如果希望去做多表的连查,也可以自定义方法,自定义SQL去执行。[MyBatis-Plus (opens new window)](https://github.com/baomidou/mybatis-plus)(简称 MP)是一个 [MyBatis (opens new window)](http://www.mybatis.org/mybatis-3/)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。对于单表的增删改查,mybatis-plus封装了对应的方法,直接调用即可实现对应的功能,无需编写SQL语句。
入门案例
1、导入依赖
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.8.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.8.RELEASE</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.22</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.43</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>3.0.5</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
2、创建mapper层接口,接口需要继承BaseMapper接口,BaseMapper接口的泛型指定了当前mapper处理的数据类型
public interface UserMapper extends BaseMapper<User> {}
3、编写实体类User
@Data@TableName("t_user")//如果表名与类名不一致,使用@TableName指定当前实体类映射的数据库表名public class User {@TableId(type = IdType.AUTO)//指定主键生成策略private Long id;private String name;private Integer age;private String email;@TableField(fill = FieldFill.INSERT)//指定自动填充策略private Date gmtCreate;@TableField(fill = FieldFill.INSERT_UPDATE)private Date gmtModified;}
4、mybatisplus通过MetaObjectHandler来处理自动填充,需要实现MetaObjectHandler接口
public class AutoFillUtil implements MetaObjectHandler {/*** 新增时自动填充* @param metaObject*/@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("gmtCreate",new Date(),metaObject);this.setFieldValByName("gmtModified",new Date(),metaObject);}/*** 修改时自动填充* @param metaObject*/@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("gmtModified",new Date(),metaObject);}}
5、编写spring的配置文件
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.woniuxy"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///mybatisplus?characterEncoding=utf-8&useSSL=false"/><property name="username" value="root"/><property name="password" value="root"/></bean><!-- 配置mybatisplus的sqlsessionfactorybean --><bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><!--指定mybatisplus的全局配置--><property name="globalConfig" ref="globalConfig"/></bean><!--进行mybatisplus全局配置--><bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig"><!--配置metaObjectHandler,用于实现自动填充--><property name="metaObjectHandler"><bean class="com.woniuxy.utils.AutoFillUtil"/></property></bean><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.woniuxy.mapper"/></bean></beans>
6、测试
public class MybatisPlusTest {private UserMapper userMapper;@Beforepublic void init(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");userMapper=applicationContext.getBean("userMapper",UserMapper.class);}@Testpublic void test(){List<User> users = userMapper.selectList(null);System.out.println(users);}@Testpublic void testAdd(){User entity = new User();entity.setName("王五");entity.setAge(35);entity.setEmail("3@3.3");System.out.println(entity);userMapper.insert(entity);System.out.println(entity);}}
乐观锁
乐观锁 : 对一切操作保持乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,
再次更新值测试 。
悲观锁:对一切操作保持悲观,它总是认为会出现问题,无论干什么都会上锁!再去操作!乐观锁实现方式:
1.数据库表中应该有version字段,用于记录当前数据的版本
2.从数据库中取出记录时,获取当前 version
3.更新时,带上获得的version
4.执行更新时, set version = newVersion where version = oldVersion ,如果version不对,就更新失败。
实现步骤:
1、修改数据库表,增加version字段
2、修改实体类,增加version属性,且在属性上加@Version注解
3、修改spring配置文件,在MybatisSqlSessionFactoryBean的plugins中增加加上乐观锁插件。
<!-- 配置mybatisplus的sqlsessionfactorybean --><bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><!--指定mybatisplus的全局配置--><property name="globalConfig" ref="globalConfig"/><!--配置乐观锁插件--><property name="plugins"><array><bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/></array></property><!-- 配置mybatis的日志记录,输出在控制台 --><property name="configuration"><bean class="com.baomidou.mybatisplus.core.MybatisConfiguration"><property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/></bean></property></bean>
3、测试
/*** 测试乐观锁*/@Testpublic void testUpdate(){//线程1:想把ID为6的人改成张三丰User user1 = userMapper.selectById(6L);//根据id查询记录user1.setName("张三丰");//线程2插队了:把ID为6的人改成张无忌User user2=userMapper.selectById(6L);user2.setName("张无忌");//线程2插队成功,执行了修改userMapper.updateById(user2);//根据ID修改数据库记录。userMapper.updateById(user1);//线程1开始执行修改}
逻辑删除
逻辑删除:如果配置了逻辑删除,则此时的删除操作都不会真正去执行delete语句,而是执行update。注意:逻辑删除操作在mybatisplus高版本中,已被移除。
实现步骤
1、数据库表中添加字段deleted 默认值给个0,表示未删除状态,
2、在实体类中添加属性deleted,属性上加@TableLogic注解,表示启用逻辑删除
3、修改spring配置文件,在globalConfig配置sqlInjector,值使用LogicSqlInjector
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig"><!--配置metaObjectHandler,用于实现自动填充--><property name="metaObjectHandler"><bean class="com.woniuxy.utils.AutoFillUtil"/></property><!--配置逻辑删除--><property name="sqlInjector"><bean class="com.baomidou.mybatisplus.extension.injector.LogicSqlInjector"/></property></bean>
4、测试
/*** 测试逻辑删除*/@Testpublic void testLogicDelete(){userMapper.deleteById(1402517259338924034L);}
分页插件
配置分页插件:mybatis-plus自带分页插件,不需要单独导入外部依赖。
实现步骤
1、配置分页插件
<!-- 配置mybatisplus的sqlsessionfactorybean --><bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><!--指定mybatisplus的全局配置--><property name="globalConfig" ref="globalConfig"/><!--配置插件--><property name="plugins"><array><!--分页插件--><bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/><!--乐观锁插件--><bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/></array></property><!-- 配置mybatis在控制台输出SQL --><property name="configuration"><bean class="com.baomidou.mybatisplus.core.MybatisConfiguration"><property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/></bean></property></bean>
2、创建page对象,用于封装分页查询后得到的结果,并设置分页查询的条件,调用selectPage方法完成分页查询
/*** 测试分页查询*/@Testpublic void testPageQuery(){//创建page对象,该对象创建时可以设置分页条件,// 最终使用该对象来接收分页查询后得到的结果Page<User> userPage = new Page<>(2,3);//调用分页查询方法userMapper.selectPage(userPage,null);System.out.println(userPage.getTotal());//获取总记录数System.out.println(userPage.getCurrent());//获取当前页码System.out.println(userPage.getSize());//获取每页记录数List<User> records = userPage.getRecords();//获取分页查询后的数据records.forEach(System.out::println);}
条件构造器
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 式
// 区分:// 链式调用 普通UpdateChainWrapper update();// 链式调用 lambda 式。注意:不支持 KotlinLambdaUpdateChainWrapper lambdaUpdate();// 等价示例:query().eq("id", value).one();lambdaQuery().eq(Entity::getId, value).one();// 等价示例:update().eq("id", value).remove();lambdaUpdate().eq(Entity::getId, value).remove();
