数据库使用的是Oracle,非Mysql
Mybatis-Plus简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
Mybatis-Plus特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
Mybatis-Plus快速入门
- 创建数据库表并往表里插入数据
mysql:
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');
oracle:
--创建user表
CREATE TABLE "user"
(
"id" INT NOT NULL,
"name" VARCHAR2(30) DEFAULT 'user_name' NOT NULL,
age INT DEFAULT 0 NOT NULL,
email VARCHAR2(50) DEFAULT NULL,
PRIMARY KEY ("id")
);
--给表的每个字段添加注释
comment on column "user"."id" is '主键id';
comment on column "user"."name" is '姓名';
comment on column "user".age is '年龄';
comment on column "user".email is '邮箱';
select * from user_col_comments where TABLE_NAME='user';
--删除表记录
DELETE FROM "user";
--往user表添加数据
INSERT ALL
INTO "user" ("id","name", age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com')
INTO "user" ("id","name", age, email) VALUES
(2, 'Jack', 20, 'test2@baomidou.com')
INTO "user" ("id","name", age, email) VALUES
(3, 'Tom', 28, 'test3@baomidou.com')
INTO "user" ("id","name", age, email) VALUES
(4, 'Sandy', 21, 'test4@baomidou.com')
INTO "user" ("id","name", age, email) VALUES
(5, 'Billie', 24, 'test5@baomidou.com')
SELECT 1 FROM DUAL;
--提交
COMMIT;
- 创建springboot工程,导入mybatis-plus依赖,尽量不要与mybatis依赖一起导入
<!--mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
- 配置yaml配置文件
spring:
# oracle配置
datasource:
username: MYBATIS-PLUS
password: Wzl7373520
url: jdbc:oracle:thin:@127.0.0.1:1521:ORCL
driver-class-name: oracle.jdbc.driver.OracleDriver
- 创建一个user实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
- 创建一个Mapper接口类
@Repository
public interface UserMapper extends BaseMapper<User> {
}
- 在springboot启动类中添加@MapperScan注解,扫描Mapper文件夹
@SpringBootApplication
@MapperScan("com.wuzenglin.mapper")
public class SpringbootMybatisplusApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisplusApplication.class, args);
}
}
- 测试
@SpringBootTest
class SpringbootMybatisplusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
测试失败,原因在于id,name,user在Oracle数据库中为关键字,我们需要去实体类对其添加注解,添加完注解再次测试既能成功。
### SQL: SELECT id,name,age,email FROM user
### Cause: java.sql.SQLSyntaxErrorException: ORA-00903: 表名无效
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("\"user\"")
public class User {
@TableId("\"id\"")
private int id;
@TableField("\"name\"")
private String name;
private Integer age;
private String email;
}
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)
配置日志
# 配置mybatis-plus日志,可以看到执行的查询语句
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Preparing: SELECT "id","name",age,email FROM "user"
测试添加数据
//测试添加数据
@Test
void insertDemo(){
User user=new User();
user.setId(110);
user.setName("吴木木");
user.setAge(21);
user.setEmail("2289707466@qq.com");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println(user);
}
这里的主键id是自己设置的,若想采用自增主键id或全球唯一id,可以使用 @TableId注解来实现,该注解提供了以下参数:
AUTO(0) 数据库id自增长
NONE(1) 未设置主键
INPUT(2) 手动输入
ASSIGN_ID(3) 全局唯一id
ASSIGN_UUID(4) 全局唯一id
但因为这里使用的是Oracle数据库,所以需要进行相应的配置,实现自增长主键参考另一篇博客:https://blog.csdn.net/weixin_49861429/article/details/108496686
测试更新操作
//测试更新数据
@Test
void updateDemo() {
User user = new User();
user.setId("1303872814919802882");
user.setName("测试修改木木");
userMapper.updateById(user);
}
自动填充
在Oracle的数据库表中新增两个字段
create_time date DEFAULT NULL,
update_time date DEFAULT NULL,
使用注解填充字段
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
编写一个处理器类
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
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);
}
}
乐观锁
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
乐观锁的配置只需三步
在Oracle数据库中新增version字段:
"version" int DEFAULT 1
配置类配置
//乐观锁
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
注解实体类字段
@Version
@TableField("\"version\"")
private int version;
对乐观锁实现的成功与失败进行测试
//测试乐观锁成功案例
@Test
void hSuccessDemo() {
User user = userMapper.selectById(1);
user.setName("木木");
user.setEmail("2289707466@qq.com");
System.out.println(user);
userMapper.updateById(user);
}
//测试乐观锁失败案例
@Test
void hFailureDemo() {
User user1 = userMapper.selectById(2);
user1.setName("木木一号");
user1.setEmail("2289707466@qq.com");
User user2 = userMapper.selectById(2);
user2.setName("木木二号");
user2.setEmail("2289707466@qq.com");
userMapper.updateById(user2);
userMapper.updateById(user1);
}
从以下两行运行结果来看,可以看到在木木二号修改成功后version就加一了,那么木木一号在去对条件version为1时进行修改就会失败
==> Preparing: UPDATE "user" SET "name"=?, age=?, email=?, update_time=?, "version"=? WHERE "id"=? AND "version"=?
==> Parameters: 木木二号(String), 20(Integer), 2289707466@qq.com(String), 2020-09-10 11:54:44.959(Timestamp), 2(Integer), 2(String), 1(Integer)
<== Updates: 1
==> Preparing: UPDATE "user" SET "name"=?, age=?, email=?, update_time=?, "version"=? WHERE "id"=? AND "version"=?
==> Parameters: 木木一号(String), 20(Integer), 2289707466@qq.com(String), 2020-09-10 11:54:44.968(Timestamp), 2(Integer), 2(String), 1(Integer)
<== Updates: 0
查询测试
//简单单条查询
@Test
void queryOneDemo(){
User user=userMapper.selectById(1);
System.out.println(user);
}
//多个id查询
@Test
void queryListDemo(){
List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
userList.forEach(System.out::println);
}
//条件查询
@Test
void queryByConditionDemo(){
HashMap<String, Object> map = new HashMap<>();
map.put("\"name\"","木木");
map.put("age",18);
List<User> userList = userMapper.selectByMap(map);
userList.forEach(System.out::println);
}
分页查询
分页插件
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
测试
//分页查询
@Test
void limitQueryDemo(){
/**
* 参数一:当前页码
* 参数二:当前页面数据条数
*/
Page<User> page = new Page<>(1,2);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
}
删除操作
//删除记录测试
@Test
void delGatherDemo(){
//根据id删除
userMapper.deleteById("ba9b69f506a0bfba663e0e51d6debab6");
//根据id批量删除
userMapper.deleteBatchIds(Arrays.asList("9addd99654f0340ffee1b8ad4db33b83","26c71d7e9ff567de262868c1251f9999"));
//根据条件进行删除
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("\"name\"","吴木木");
hashMap.put("age",21);
userMapper.deleteByMap(hashMap);
}
逻辑删除
往Oracle数据库表中增加字段
deleted int default 0,
在yaml上配置
mybatis-plus:
#日志,可以看到执行的查询语句
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#逻辑删除配置
global-config:
db-config:
logic-delete-field: deleted #逻辑删除的实体类字段名,配置该条实体字段不用加上注解
logic-delete-value: 1 #逻辑删除的已删除值
logic-not-delete-value: 0 #逻辑删除的已删除值
在实体类上添加字段
@TableLogic //若在yaml配置文件中未配置,需添加该注解
private int deleted;
性能分析插件
导入p6spy依赖
<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
yaml配置
spring:
# oracle配置
datasource:
username: MYBATIS-PLUS
password: Wzl7373520
url: jdbc:p6spy:oracle:thin:@127.0.0.1:1521:ORCL
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
spy.properties 配置:
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
# 自定义日志打印格式
customLogMessageFormat=%(currentTime) | SQL耗时: %(executionTime) ms | 连接信息: %(category)-%(connectionId) | 执行语句: %(sql)
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=1
测试结果:
2020-09-10 17:54:59 | SQL耗时: 35 ms | 连接信息: statement-0 | 执行语句: SELECT "id","name",age,email,create_time,update_time,"version",deleted FROM "user" WHERE "name" = '木木'
AND age = 18
AND deleted=0
代码生成器
依赖
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
<!--添加代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
生成器
/**
* 代码生成器
*/
public class CodeGenerator {
public static void main(String[] args) {
//代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
//获取用户的目录
String projectPath = System.getProperty("user.dir");
//输出目录
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("木木");//作者
gc.setOpen(false);//是否打开资源管理器
gc.setFileOverride(true);//是否覆盖原来生成的
gc.setIdType(IdType.INPUT);//设置id生成策略
gc.setDateType(DateType.ONLY_DATE);//设置日期的类型
gc.setSwagger2(true); //实体属性 Swagger2 注解
gc.setServiceName("%sService");//去掉默认的I前缀
mpg.setGlobalConfig(gc);//将全局配置放入代码生成器中
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:p6spy:oracle:thin:@127.0.0.1:1521:ORCL");
dsc.setDriverName("com.p6spy.engine.spy.P6SpyDriver");
dsc.setUsername("MYBATIS-PLUS");
dsc.setPassword("Wzl7373520");
dsc.setSchemaName("MYBATIS-PLUS");
dsc.setDbType(DbType.ORACLE);//数据库类型
dsc.setTypeConvert(new OracleTypeConvert(){
@Override
public IColumnType processTypeConvert(GlobalConfig config, String fieldType) {
return super.processTypeConvert(config, fieldType);
}
});
mpg.setDataSource(dsc);//将数据源配置放入代码生成器
// 包配置
PackageConfig pc = new PackageConfig();
/**
* 对目录名进行设置,若不设置,则采用默认
*/
pc.setModuleName("generatedCode");
pc.setParent("com.wuzneglin");
pc.setEntity("pojo");
pc.setController("controller");
pc.setMapper("mapper");
pc.setService("service");
// pc.setServiceImpl("serviceImpl");
mpg.setPackageInfo(pc);//将包配置放入代码生成器中
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("wuzenglin");//数据库映射的表
strategy.setNaming(NamingStrategy.underline_to_camel);//表名生成策略,下划线生成驼峰命名
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//字段名生成策略,下划线生成驼峰命名
strategy.setCapitalMode(false); // 全局大写命名 ORACLE 注意
//自定义实现父类
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);//自动使用lombok插件
strategy.setRestControllerStyle(true);//生成@RestController控制器
strategy.setControllerMappingHyphenStyle(true);//生成请求url
//逻辑删除配置
strategy.setLogicDeleteFieldName("deleted");
//自动填充配置
TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(createTime);
tableFills.add(updateTime);
strategy.setTableFillList(tableFills);
//乐观锁配置
strategy.setVersionFieldName("version");
mpg.setStrategy(strategy);//将策略配置放入全局配置
mpg.execute();//执行
}
}