1、起因
因为要在项目中同时访问redis,mongo和mysql三种数据库,而且因为偏向spring-data,所以都使用了spring-data
在使用的过程中如果不做配置发现会有冲突,这篇文章也是解决这个问题,避免以后遇到同样的问题不知所措
2、项目实战
2.1 创建springboot项目
没啥好说的,直接一路next,使用maven管理项目
2.2 加入spring-data的配置
这里使用了mongo和mysql,也是工作中常用的两个数据库,pom中配置如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.3 配置数据源
application.yml中主要是数据库的链接信息,mysql 和mongo的
server:
port: 9099
spring:
data:
mongodb:
uri: mongodb://172.26.1.152:27017/ccp_test
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://172.26.1.152:3306/cx_test?characterEncoding=utf8&serverTimezone=UTC
username: root
password: 123456
hikari:
minimum-idle: 10
maximum-pool-size: 150
connection-test-query: SELECT 1 FROM DUAL
connection-timeout: 600
idleTimeout: 30000
maxLifetime: 40000
validation-timeout: 300
login-timeout: 10
jpa:
show-sql: true
2.4 配置entity
这里是使用mysql的entity,mongo中直接复用同样的entity,
@Data
@Entity
@Table(name = "test_table")
@Document(collection = "test_table")
public class TestTable implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;}
2.5 分别创建Repository
mysql的Repository,操作mysql 数据
package com.xin.mongoandmysql.dao.mysql;
import com.xin.mongoandmysql.entity.TestTable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface TestTableRepository extends JpaRepository<TestTable, Integer>, JpaSpecificationExecutor<TestTable> {
}
mongo的Repository,操作mongo 数据
package com.xin.mongoandmysql.dao.mongo;
import com.xin.mongoandmysql.entity.TestTable;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface MongoTableRepository extends MongoRepository<TestTable, String> {
}
注:上面两个repo 放在不同的包下面,不同数据库集成的repo不同
2.6 配置扫描的包
@SpringBootApplication
@EnableMongoRepositories(basePackages = {"com.xin.mongoandmysql.dao.mongo"})
@EnableJpaRepositories(basePackages = "com.xin.mongoandmysql.dao.mysql")
public class MongoAndMysqlApplication {
public static void main(String[] args) {
SpringApplication.run(MongoAndMysqlApplication.class, args); }
}
这里很重要,配置mysql 和mongo的扫描路径,也是这里的难点,后面会讲原理和为什么
2.7 测试
创建一个简单的controller就行
@RestController
public class TestController {
@Resource
MongoTableRepository mongoTableRepository;
@Resource
TestTableRepository testTableRepository;
@GetMapping("/test")
public void test(){
TestTable testTable = new TestTable();
testTable.setName("香菜");
testTable.setId(1);
mongoTableRepository.save(testTable);
testTableRepository.save(testTable);
}
}
直接调用接口,就可以看到数据存入数据库,OK,实验结束。
2.8 看下结果
可以看到mysql和mongo 都存入了数据
3、打怪之旅
3.1 服务器启动不了,bean注册失败
最初的时候因为没有分包扫描,导致服务器启动失败,之前没怎么注意过这件事
原因是因为jpa和mongo都会去根路径下扫描所有的repo,然后生成动态代理,
mysql 扫描到testRepo会生成代理
mongo扫描到testRepo也会生成代理
两者都会想容器进行注册,这就导致bean出现重名的现象
源码:
org.springframework.data.repository.config.RepositoryConfigurationDelegate#registerRepositoriesIn
这里可以看到,RepositoryConfigurationDelegate来自spring-data-commons的包,mysql和mongo都会调用
在源码中可以看到有个basePackages的属性,如果不配置,默认是从根路径进行扫描,就会出现被代理多次注册的问题
3.2 spring-data 是如何实现repo中的接口的
在一个项目中有多个 spring-data 模块时,尝试为特定实体创建一个接口,该接口没有使用特定的持久性技术注释进行注释
(如 javax.persistence 中的@Entity,或 mongo 中的@Document),通过从 spring-data-commons 模块(即 Repository 或 CrudRepository)扩展接口 - 这根本不起作用,因为 spring 基本上不知道你的实体与哪个数据存储相关联。
在 Spring 内部有一个抽象,称为RepositoryFactoryBeanSupport。尝试将 spring data jdbc starter 和 spring data mongodb starter 添加到您的项目中。你会注意到,在类路径中有2 个不同的 RepositoryFactoryBeanSupport 实现:
- JdbcRepositoryFactoryBean(来自spring data jdbc starter)
- MongoRepositoryFactoryBean(来自spring data mongo starter)
如何让不同的repo关联相应的数据库
- 扩展特定于技术的存储库。例如,如果希望实体 A 与 PostgreSQL 关联,则不要使用 CrudRepository - 应该使用 JpaRepository。如果希望实体 B 与 Redis 相关联,请使用 RedisRepository 等。
- 使用注释对您的实体进行注释,表明它隶属于特定的数据存储。如@Entity、@Document、@Table 等。
总结
注:分包扫描是解决问题的重点
很久之前写的文章了,在看之前回想一下,依然没有清晰的理解
复习一遍就掌握了,学而时习之不亦乐乎
关注点赞,一键三连,感谢支持