1.MybatisPlus使用和介绍
先不谈多数据源及其数据库连接池,就从最基础的MybatisPlus及其基础的数据库配置说起。
为什么使用:见名知意,Plus就是Mybatis的增强版本,并且支持原来Mybatis的配置及其写法。
(1)它简化了我们的CRUD的写法,简单来说它提供了很多CRUD的方法供我们直接使用
(2)它提供了代码生成器,可以帮我们直接生成entity,mapper,service,mapper的映射文件
主要作用就是(1),本来增删改也没啥含金量,能直接调用别人写好的不是更方便嘛。
下面直接上代码看看是怎么简化的。
1.1引入MybatisPlus起步依赖和数据库Mysql连接的依赖
<!-- springboot基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mybatis-plus起步以来 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--mysql连接依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
<scope>runtime</scope>
</dependency>
1.2创建数据库和对应的包名
数据库,我这就一个学生表student,方便我们进行测试
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '学生姓名',
`age` int NOT NULL COMMENT '学生年龄',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=94 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';
现在创建我们的包,只创建包不创建具体的类,类我们下面用Mybatis提供给我们的代码生成器去生成,直接节省N倍工作量,创建entity,mapper,service,mapper的映射文件包xml。
1.3引入代码生成器依赖
<!-- mybatis-plus代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
它为我们提供了不同类型的模版类,例如生成entity的模板类,service的模板类,我们代码生成器最终生成的实体都是按照这个去生成的。
现在稍微看一下这个模板类,引入以来后已经帮我们提供了
接下来我们随便看一个,例如我们的实体类entity.java.vm,这里主要看一下他的大概样子即可。我随便截取一段。
其实它里面大都是if else操作。大概逻辑是如果存在,就显示,例如
这个相当于你做了序列化的配置我就把 private static final long serialVersionUID = 1L;这句话加到你的实体类中,一个实体所有的东西都是这么判断显示的,例如包,类名,字段,注释等等。
上面的模板类了解以后,我们接下来就写代码生成的工具类GenreatorUtil
public class GeneratorUtil {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入作者: ");
String author = scanner.nextLine();//作者
System.out.print("请输入数据库表名: ");
String tableName = scanner.nextLine();//数据库表名
String url = "jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8";
String username = "root";//数据库名用户
String password = "daz123456";//数据库密码
String path = System.getProperty("user.dir");//获取当前项目的根目录
FastAutoGenerator.create(url,username, password)
//全局配置
.globalConfig(builder -> {
builder.author(author) // 设置作者
.fileOverride() // 覆盖已生成文件
.outputDir(path+"/src/main/java/")// 指定输出目录
.disableOpenDir()//禁止打开输出目录
.commentDate("yyyy/MM/dd");//设置日期格式
})
//包配置
.packageConfig(builder -> {
builder.parent("com.test") // 设置父包名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, path+"/src/main/resources/xml")); // 设置mapperXml生成路径
})
//策略配置
.strategyConfig(builder -> {
builder.addInclude(tableName); // 设置需要生成的表名
builder.entityBuilder()
.enableLombok()//开启lombok,不开启会帮你生成set,get方法
.formatFileName("%sDO")//生成的实体类的格式,%s:这个是表名
.idType(IdType.AUTO);//设置ID主键自增
})
.execute();
}
}
上面的配置,每一项需要自己根据自己创建的目录去配置,需要输入作者和表名进行生成,最终它会根据我们的模板类去生成,我们先用它给我的模板去看看结果是什么。
看看我们生成的目录及其效果。
可以看出所有的都生成了,我们进去看看他提供的默认的模板,例如entity。
或多或少都存在一些小问题,此时就需要我们自己去定义一个符合我们要求的或者样式的。
1.4自定义代码生成的模板模板
其实就是把系统提供给我们的给修改了,如果我们在resources/templates目录下有定义这几个模板,优先会在那好我们定义的模板进行生成。我们如何自定义呢?其实很简单,把它提供给我们默认的直接copy到templates目录下面。拿我们的entity.java.vm举例子。
其实也很简单,就拿那个注释来举例,我们直接把P标签直接去掉,加上我们的@description xxx
还有那个setter,getter,我们也不要直接换成@Data。
其次是那个@TableId没有对齐我们给他设置对齐,就是把前面的空格删掉。
接下来,我们把原来生成的删掉重新生成看效果。
基本上所有的样式都成我们自定义的啦,按需修改就行,都是些微调简单的东西,service,mapper等其他的模板修改操作也是如此,这样我们的代码生成器就完成了,以后如果你想要生成代码直接走这个。如果你想要生成多个,当然也可以,输入表明的时候用","分隔开,搞个循环去创建,自己可以试试。
1.5看看MybatisPlus生成的service和mapper的与我们自己定义的区别
首先看mapper,其实就是多继承了一个BaseMapper
BaseMapper是什么?
其实可以看出就是为我们提供好了很多的增删改查的方法直接使用,这样我们不用再到mapper的映射文件去写sql了,简单的增删改也没必要写对吧。
接下里看一下service及其service的实现类。
其实可以看出来,Service继承了IService,那IService里面又有什么呢?
其实说白了,它里面放的也是增删改查的方法,并且多提供了一些方法,例如listByIds,那这里有个疑问,为什么mapper已经提供了,我们直接使用mapper的不就行了,为什么还要多出一个service,这就是我们三层结构的原因,你总不能在controller直接调用mapper层吧,不符合规范,我们一般在controller调用service,并且我们在service层还可以进行自定义的方法去调用。如果你非要在controller层去调用mapper,那我也没办法。
1.6在controller进行测试
在写之前,先将我们mapper层的@Mapper注解加上,生成的好像没有,并且生成的controller上面的@Controller要改成@RestController,所以你自定义去实现,或者手动加上或修改,再配一下application.yml文件连接数据库。
server:
port: 9999
spring:
application:
name: test
datasource:
type:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: daz123456
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true&rewriteBatchedStatements=true
我们现在看看我们查询有多简单,在我们生成的controller去写个获取列表的测试方法。
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private IStudentService iStudentService;
@RequestMapping("/list")
public List<StudentDO> list(){
List<StudentDO> list = iStudentService.list();
return list;
}
}
可以看出我们就一行代码iStudentService.list就获取了列表,接下来介绍一下,条件查询我们该怎么去查。其实这个list是个重载的方法,支持我们传入一个参数叫wrapper。
这个wrapper,这里你可以把它理解为我们sql里面的where查询条件,他是接口,他的实现类如下
重点就看Querywrapper和UpdateWrapper就行。我们就直接看写法就行。
可见查出年龄全为23岁的啦,其实那个接下来就介绍一下QueryWapper条件查询常见的方法。
1.lambda,这个就是相当于将QueryWrapper换成了LambdaQueryWrapper的写法,这样就支持我们的lambda的写法,例如.eq方法的第一个参数,本来应该写个字段的名称例如"age",但是这样呢不易维护,如果你后期字段名称修改了,你很多地方都需要修改,那直接取类的某个字段作为字段名称的写法就很方便,并且以后修改了,只用改类里面,看起来也比较雅观。
2.eq方法,这个简单,前者就是字段名,后者是条件
这些全是,足够我们的查询使用了,自己私下都去试试每个方法是干嘛的。
它的修改方法以及传什么样的wrapper都已经告诉你了,自己都去试试,我这就不多介绍了。
下面就介绍一下它的分页查询怎么优化的,怎么更简单的。
1.7如何进行分页查询
作为后台系统来说,必不可少的就是分页查询,那MybatisPlus怎么实现的呢?
1.配置一个MybatisPlusInterceptor拦截器交给Spring去管理,编写MyabtisPlusConfig配置类
@Component
public class MybatisPlusConfig {
/**
* 分页插件,不配置否则分页不生效
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
如果你不配置,下面我们虽然能去调用相关的方法,但是不会有分页的效果。
2.调用它的page方法。
可以可看到需要一个page参数和一个queryWrapper,queryWrapper已经有了,Page参数MybatisPlus也为我们提供好了,这个Page主要是为了设置,当前页和分页大小的。
完整代码:
@RequestMapping("/list")
public List<StudentDO> list(){
QueryWrapper<StudentDO> studentDOQueryWrapper = new QueryWrapper<>();//创建wrapper
studentDOQueryWrapper.lambda().eq(StudentDO::getAge,23);//添加where查询条件
Page<StudentDO> page = new Page<>(1,2);//配置当前页和分页大小
Page<StudentDO> studentPage = iStudentService.page(page, studentDOQueryWrapper);
return studentPage.getRecords();
}
我们断点来看一下这个studentPgae分页结果是什么?
可以看到分页的信息,总条数这写结果都有,足够我们使用了。
所以可看出我们的分页查询也够用。
1.8为什么还要用Mybatis
虽然提供了我们很CRUD的方法,但是上面大都是单表或者是链一两张表都可以解决,但是如果我们一个查询涉及到多张表(7张左右),此时你没办法处理,你总不能查询一张拿出结果再查另外一张,以此类推,这个首先读取多少次磁盘暂且不说,你这样会非常麻烦,那么此时还是需要我们去写Mapper的映射文件,但是MybatisPlus也支持Mybatis的写法,只是application.yml配置文件的写法可能稍有不同。看看MybatisPlus的写法。
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:xml/*Mapper.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 auto:"数据库ID自增" 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: auto
#字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"
field-strategy: NOT_EMPTY
#数据库类型
db-type: MYSQL
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 0 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
可以看出我们可以配置mapper映射文件及其开启驼峰命名等等配置都是有的。
2.SpringBoot配置数据库连接池
2.1为什么使用数据库连接池
- 提高性能:通过复用已有的连接,减少创建和销毁连接的开销。
- 减少资源消耗:有效管理和限制同时连接数据库的数量,避免过度占用数据库资源。
- 增强应用响应速度:减少每次数据库操作时的连接建立时间,提高响应速度。
- 提高稳定性:提供连接池健康检查和故障恢复机制,确保连接的稳定性和可靠性。
- 支持并发访问:允许多个线程或用户同时安全地访问数据库,提升并发处理能力
2.2数据库连接池的种类介绍
Hikari:SpringBoot默认提供的
DBCP:Apache提供的
C3p0
Druid:由阿里巴巴提供,提供了强大的监控功能。
我们此处将一下Druid,好处就是提供了监控的功能。
2.3如何使用Druid数据库连接池
1.引入依赖
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
2.在application中配置druid的数据库连接池的配置
spring:
application:
name: test
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: daz123456
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true&rewriteBatchedStatements=true
type: com.alibaba.druid.pool.DruidDataSource
# 数据连接池配置
druid:
#连接池最大连接数
max-active: 150
#获取连接的最大等待时间
max-wait: 60000
#连接池最小连接数
min-idle: 15
#连接池初始化大小
initial-size: 15
filters: stat,wall,slf4j
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
max-pool-prepared-statement-per-connection-size: 20
pool-prepared-statements: false
test-on-borrow: false
test-on-return: false
test-while-idle: true
validation-query: SELECT 1
min-evictable-idle-time-millis: 300000
time-between-eviction-runs-millis: 60000
#监控信息 localhost:${server.port}/druid/login.html
stat-view-servlet:
login-username: xxx
login-password: xxx
#允许哪个机器登录
allow:
#sql监控开关
enabled: true
#url监控
web-stat-filter:
enabled: true
#session监控
session-stat-enable: true
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
主要是配置datasource.type这个指定使用数据库连接池的类型,不指定默认使用Hikari。
下面就是durid数据库连接池的配置,例如最大连接数,最小连接数,最长连接等待时间,以及监控信息的配置,一定要做好监控,也发挥它的优势所在。
3.SpringBoot实现多数据源的配置
3.1为什么要进行多数据源的配置
1.读写分离:为了分担数据库的压力,一般是将增删改和读的操作分别操作不同的数据库。
2.不同模块所操作的数据源也是不同的,例如登录模块,核心业务模块和财务模块使用的数据库都是不同的,也需要配置多数据源
3.2如何进行多数据源的配置
1.引入dynamic的起步依赖
<!-- 用来配置多数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.5.5</version>
</dependency>
2.在application进行配置多数据源
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
application:
name: test
datasource:
dynamic:
primary: master
strict: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: daz123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true&rewriteBatchedStatements=true
跟我们之前的写法稍有不同,这次是在配置我们datasource之前多了一个dynamic的配置。
dynamic.primary:这个是配置默认使用的数据源的。
dynamic.strict:这个是不开启严格检查,避免严格检查导致的报错。
上面还有一个配置spirng.autoconfiguration.exclude这个,必须要配置,排除druid数据库连接池的自动配置,否则他会跟我们配置的起冲突,导致启动项目时就会报错。
dynamic.datasource.xxx:这个xxx就是我们数据库的别名,用于我们后续使用,你有多少个我就配置多少个数据库,通过xxx名称来区分他们,现在我模拟两个数据源,一个主:(用来增删改),一个从数据库(用于读取),分别对应不同的数据库。
master(数据库test):放一条数据,名字为张三
slaver_rea(数据库slave_test):也放一条数据,名字叫李四
接下来就是我们如何去使用不同的数据库。
3.3测试多数据源
其实就是一个注解即可,@DS("xxx"),就是你刚才配置的master或者slave_read
我现在写三个方法去测试,看看结果如何。
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private IStudentService iStudentService;
/**
* 1.不指定看看默认操作的数据源是不是我们primary指定的
*/
@RequestMapping("/list")
public List<StudentDO> list(){
List<StudentDO> list = iStudentService.list();
return list;
}
/**
* 2.模拟向主数据库插入一条数据
*/
@RequestMapping("/insert")
public void insert(){
StudentDO student = new StudentDO();
student.setName("王五");
student.setAge(30);
iStudentService.save(student);
}
/**
* 3.使用注解@DS指定从从数据库路进行读取数据
*/
@DS("slave_read")
@RequestMapping("/listBySlave")
public List<StudentDO> listBySlave(){
List<StudentDO> list = iStudentService.list();
return list;
}
}
方法一和方法二都未指定@DS从哪个数据库获取,默认就从我们的primary配置的master中获取。
我们master目前存储的是张三,方法一查询的结果也应该是张三
结果是对的,现在执行方法二,插入王五,再执行方法一,看看结果。
结果多了一条王五的数据。
我们再来看看方法三,它使用@DS注解指定从slave_read从数据库获取,里面只有一个李四,看结果
结果显示正确,也验证了我们的配置和想法,也实现了多数据源的配置。再来看看@DS这个注解
这个注解可以用在方法上,也可以用在类上。用在类上的时候是有一些数据库它并不分主从,例如财务模块的数据库,就不分主从,直接加在类上,不过具体还得分情况。