为什么需要多数据源
同一个项目有时会涉及到多个数据库,这时我们就要配置多个数据源。配置多数据源的常见情况有以下两种:
1)同一个项目中涉及两个或多个业务数据库,它们之间相互独立,这种情况也可以作为两个或多个项目来开发
2)两个或多个数据库之间是主从关系,主库负责写,从库负责读
多数据源的配置
1、pom.xml配置
在pom.xml中增加MyBatis-Plus多数据源依赖:
<!-- MyBatis-Plus多数据源依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.1</version>
</dependency>
2、配置文件配置
在配置文件application.yml中配置我们需要连接的数据库:blog和user,默认为blog
spring:
datasource:
dynamic:
# 设置默认数据源
primary: blog
# 设置严格模式,false为不启动,启动后在未匹配到指定数据源时会抛出异常,不启动则使用默认数据源
strict: false
datasource:
blog:
url: jdbc:mysql://localhost:3306/blog
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
druid:
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 2000
validationQuery: select 'x'
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
user:
url: jdbc:mysql://localhost:3306/user
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
druid:
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 2000
validationQuery: select 'x'
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、启动类配置
在@SpringBootApplication注解上增加exclude = DruidDataSourceAutoConfigure.class配置:
@SpringBootApplication(scanBasePackages = "com.tn222.springboot.article12.**",
exclude = DruidDataSourceAutoConfigure.class)
@MapperScan(basePackages = "com.tn222.springboot.article12.dao")
public class Article12Application {
public static void main(String[] args) {
SpringApplication.run(Article12Application.class, args);
}
}
这个配置的作用是去掉对DruidDataSourceAutoConfigure的自动配置,否则程序会报错:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
原因:
DruidDataSourceAutoConfigure在DynamicDataSourceAutoConfiguration之前,其会注入一个DataSourceWrapper,会在原生的spring.datasource下找url, username, password等,而我们动态数据源的配置路径是变化的。
4、实体类和dao层配置
在po文件夹下创建blog和user文件夹,分别用于存储blog数据库和user数据库的实体:
@TableName(value = "article")
public class ArticlePo {
@TableId(type = IdType.AUTO)
private Integer id;
private String articleId;
private String title;
// get、set...
}
@TableName(value = "user_info")
public class UserInfoPo {
@TableId(type = IdType.AUTO)
private Integer id;
private String userId;
private String name;
// get、set...
}
注解:
@TableName:表名注解,标识实体类对应的表
@TableId:主键注解,当type = IdType.AUTO时,表示这个主键是自增主键
在dao文件夹下创建blog和user文件夹,分别用于存储blog和user的dao:
@Repository
public interface ArticleDao extends BaseMapper<ArticlePo> {
}
@Repository
@DS("user")
public interface UserInfoDao extends BaseMapper<UserInfoPo> {
}
注解:
@Repository:将数据访问层(DAO层)的类标识为Spring Bean
@DS:配置非默认数据源,本示例中blog为默认数据源,user为非默认数据源,在使用@DS注解时,有如下注意事项:
1)不能使用事务,否则数据源不会切换,使用的还是第一次加载的数据源
2)第一次加载数据源之后,第二次,第三次……操作其他数据源,如果数据源不存在,使用的还是第一次加载的数据源
3)数据源名称不要包含下划线,否则不能切换
5、测试验证
编写ArticleController和UserInfoController:
@RestController
@RequestMapping("/article")
public class ArticleController {
@Resource
private ArticleDao articleDao;
@GetMapping(value = "/get")
public ArticlePo get(@RequestParam("articleId") String articleId) {
ArticlePo articlePo = articleDao.selectOne(Wrappers.<ArticlePo>lambdaQuery()
.eq(ArticlePo::getArticleId, articleId));
return articlePo;
}
@PostMapping(value = "/insert")
public Boolean insert(@RequestBody ArticlePo articlePo) {
articleDao.insert(articlePo);
Boolean res = false;
if (articlePo.getId() > 0) {
res = true;
}
return res;
}
}
@RestController
@RequestMapping("/user")
public class UserInfoController {
@Resource
private UserInfoDao userInfoDao;
@GetMapping(value = "/get")
public UserInfoPo get(@RequestParam("userId") String userId) {
UserInfoPo res = userInfoDao.selectOne(Wrappers.<UserInfoPo>lambdaQuery().eq(UserInfoPo::getUserId, userId));
return res;
}
@PostMapping(value = "/insert")
public Boolean insert(@RequestBody UserInfoPo userInfoPo) {
userInfoDao.insert(userInfoPo);
Boolean res = false;
if (userInfoPo.getId() > 0) {
res = true;
}
return res;
}
}
注⚠️:业务逻辑复杂时,Controller和Mapper中间会有Service层来处理业务逻辑,现在我们就简单的测试一下多数据源,所以直接使用Controller调用Mapper了
MyBatis-Plus的分页
1、配置分页插件
@Configuration
@MapperScan("com.tn222.springboot.article12.dao")
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
2、分页方法
1)使用MyBatis-Plus的selectPage方法
@GetMapping("/listByLambda")
public Object listByLambda(@RequestParam("title") String title, @RequestParam("pageIndex") Integer pageIndex,
@RequestParam("pageSize") Integer pageSize) {
IPage<ArticlePo> thisPage = new Page<>(pageIndex, pageSize);
LambdaQueryWrapper<ArticlePo> eq = Wrappers.<ArticlePo>lambdaQuery()
.eq(ArticlePo::getTitle, title).orderByDesc(ArticlePo::getId);
thisPage = articleDao.selectPage(thisPage, eq);
return thisPage;
}
使用MyBatis-Plus的selectPage方法,返回了IPage<ArticlePo>,示例:
{
"records": [
{
"id": 7,
"articleId": "f68c5ccf9e3b4a719b369f9a84316635",
"title": "Mybatis-Plus update"
},
{
"id": 2,
"articleId": "1eabc4f8ce0711ec9b05979114b13a0f",
"title": "Mybatis-Plus update"
}
],
"total": 3,
"size": 2,
"current": 1,
"orders": [],
"searchCount": true,
"pages": 2
}
2)sql分页
有时候有些分页需要关联多张表,使用LambdaQueryWrapper不太方便,这时候可以自己写sql来实现分页,主要有两种:纯sql自己实现分页和使用IPage实现分页
注⚠️:这里的sql示例就使用单表查询了,具体的可根据业务场景使用多表查询
A、纯sql自己实现分页
分页的数据list和总条数单独调用方法返回 :
@Select("select * from article where title=#{title} order by id desc limit #{offset}, #{pageSize}")
List<ArticlePo> listByTitle(@Param("title") String title, @Param("offset") Integer offset,
@Param("pageSize") Integer pageSize);
@Select("select count(id) from article where title=#{title}")
int countByTitle(@Param("title") String title);
B、使用IPage实现分页(常用)
@Select("select * from article where title=#{title} order by id desc")
IPage<ArticlePo> listByPage(IPage thisPage, @Param("title") String title);
返回IPage<ArticlePo>,返回值的数据结构见“1)使用MyBatis-Plus的selectPage方法”
本文简单介绍了一下MyBatis-Plus的多数据源和分页,本文示例代码