SpringBoot&MyBatisPlus
5. SpringBoot
- 学习目标:
- 掌握基于SpringBoot框架的程序开发步骤
- 熟练使用SpringBoot配置信息修改服务器配置
- 基于SpringBoot完成SSM整合项目开发
5.1 入门案例
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
-
原生开发SpringSSM程序过程
- 创建工程,在pom中配对应的依赖
- 添加Spring、MyBatis、SpringMVC的相关配置类
- 添加Controller类、Service类、Dao
-
实现步骤:
-
创建工程
-
编写controller
-
运行Application
-
-
最简SpringBoot程序所包含的基础文件
-
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version> 2.7.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.mark</groupId> <artifactId>SpringBoot_01_QuickStart</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
-
Application类
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
-
Spring程序与SpringBoot程序对比
-
基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架
5.2 SpringBoot工程官网创建方式
5.3 SpringBoot项目快速启动
-
对SpringBoot项目打包(执行Maven构建指令package)
-
执行启动指令
java -jar springboot.jar
注意事项:
jar支持命令行启动需要依赖maven插件支持,需要确认在打包时是否具有SpringBoot对应的maven插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.3</version>
</plugin>
</plugins>
</build>
5.4 SpringBoot概述
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
-
Spring程序缺点
- 配置繁琐
- 依赖设置繁琐
-
SpringBoot程序优点
- 自动配置
- 起步依赖(简化依赖配置)
- 辅助功能(内置服务器,...)
-
起步依赖帮助我们实现了快速配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.3</version> </parent> <groupId>com.mark</groupId> <artifactId>SpringBoot_01_QuickStart</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>11</java.version> </properties> <dependencies> <!--起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
- starter
- SpringBoot中常见项目名称,定义了当前项目使用的所有项目坐标,以达到减少依赖配置的目的
- partent
- 所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少冲突的目的
- spring-boot-starter-parent(2.5.0)与spring-boot-starter-parent(2.4.6)共计57处坐标版本不同
- starter
-
实际开发
- 使用任意坐标时,仅书写VGA中的G和A,V由SpringBoot提供
- 如发生坐标错误,再指定version(要小心版本冲突)
-
SpringBoot启动
-
启动方式
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
Spring在创建项目时,采用jar的打包方式
-
SpringBoot的引导类是项目的入口,运行main方法就可以启动
-
5.5 Spring辅助功能:切换web服务器
使用jetty替换tomcat:使用maven依赖管理变更起步依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Jetty比Tomcat更轻量级,可扩展性更强(相较于Tomcat),谷歌搜索引擎(GAE)已经全面切换为Jetty
5.6 配置文件格式
-
修改服务器端口
http://localhost:8080/books/1
修改为80端口号:
http://localjost/books/1
-
SpringBoot提供了多种属性配置方式
-
application.properties中修改
在resources下的application.properties添加
server.port=80
-
application.yml中修改
在resources下新建application.yml文件,添加
server:port:80
-
application.ymal中修改
在resources下新建application.yaml文件,添加
server:port:80
-
-
注意:
- 在之后主要在application.yml中修改配置
- 如果三个文件都存在,properties为第一配置文件,yml为第二配置文件,yaml为第三配置文件。即如果三个文件有相同的配置,优先级为properties->yml->yaml
5.7 yaml格式
-
YAML(YAML Ain't Markup Language):一种数据序列化格式
-
优点:
- 容易阅读
- 容易与脚本语言交互
- 以数据为核心,重数据轻格式
-
YAML文件扩展名
- .yml(主流)
- .yaml
-
yaml语法规则
-
大小写敏感
-
属性层级关系使用多行描述,每行结尾使用冒号结束
-
使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tb键)
-
属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
-
#表示注释
-
数组数据在书写位置的下方使用减号作为数据的开始,每行书写一个数据,减号与数据间空格分隔
-
-
yaml数据读取的方式
数据如下:
lesson: SpringBoot server: port: 80 person: name: mark age: 21 tel: 123456789 likes: - Java - Game - Music logging: level: root: info
-
方式一:使用@value注解读取单个数据。引用方式:${一级属性名.二级属性名}
@RestController @RequestMapping("/books") public class BookController { @Value("${lesson}") private String lesson; @Value("${server.port}") private Integer port; @Value("${person.likes[0]}") private String likes_00; @GetMapping("/{id}") public String getById(@PathVariable Integer id) { System.out.println(lesson); System.out.println(port); System.out.println(likes_00); return "hello Spring Boot"; } }
-
方式二:使用Environment对象加载所有环境信息并封装(一般在框架内部使用)
@RestController @RequestMapping("/books") public class BookController { //加载所有的环境信息 @Autowired private Environment environment; @GetMapping("/{id}") public String getById(@PathVariable Integer id) { System.out.println(environment.getProperty("lesson")); System.out.println(environment.getProperty("server.port")); System.out.println(environment.getProperty("person.likes[0]")); return "hello Spring Boot"; } }
-
方式三:自定义对象封装指定数据(常用)
-
定义实体类,添加注解
@Component
和@ConfigurationProperties
@Getter @Setter @ToString //1.将其定义成一个bean使之能受Spring控制 @Component //2.读取配置中的属性person @ConfigurationProperties(prefix = "person") public class Person { private String name; private Integer age; private String tel; private String[] likes; }
-
使用时自动装配即可
@RestController @RequestMapping("/books") public class BookController { @Autowired private Person person; @GetMapping("/{id}") public String getById(@PathVariable Integer id) { System.out.println(person); return "hello Spring Boot"; } }
-
自定义对象封装数据警告解决方案:添加以下依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
-
-
5.8 多环境开发配置
yaml、yml配置方式:
#设置启用的环境
spring:
profiles:
active: dev
---
#开发
spring:
profiles: dev
server:
port: 80
---
#生产
spring:
profiles: pro
server:
port: 81
---
#测试
spring:
profiles: test
server:
port: 82
上述是过时写法,以下为不过时写法
#设置启用的环境
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
server:
port: 82
properties配置方式:(了解即可)
在主启动配置文件application.properties中添加
#设置启用的环境
spring.profiles.active=dev
新建分类配置文件application-dev.properties添加环境配置
server.port=80
同理新建application-pro.properties、application-test.properties添加环境配置
server.port=81
server.port=82
-
多环境启动命令格式
-
因为有中文,所以先设置编码
-
在打包前首先执行clean,然后package打包。避免影响结果
-
cmd执行:带参数启动SpringBoot
java -jar SpringBoot_04_profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=test
即在测试环境下运行
-
使用临时参数启动SpringBoot
java -jar SpringBoot_04_profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=test --server.port=88
-
-
参数加载优先级:
- 参考https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
-
多环境开发兼容问题(Maven与Boot)
-
Maven中设置生产环境
<profiles> <profile> <id>dev</id> <properties> ⭐<profile.active>dev</profile.active> </properties> </profile> <profile> <id>pro</id> <properties> <profile.active>pro</profile.active> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>test</id> <properties> <profile.active>test</profile.active> </properties> </profile> </profiles>
-
Boot的yml中设置生产环境
#设置启用的环境 spring: profiles: active: ${profile.active} --- #开发 spring: profiles: dev server: port: 80 --- #生产 spring: profiles: pro server: port: 81 --- #测试 spring: profiles: test server: port: 82
-
打包运行发现端口号是8080
-
解决:
-
添加插件
<!--对资源文件进行解析--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <configuration> <encoding>UTF-8</encoding> <useDefaultDelimiters>true</useDefaultDelimiters> </configuration> </plugin>
-
-
5.9 配置文件分类
- SpringBoot中4级配置文件
- 1级:file: config/application.yml ----最高
- 2级:file: application.yml
- 3级:classpath: config./application.yml
- 4级:classpath: application.yml ----最低
- 作用:
- 1级与2级留做系统打包后设置通用属性(在资源管理器target目录下创建yml)
- 3级与4级用于系统开发阶段设置通用属性(在IDEA的resources包下创建)
5.10 SpringBoot整合junit
Spring整合Junit
//设置运行器
@RunWith(SpringJUnit4ClassRunner.class)
//加载环境
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void testGetById() {
Book book = bookService.getById(2);
System.out.println(book);
}
}
书写一个Service接口和实现类,直接在TestApplication中测试即可
@SpringBootTest
class SpringBoot07TestApplicationTests {
@Autowired
private BookService bookService;
@Test
void contextLoads() {
bookService.save();
}
}
5.11 SpringBoot整合SSM
-
SpringBoot整合Spring(不存在)
-
SpringBoot整合SpringMVC(不存在)
-
SpringBoot整合MyBatis(主要、唯一)
-
创建工程,在创建模块时添加MyBatis和MySQL
-
书写实体类(pojo)和dao接口
-
在dao接口上添加注解@Mapper
-
修改配置文件,设置数据源参数
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///ssm_db username: root password: 123
-
如果使用的SpringBoot版本低于2.4.3(不含),MySQL驱动版本大于8.0时,需要在url连接串中配置时区,或在MySQL数据库端配置时区解决此问题
url: jdbc:mysql:///ssm_db?serverTimezone=UTC
-
-
现在就可以运行啦
-
-
如何使用Druid数据库连接池?
-
添加druid依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
-
修改配置文件
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///ssm_db username: root password: 123 type: com.alibaba.druid.pool.DruidDataSource
-
5.12 基于SpringBoot的整合SSM案例
-
pom.xml
配置起步依赖,必要的资源坐标(druid)
-
application.yml
设置数据源、端口等
#TODO 配置数据源相关信息 server: port: 80 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db username: root password: 123
-
配置类
全部删除
-
dao
设置@Mapper
-
测试类
-
页面
-
将静态页面放在resources下的static目录下
-
可以在static下创建index.html主页,这样不用输入地址即可进入
<script> document.location.href="pages/books.html" </script>
-
6. MyBatisPlus
6.1 入门案例
MyBatisPlus(简称MP)是基于MyBatis框架基础上的增强型工具,旨在简化开发、提高效率
-
SpringBoot整合MyBatis开发过程
- 创建SpringBoot工程
- 勾选配置使用的技术
- 设置dataSource相关属性(JDBC参数)
- 定义数据层接口映射配置
-
基于SpringBoot使用MyBatisPlus
-
创建工程,勾选MySQL
-
手动添加MyBatisPlus、Druid依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.11</version> </dependency>
-
配置数据源
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db username: root password: 123
-
创建domain下的实体类User
@Getter @Setter @ToString public class User { private Long id; private String name; private String password; private Integer age; private String tel; }
-
创建dao下的接口UserDao继承BeanMapper,添加泛型为User
@Mapper public interface UserDao extends BaseMapper<User> { }
-
接下里就可以测试了
@SpringBootTest class MyBatisPlus01QuickStartApplicationTests { @Autowired private UserDao userDao; @Test void testGetAll() { List<User> list = userDao.selectList(null); System.out.println(list); } }
-
6.2 MyBatisPlus简介
-
MyBatisPlus(简称MP)是基于MyBatis框架基础上的增强型工具,旨在简化开发、提高效率
-
MyBatisPlus特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 强大的 CRUD 操作:内置通用 Mapper、少量配置即可实现单表大部分 CRUD 操作
- 支持 Lambda 形式调用:编写各类查询条件,无需再担心字段写错
- 支持主键自动生成
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
6.3 标准数据层CRUD功能
@SpringBootTest
class MyBatisPlus01QuickStartApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testSave(){
User user = new User();
user.setName("王昭君");
user.setPassword("123");
user.setAge(12);
user.setTel("123456888");
userDao.insert(user);
}
@Test
void testDelete(){
userDao.deleteById(1567537461138120705L);
}
@Test
void testUpdate(){
User user = new User();
user.setId(1L);
user.setName("Tom666");
//提供哪些字段,修改哪些字段
userDao.updateById(user);
}
@Test
void testById(){
User user = userDao.selectById(2L);
System.out.println(user);
}
@Test
void testGetAll() {
List<User> list = userDao.selectList(null);
System.out.println(list);
}
}
6.4 标准分页功能制作
@Test
void testGetByPage(){
//1:查第一页 2:一页有多少条数据
IPage page =new Page(1,2);
userDao.selectPage(page,null);
System.out.println("当前页码值:"+page.getCurrent());
System.out.println("当前页有:"+page.getSize()+"条");
System.out.println("一共有:"+page.getPages()+"页");
System.out.println("一共有:"+page.getTotal()+"条数据");
System.out.println("数据:"+page.getRecords());
//此时查数据发现查不出有多少页多少数据且数据为全部数据
//这就需要配置PM的分页拦截器
}
此时查数据发现查不出有多少页多少数据且数据为全部数据
这就需要配置PM的分页拦截器
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mPlusInterceptor(){
//1.定义MP的拦截器
MybatisPlusInterceptor mybatisPlusInterceptor =new MybatisPlusInterceptor();
//2.在MP的拦截器添加具体的拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
结果如下:
当前页码值:1
当前页有:2条
一共有:3页
一共有:5条数据
数据:[User(id=1, name=Tom666, password=tom, age=13, tel=18888888888), User(id=2, name=Jerry, password=jerry, age=12, tel=13999999999)]
可以在yml配置中开启日志
# 开启MP的日志(输出到控制台)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
6.5 条件查询
MP将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合
由于输出日志太多,因此可以配置一个logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
还可以清除MP和Spring的LOGO:在yml中配置banner
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db
username: root
password: 123
main:
banner-mode: off
mybatis-plus:
global-config:
banner: false
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
这样输出结果就会很清爽
-
方式一:
@Test void testGetAll() { //按条件查询方式一: QueryWrapper wrapper = new QueryWrapper(); //<:小于 >:大于 wrapper.lt("age",18); List<User> userList = userDao.selectList(wrapper); System.out.println(userList); }
-
方式二:
//按条件查询方式二: QueryWrapper<User> wrapper = new QueryWrapper<User>(); wrapper.lambda().lt(User::getAge,18); List<User> userList = userDao.selectList(wrapper); System.out.println(userList);
-
方式三⭐(推荐):
//按条件查询方式三: LambdaQueryWrapper<User> lambdaQueryWrapper =new LambdaQueryWrapper<User>(); lambdaQueryWrapper.lt(User::getAge,10); List<User> userList = userDao.selectList(lambdaQueryWrapper); System.out.println(userList);
//方式三添加多个条件 LambdaQueryWrapper<User> lambdaQueryWrapper =new LambdaQueryWrapper<User>(); //lambdaQueryWrapper.lt(User::getAge,18); //lambdaQueryWrapper.gt(User::getAge,12); //12到18之间 //lambdaQueryWrapper.lt(User::getAge,18).gt(User::getAge,12); //小于12或大于18 lambdaQueryWrapper.lt(User::getAge,12).or().gt(User::getAge,18); List<User> userList = userDao.selectList(lambdaQueryWrapper); System.out.println(userList);
6.6 条件查询—null值处理
//模拟页面传递过来的查询数据
UserQuery userQuery = new UserQuery();
//userQuery.setAge(10);
userQuery.setAge2(30);
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
//先判定第一个参数是否为true,如果为true,如果为true连接当前条件
lambdaQueryWrapper.lt(null != userQuery.getAge2(),User::getAge,userQuery.getAge2());
lambdaQueryWrapper.gt(null != userQuery.getAge(),User::getAge,userQuery.getAge());
List<User> userList = userDao.selectList(lambdaQueryWrapper);
System.out.println(userList);
6.7 查询投影(查询的字段控制)
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
//该格式只适用于lambda表达式
lambdaQueryWrapper.select(User::getId, User::getName,User::getAge);
List<User> userList = userDao.selectList(lambdaQueryWrapper);
System.out.println(userList);
/*结果:
Preparing: SELECT id,name,age FROM user
==> Parameters:
<== Columns: id, name, age
<== Row: 1, Tom666, 13
<== Row: 2, Jerry, 12
<== Row: 3, Mark, 21
<== Row: 4, 诸葛亮, 15
<== Row: 1567537209630830593, 王昭君, 12
<== Total: 5
*/
非lambda表达式写法:
QueryWrapper<User> lambdaQuery = new QueryWrapper<User>();
lambdaQuery.select("id","name","age");
List<User> userList = userDao.selectList(lambdaQuery);
System.out.println(userList);
查询总数
QueryWrapper<User> lambdaQuery = new QueryWrapper<User>();
lambdaQuery.select("count(*) as count");
List<Map<String, Object>> userMaps = userDao.selectMaps(lambdaQuery);
System.out.println(userMaps);
/*结果:
==> Preparing: SELECT count(*) FROM user
==> Parameters:
<== Columns: count(*)
<== Row: 5
<== Total: 1
*/
分组统计
QueryWrapper<User> lambdaQuery = new QueryWrapper<User>();
//根据电话分组,查询个数
lambdaQuery.select("count(*) as count,tel");
lambdaQuery.groupBy("tel");
List<Map<String, Object>> userMaps = userDao.selectMaps(lambdaQuery);
System.out.println(userMaps);
6.8 查询条件
-
范围匹配(>、=、between)
-
模糊匹配(like)
-
空判定(null)
-
包含性匹配(in)
-
分组(group)
-
排序(order)
用户登录
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>(); //等同于"="(equal) lambdaQuery.eq(User::getName, "Jerry").eq(User::getPassword, "jerry"); User user = userDao.selectOne(lambdaQuery); System.out.println(user);
购物设定价格区间、户籍设定年龄区间
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>(); //范围查询 lt le gt ge between //方式二 //lambdaQuery.le(User::getAge,30).ge(User::getAge,10); //方式二 lambdaQuery.between(User::getAge,10,30); List<User> list = userDao.selectList(lambdaQuery); System.out.println(list);
查信息,搜索新闻(非全文检索版:like匹配)
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>(); //模糊匹配 Right和Left代表百分号的位置 lambdaQuery.likeRight(User::getName,"M"); List<User> list = userDao.selectList(lambdaQuery); System.out.println(list);
统计报表(分组查询聚合函数)
QueryWrapper<User> lambdaQuery = new QueryWrapper<User>(); lambdaQuery.select("count(*) as count"); List<Map<String, Object>> userMaps = userDao.selectMaps(lambdaQuery); System.out.println(userMaps);
更多条件查询查看http://mybatis.plus/guide/wrapper.html#abstractwrapper
6.9 映射匹配兼容性
-
问题一:表字段与编码属性设计不同步
@Data public class User { @TableField(value = "pwd"); private String password; }
-
问题二:编码中添加了数据库中未定义的属性
@Data public class User { private Long id; private String name; private String password; private Integer age; private String tel; @TableField(exist = false) private boolean online; }
-
问题三:采用默认查询开放了更多字段的查看权限
@Data public class User { @TableField(value = "pwd",select = false); private String password; }
-
问题四:表名与编码开发设计不同步
@TableName("tbl_user") @Data public class User { private Long id; private String name; private String password; private Integer age; private String tel; }
6.10 id生成策略
-
不同的表应用不同的id生成策略
- 日志:自增(1,2,3,4,…)
- 购物订单:特殊规则(F023948AK3843)
- 外卖单:关联地区日期等信息(1004202003143491)
- 关系表:可省略id
- ...
-
注解:@TableId
-
范例:
@TableName("tbl_user") @Data public class User { @TableId(type = IdType.AUTO) private Long id; private String name; }
- 相关属性:
- value:设置数据库主键名称
- type:设置主键属性的生成策略,值参照IdType枚举值
- AUT0(0):使用数据库id自增策略控制id生成
- NONE(1):不设置id生成策略
- INPUT(2):用户手工输入id
- ASSIGN ID(3):雪花算法生成id(可兼容数值型与字符串型)
- ASSIGN UUID(4):以UUID生成算法作为id生成策略
- 相关属性:
6.11 多数据操作(删除与查询)
@Test
void testDelete(){
List<Long> list = new ArrayList<>();
list.add(1567537209630830593L);
list.add(1568108502637993985L);
userDao.deleteBatchIds(list);
}
@Test
void testSelectPro(){
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
userDao.selectBatchIds(list);
}
6.12 逻辑删除
-
删除操作业务问题:业务数据从数据库张丢弃
-
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中
在数据库表中新增字段delete,类型为int,长度为1,默认值为0
@TableLogic(value="0",deval = "1")
在User类标注后,执行delete不会删除该条数据但是查询无法查到
还可以在通用配置添加逻辑删除字段
mybatis-plus: global-config: banner: false db-config: #逻辑删除字段 logic-delete-field: deleted #删除 logic-delete-value: 1 #没有被删除 logic-not-delete-value: 0
6.13 乐观锁
业务并发现象带来的问题:秒杀
-
在数据库表中新增字段version,类型为int,长度为int最大长度11,默认值为1
-
在User对象中添加属性version,添加注解@Version
-
添加拦截器
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mPlusInterceptor(){ //添加乐观锁 mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } }
6.14 代码生成器
-
模板:MP提供
-
数据库相关配置:读取数据库获取信息
-
开发者自定义配置:手工配置
-
使用步骤:
-
添加坐标
<!--代码生成器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!--velocity模板引擎--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>
-
代码生成器类Generator
public class Generator { public static void main(String[] args) { AutoGenerator autoGenerator = new AutoGenerator(); DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///mybatisplus_db?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("123"); autoGenerator.setDataSource(dataSource); autoGenerator.execute(); //设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); //设置代码生成位置 globalConfig.setOutputDir(System.getProperty("user.dir") + "/MyBatisPlus_04_generator/src/main/java"); //设置生成完毕后是否打开生成代码所在的目录 globalConfig.setOpen(false); //设置作者 globalConfig.setAuthor("Mark"); //设置是否覆盖原始生成的文件 globalConfig.setFileOverride(true); //设置数据层接口名,%s为占位符,指代模块名称 globalConfig.setMapperName("%sDao"); //设置Id生成策略 globalConfig.setIdType(IdType.ASSIGN_ID); autoGenerator.setGlobalConfig(globalConfig); //设置包名相关配置 PackageConfig packageInfo = new PackageConfig(); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径 packageInfo.setParent("com.aaa"); //设置实体类包名 packageInfo.setEntity("domain"); //设置数据层包名 packageInfo.setMapper("dao"); autoGenerator.setPackageInfo(packageInfo); //策略设置 StrategyConfig strategyConfig = new StrategyConfig(); //设置当前参与生成的表名,参数为可变参数 strategyConfig.setInclude("tbl_user"); //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_ strategyConfig.setTablePrefix("tbl_"); //设置是否启用Rest风格 strategyConfig.setRestControllerStyle(true); //设置乐观锁字段名 strategyConfig.setVersionFieldName("version"); //设置逻辑删除字段名 strategyConfig.setLogicDeleteFieldName("deleted"); //设置是否启用lombok strategyConfig.setEntityLombokModel(true); autoGenerator.setStrategy(strategyConfig); //2.执行生成操作 autoGenerator.execute(); } }
-
-
代码生成器模板
- 添加坐标
<dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <optional>false</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
- 添加数据库配置和生成代码的位置
dataSource.url=jdbc:mysql://123.456.789.000/tableName?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false dataSource.driverName=com.mysql.cj.jdbc.Driver dataSource.username=root dataSource.password=9999 package.base=com.mark.mall.modules
- MyBatisPlusGenerator类
public class MyBatisPlusGenerator { public static void main(String[] args) { String projectPath = System.getProperty("user.dir") + "/tuling-mpbg"; String moduleName = scanner("模块名"); String[] tableNames = scanner("表名,多个英文逗号分割").split(","); // 代码生成器 AutoGenerator autoGenerator = new AutoGenerator(); autoGenerator.setGlobalConfig(initGlobalConfig(projectPath)); autoGenerator.setDataSource(initDataSourceConfig()); autoGenerator.setPackageInfo(initPackageConfig(moduleName)); autoGenerator.setCfg(initInjectionConfig(projectPath, moduleName)); autoGenerator.setTemplate(initTemplateConfig()); // autoGenerator.setStrategy(initStrategyConfig(tableNames)); autoGenerator.setTemplateEngine(new VelocityTemplateEngine()); autoGenerator.execute(); } /** * 读取控制台内容信息 */ private static String scanner(String tip) { Scanner scanner = new Scanner(System.in); System.out.println(("请输入" + tip + ":")); if (scanner.hasNext()) { String next = scanner.next(); if (StrUtil.isNotEmpty(next)) { return next; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } /** * 初始化全局配置 */ private static GlobalConfig initGlobalConfig(String projectPath) { GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(projectPath + "/src/main/java"); globalConfig.setAuthor("XuShu"); globalConfig.setOpen(false); globalConfig.setSwagger2(true); globalConfig.setBaseResultMap(true); globalConfig.setFileOverride(true); globalConfig.setDateType(DateType.ONLY_DATE); globalConfig.setEntityName("%s"); globalConfig.setMapperName("%sMapper"); globalConfig.setXmlName("%sMapper"); globalConfig.setServiceName("%sService"); globalConfig.setServiceImplName("%sServiceImpl"); globalConfig.setControllerName("%sController"); return globalConfig; } /** * 初始化数据源配置 */ private static DataSourceConfig initDataSourceConfig() { Props props = new Props("generator.properties"); DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setUrl(props.getStr("dataSource.url")); dataSourceConfig.setDriverName(props.getStr("dataSource.driverName")); dataSourceConfig.setUsername(props.getStr("dataSource.username")); dataSourceConfig.setPassword(props.getStr("dataSource.password")); return dataSourceConfig; } /** * 初始化包配置 */ private static PackageConfig initPackageConfig(String moduleName) { Props props = new Props("generator.properties"); PackageConfig packageConfig = new PackageConfig(); packageConfig.setModuleName(moduleName); packageConfig.setParent(props.getStr("package.base")); packageConfig.setEntity("model"); return packageConfig; } /** * 初始化模板配置 */ private static TemplateConfig initTemplateConfig() { TemplateConfig templateConfig = new TemplateConfig(); //可以对controller、service、entity模板进行配置 //mapper.xml模板需单独配置 templateConfig.setXml(null); return templateConfig; } /** * 初始化策略配置 */ private static StrategyConfig initStrategyConfig(String[] tableNames) { StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); strategyConfig.setEntityLombokModel(true); strategyConfig.setRestControllerStyle(true); //当表名中带*号时可以启用通配符模式 if (tableNames.length == 1 && tableNames[0].contains("*")) { String[] likeStr = tableNames[0].split("_"); String likePrefix = likeStr[0] + "_"; strategyConfig.setLikeTable(new LikeTable(likePrefix)); } else { strategyConfig.setInclude(tableNames); } return strategyConfig; } /** * 初始化自定义配置 */ private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) { // 自定义配置 InjectionConfig injectionConfig = new InjectionConfig() { @Override public void initMap() { // 可用于自定义属性 } }; // 模板引擎是Velocity String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + moduleName + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); injectionConfig.setFileOutConfigList(focList); return injectionConfig; } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战