MyBatisPlius
0、创建表
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');
1、导入依赖
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version> <!-- 使用适当的版本号 -->
</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.5.1</version>
</dependency>
- 我们使用
mybaits-plus
可以节省我们大量的代码,尽量不要同时导入mybatis
和mybaits-plus
2、连接数据库
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=PRC&characterEncoding=UTF8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3、做一个小测试
pojo
:com.yap.mybatis_plus.pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
mapper
:com.yap.mybatis_plus.mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yap.mybatis_plus.pojo.User;
import org.springframework.stereotype.Repository;
/**
* @author yap
* @version 1.0
*/
//在对应的mapper 上面继承基本的类 BaseMapper
@Repository//代表持久层
public interface UserMapper extends BaseMapper<User> {
}
启动类
:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//扫描mapper 文件夹
@MapperScan("com.yap.mybatis_plus.mapper")
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
4、测试类中
- 引入
//继承了BaseMapper,所以的方法都来自父类
//我们也可以编写自己的扩展方法
@Autowired
private UserMapper userMapper;
@Test
void selectList() {
//参数是一个Wrapper,条件构造器,
//查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
- 输出结果
User(id=1, name=Jone, age=18, email=<test1@baomidou.com>)
User(id=2, name=Jack, age=20, email=<test2@baomidou.com>)
User(id=3, name=Tom, age=28, email=<test3@baomidou.com>)
User(id=4, name=Sandy, age=21, email=<test4@baomidou.com>)
User(id=5, name=Billie, age=24, email=<test5@baomidou.com>)
5、配置日志
- 现在是看不到sql的,配置控制台输出日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
- 输出结果
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3289079a] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@324701295 wrapping com.mysql.cj.jdbc.ConnectionImpl@6419a0e1] will not be managed by Spring
==> Preparing: SELECT id,name,age,email FROM user
==> Parameters:
<== Columns: id, name, age, email
<== Row: 1, Jone, 18, test1@baomidou.com
<== Row: 2, Jack, 20, test2@baomidou.com
<== Row: 3, Tom, 28, test3@baomidou.com
<== Row: 4, Sandy, 21, test4@baomidou.com
<== Row: 5, Billie, 24, test5@baomidou.com
<== Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3289079a]
6、CRUD拓展
6.1 测试插入(insert)
- insert:使用的是雪花算法生成唯一Id
- 雪花算法:snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是: 使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID ),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个ID),最后还有一个符号位,永远是0。可以保证全球唯一!
- 主键自增:
- 1、 :实体类字段上
@TableId(type = IdType.ID_WORKER)
//唯一- AUTO:数据库id自增
- NONE:未设置主键
- INPUT:手动输入
- ID_WORKER:默认的全局唯一ID
- UUID:全局唯一id uuid
- ID_WORKER_STR:ID_WORKER的字符串表示法
- 2、 :数据库字段一定要是自增否则会报错!
- 1、 :实体类字段上
@Test
void insert() {
User user = new User();
user.setName("abc");
user.setAge(3);
user.setEmail("456");
int result = userMapper.insert(user);//自动生成id
System.out.println(result);
System.out.println(user);
}
6.2 测试更新(update)
- updateById:参数是一个对象
@Test
void updateById() {
User user = new User();
//通过条件自动拼接sql
user.setId(1667764916303597570L);
user.setName("123");
user.setAge(1);
user.setEmail("456");
//注意updateById的参数是个对象
int result = userMapper.updateById(user);
System.out.println(result);
System.out.println(user);
}
6.3 自动填充
- 创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!
- 阿里巴巴开发手册:所有数据库表:创建时间(
gmt_create
)、修改时间:(gmt_modified
)几乎所有表都要配置上!而且需要自动化!
方式:代码级别
- 1:在表中新建字段 create_time,update_time
- 2:同步实体类User
//字段添加填充内容
@TableField(fill = FieldFill.INSERT)//代表添加的时候执行
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)//代表添加修改的时候执行
private Date updateTime;
- 3:创建com.yap.mybatis_plus.handler.MyMetaObjectHandler
- 自定义实现类
package com.yap.mybatis_plus.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component//不要忘记把处理器加到ioc容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
//插入的时候添加和修改都会存入时间
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
//更新的时候会修改时间
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
6.4 乐观锁
乐观锁:顾名思义十分乐观,它总会认为不会出现问题,无论干什么都不上锁!如果出现问题,再次更新值测试
悲观锁:顾名思义十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!
- 乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
乐观锁:1、先查询 获得版本号 version = 1
-- A
update user set name = "abc",version = version + 1
where id = 2 and version = 1
-- B 线程抢先完成,这个时候version = 2,会导致A修改失败!
update user set name = "abc",version = version + 1
where id = 2 and version = 1
测试一下MP乐观锁插件
- 1、:给数据库增加
version
字段,默认值为1
,int
类型,长度10
- 2、:实体类增加字段
@Version//乐观锁version注解
private Integer version;
-
说明:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
newVersion
会回写到entity
中- 仅支持
updateById(id)
与update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
-
3、:配置类com.yap.mybatis_plus.Config
package com.yap.mybatis_plus.Config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// Spring Boot 方式
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
- 4、:测试一下!
//测试乐观锁成功
@Test
public void testVersion() {
//1、查询用户信息
User user = userMapper.selectById(1L);
//2、修改用户信息
user.setName("abc");
user.setEmail("2314526");
//3、执行更新操作
int i = userMapper.updateById(user);
System.out.println(i);
}
//测试乐观锁失败
//开启version最后数据库显示的结果是abc222
//未开启version最后数据库显示的结果是abc111
@Test
public void testVersion2() {
//线程1
User user = userMapper.selectById(1L);
user.setName("abc111");
user.setEmail("2314526");
//模拟另外一个线程执行了插队操作
User user2 = userMapper.selectById(1L);
user2.setName("abc222");
user2.setEmail("2314526");
userMapper.updateById(user2);
//自旋锁来多次尝试提交
userMapper.updateById(user);//如果没有乐观锁就会覆盖插队线程的值
}
6.5 普通查询
selectById
:通过id查询数据selectBatchIds
:通过集合查询数据selectByMap
:通过条件查询数据
@Test
public void selectById() {
//通过id 查询一条数据
User user = userMapper.selectById(1);
System.out.println(user);
//SELECT id,name,age,email,version,create_time,update_time FROM user WHERE id=?
}
@Test
public void selectBatchIds() {
//查询多条数据
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
//SELECT id,name,age,email,version,create_time,update_time FROM user WHERE id IN ( ? , ? , ? )
}
@Test
public void selectByMap() {
//条件查询
HashMap<String, Object> map = new HashMap<>();
map.put("name","abc");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
//SELECT id,name,age,email,version,create_time,update_time FROM user WHERE name = ?
}
6.6 分页查询
- 简单的单表分页查询
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
@Test
public void selectPage() {
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
//SELECT id,name,age,email,version,create_time,update_time FROM user LIMIT ?,?
}
6.7 普通删除
deleteById
:通过id删除deleteBatchIds
:删除多条数据deleteByMap
:条件删除数据
@Test
public void deleteById() {
//删除一条数据
userMapper.deleteById(1);
//DELETE FROM user WHERE id=?
}
@Test
public void deleteBatchIds() {
//删除多条数据
userMapper.deleteBatchIds(Arrays.asList(4, 2, 3));
//DELETE FROM user WHERE id IN ( ? , ? , ? )
}
@Test
public void deleteByMap() {
//条件删除
HashMap<String, Object> map = new HashMap<>();
map.put("id", "1667848352129540097");
userMapper.deleteByMap(map);
//DELETE FROM user WHERE id = ?
}
6.8 逻辑删除
- 配置之后查询的时候也会默认查询为0的
- 数据库增加字段:
deleted
,int
类型,长度1
,默认0
- 实体类增加属性
@TableLogic
private Integer deleted;
#配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-field=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
- 测试
@Test
public void deleteById() {
//删除一条数据
userMapper.deleteById(5);
}
6.9 条件构造器
- allEq:
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
- ge:
ge("age", 18)
--->age >= 18
- lt:
lt("age", 18)
--->age < 18
- le:
le("age", 18)
--->age <= 18
- between:
between("age", 18, 30)
--->age between 18 and 30
- notBetween:
notBetween("age", 18, 30)
--->age not between 18 and 30
- like:
like("name", "王")
--->name like '%王%'
- notLike:
notLike("name", "王")
--->name not like '%王%'
- likeLeft:
likeLeft("name", "王")
--->name like '%王'
- likeRight:
likeRight("name", "王")
--->name like '王%'
- notLikeLeft:
notLikeLeft("name", "王")
--->name not like '%王'
- notLikeRight:
notLikeRight("name", "王")
--->name not like '王%'
- isNull:
isNull("name")
--->name is null
- isNotNull:
isNotNull("name")
--->name is not null
- in:
in("age", 1, 2, 3)
--->age in (1,2,3)
- notIn:
notIn("age", 1, 2, 3)
--->age not in (1,2,3)
- inSql:
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:
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:
groupBy("id", "name")
--->group by id,name
- orderByAsc:
orderByAsc("id", "name")
--->order by id ASC,name ASC
- orderByDesc:
orderByDesc("id", "name")
--->order by id DESC,name DESC
- orderBy:
orderBy(true, true, "id", "name")
--->order by id ASC,name ASC
- having:
having("sum(age) > 10")
--->having sum(age) > 10
having("sum(age) > {0}", 11)
--->having sum(age) > 11
- func:
func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
- or:
eq("id",1).or().eq("name","老王")
--->id = 1 or name = '老王'
or(i -> i.eq("name", "李白").ne("status", "活着"))
--->or (name = '李白' and status <> '活着')
- and:
and(i -> i.eq("name", "李白").ne("status", "活着"))
--->and (name = '李白' and status <> '活着')
- nested:
nested(i -> i.eq("name", "李白").ne("status", "活着"))
--->(name = '李白' and status <> '活着')
- apply:
apply("id = 1")
--->id = 1
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")
--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
- last:
last("limit 1")
- exists:
exists("select id from table where age = 1")
--->exists (select id from table where age = 1)
- notExists:
notExists("select id from table where age = 1")
--->not exists (select id from table where age = 1)
7、代码生成器
- 用的依赖版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!-- MyBatis-Plus 代码生成器依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
- 基础版本
package com.yap.mybatis_plus;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class CodeGenerator {
public static void main(String[] args) {
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://"); // 数据库连接URL
dataSourceConfig.setUsername("root"); // 数据库用户名
dataSourceConfig.setPassword("r"); // 数据库密码
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); // 数据库驱动类名
// 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir") + "/mybatis_plus/src/main/java"); // 代码输出路径
globalConfig.setAuthor("yap"); // 作者
globalConfig.setOpen(false); // 生成后是否打开输出目录
globalConfig.setFileOverride(true); // 是否覆盖已有文件
globalConfig.setServiceName("%sService"); // Service接口命名方式
globalConfig.setIdType(IdType.AUTO); // 主键生成策略
globalConfig.setDateType(DateType.ONLY_DATE); // 日期类型
// 包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.yap"); // 父包名
packageConfig.setModuleName("users"); // 模块名
packageConfig.setEntity("pojo"); // 实体类包名
packageConfig.setMapper("mapper"); // Mapper接口包名
packageConfig.setService("service"); // Service接口包名
packageConfig.setServiceImpl("service.impl"); // Service实现类包名
packageConfig.setController("controller"); // Controller包名
packageConfig.setXml("mapper.xml"); // XML文件包名
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("users"); // 需要生成代码的表名
strategyConfig.setEntityLombokModel(true); // 使用Lombok注解
strategyConfig.setNaming(NamingStrategy.underline_to_camel); // 数据库表名映射到实体类的命名策略
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); // 数据库字段名映射到实体类属性名的命名策略
// 代码生成器配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setDataSource(dataSourceConfig);
autoGenerator.setGlobalConfig(globalConfig);
autoGenerator.setPackageInfo(packageConfig);
autoGenerator.setStrategy(strategyConfig);
// 执行生成代码
autoGenerator.execute();
}
}