1.hint指定强制使用索引
使用Query.withHint方法来指定使用索引
@Test
void testHint(){
//1.设置年龄索引
Index ageIndex = new Index().on("age", Sort.Direction.ASC);
//2.创建索引
String indexName = mongoTemplate.indexOps(Student.class).ensureIndex(ageIndex);
//3.创建查询条件
Query query = new Query(Criteria.where("age").is(1)).withHint(indexName);
//4.指定使用年龄索引查询
List<Student> students = mongoTemplate.find(query, Student.class);
}
2.事务操作
前置条件
- mongo服务器版本>=4.0
- mongo服务器必须部署为副本集模式/副本集的分片模式
- mongo在事务中,只允许从主节点读取数据,不允许从从节点读取数据。因此需要视情况readPreference(读偏好)的配置
单数据源
添加事务处理器
如果提示factory找不到,不用管它。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.MongoTransactionManager; import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
/**
- @author: 冯铁城 [17615007230@163.com]
- @date: 2022-07-27 15:49:00
@describe: mongo事务监听器 */ @Configuration public class MongoTransactionConfiguration {
@Bean MongoTransactionManager mongoTransactionManager(SimpleMongoClientDatabaseFactory factory) {
return new MongoTransactionManager(factory);
编写方法并添加事务注解
```java import com.ftc.mongotestv2.entity.Student; import lombok.RequiredArgsConstructor; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
/**
- @author:冯铁城 [17615007230@163.com]
- @date:2022-07-27 15:52:12
@describe: */ @Service @RequiredArgsConstructor public class StudentService {
private final MongoTemplate mongoTemplate;
@Transactional(rollbackFor = Exception.class) public void saveStudent() {
//1.创建对象
Student student = new Student();
student.setId("1");
//2.保存
mongoTemplate.insert(student);
//3.执行异常
int error = 1 / 0;
验证
- 查询集合中并无数据
- 触发保存 ```java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest class StudentServiceTest {
@Autowired
private StudentService studentService;
@Test
void saveStudent() {
studentService.saveStudent();
}
}
3. 数据并未存入,事务回滚成功
![image.png](https://cdn.nlark.com/yuque/0/2022/png/28218714/1659075046697-ac86c57d-994b-4fb8-8149-39c549bc99f3.png#clientId=u0e47fd13-e2a0-4&crop=0&crop=0&crop=1&crop=1&errorMessage=unknown%20error&from=paste&height=54&id=uab41ad1f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=81&originWidth=518&originalType=binary&ratio=1&rotation=0&showTitle=false&size=10684&status=error&style=none&taskId=ufbe86f1b-238a-434e-969e-f76a9d5161e&title=&width=345.3333333333333)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/28218714/1659075017571-30f0fc4b-28ec-4e1f-9887-17a9bfc489e7.png#clientId=u0e47fd13-e2a0-4&crop=0&crop=0&crop=1&crop=1&errorMessage=unknown%20error&from=paste&height=308&id=u99d4a734&margin=%5Bobject%20Object%5D&name=image.png&originHeight=462&originWidth=1669&originalType=binary&ratio=1&rotation=0&showTitle=false&size=102376&status=error&style=none&taskId=uc0a7da77-6035-4d78-b709-fc32ff07dc3&title=&width=1112.6666666666667)
<a name="tobdW"></a>
### 多数据源
<a name="TTbL1"></a>
#### 主数据源添加事务管理器
重点关注两点:
1. primaryMongoTransactionManager的创建
2. @Bean("primaryFactory")注解一定不能少!!!少了之后事务管理器直接失效!!!!!具体为啥目前还没测试出来,但是一定不能没有!!!!
```java
import cn.hutool.core.util.StrUtil;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ConnectionPoolSettings;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author: 冯铁城 [17615007230@163.com]
* @date: 2022-07-29 15:44:02
* @describe: mongo主数据源各种配置类
*/
@Configuration
@EnableMongoRepositories(basePackages = "com.ftc.mongotest", mongoTemplateRef = "primaryTemplate")
public class PrimaryMongoTemplate {
@Resource
@Qualifier("primaryMongoProperties")
private MongoConfigProperties primaryProperties;
@Primary
@Bean(name = "primaryTemplate")
public MongoTemplate primaryTemplate() {
//1.获取primaryTemplate
SimpleMongoClientDatabaseFactory primaryFactory = getFactory(this.primaryProperties);
MongoTemplate primaryTemplate = new MongoTemplate(primaryFactory);
//2.默认数据源监听处理
String type = "_class";
MongoConverter converter = primaryTemplate.getConverter();
if (converter.getTypeMapper().isTypeKey(type)) {
((MappingMongoConverter) converter).setTypeMapper(new DefaultMongoTypeMapper(null));
}
//3.返回
return primaryTemplate;
}
@Bean(name = "primaryMongoTransactionManager")
public MongoTransactionManager primaryTransactionManager() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.primaryProperties);
return new MongoTransactionManager(factory);
}
@Primary
@Bean(name = "primaryGridFsTemplate")
public GridFsTemplate primaryGridFsTemplate() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.primaryProperties);
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext());
return new GridFsTemplate(factory, converter);
}
@Primary
@Bean("primaryFactory")
public SimpleMongoClientDatabaseFactory getFactory(MongoConfigProperties properties) {
//1.设置链接地址
List<ServerAddress> hosts = new ArrayList<>();
properties.getAddress().forEach(address -> {
List<String> addressInfos = StrUtil.split(address, StrUtil.COLON);
hosts.add(new ServerAddress(addressInfos.get(0), Integer.parseInt(addressInfos.get(1))));
});
//2.初始化连接池参数
ConnectionPoolSettings poolSetting = ConnectionPoolSettings
.builder()
.maxWaitTime(10000, TimeUnit.MILLISECONDS)
.build();
//3.构造基础链接参数
MongoClientSettings.Builder settingBuilder = MongoClientSettings
.builder()
.applyToConnectionPoolSettings(builder -> builder.applySettings(poolSetting))
.applyToClusterSettings(builder -> builder.hosts(hosts))
.readPreference(ReadPreference.secondaryPreferred());
//4.初始链接参数以及连接池参数
MongoClientSettings settings;
//5.根据用户名是否为空判定是否鉴权
if (StrUtil.isNotBlank(properties.getUsername())) {
//6.添加授权参数
MongoCredential credential = MongoCredential.createScramSha1Credential(
properties.getUsername(), properties.getDatabase(), properties.getPassword().toCharArray()
);
//7.添加链接参数
settings = settingBuilder.credential(credential).build();
} else {
//7.添加链接参数
settings = settingBuilder.build();
}
//8.创建工厂返回
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), properties.getDatabase());
}
}
备数据源添加事务管理器
与主数据源关注一致即可
import cn.hutool.core.util.StrUtil;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ConnectionPoolSettings;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author: 冯铁城 [17615007230@163.com]
* @date: 2022-07-29 15:44:02
* @describe: mongo主数据源各种配置类
*/
@Configuration
@EnableMongoRepositories(basePackages = "com.ftc.mongotest", mongoTemplateRef = "secondaryTemplate")
public class SecondaryMongoTemplate {
@Resource
@Qualifier("secondaryMongoProperties")
private MongoConfigProperties secondaryProperties;
@Bean(name = "secondaryTemplate")
public MongoTemplate secondaryTemplate() {
//1.获取secondaryTemplate
SimpleMongoClientDatabaseFactory secondaryFactory = getFactory(this.secondaryProperties);
MongoTemplate secondaryTemplate = new MongoTemplate(secondaryFactory);
//2.默认数据源监听处理
String type = "_class";
MongoConverter converter = secondaryTemplate.getConverter();
if (converter.getTypeMapper().isTypeKey(type)) {
((MappingMongoConverter) converter).setTypeMapper(new DefaultMongoTypeMapper(null));
}
//3.返回
return secondaryTemplate;
}
@Bean(name = "secondaryMongoTransactionManager")
public MongoTransactionManager secondaryTransactionManager() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.secondaryProperties);
return new MongoTransactionManager(factory);
}
@Bean(name = "secondaryGridFsTemplate")
public GridFsTemplate secondaryGridFsTemplate() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.secondaryProperties);
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext());
return new GridFsTemplate(factory, converter);
}
@Bean("secondaryFactory")
public SimpleMongoClientDatabaseFactory getFactory(MongoConfigProperties properties) {
//1.设置链接地址
List<ServerAddress> hosts = new ArrayList<>();
properties.getAddress().forEach(address -> {
List<String> addressInfos = StrUtil.split(address, StrUtil.COLON);
hosts.add(new ServerAddress(addressInfos.get(0), Integer.parseInt(addressInfos.get(1))));
});
//2.初始化连接池参数
ConnectionPoolSettings poolSetting = ConnectionPoolSettings
.builder()
.maxWaitTime(10000, TimeUnit.MILLISECONDS)
.build();
//3.构造基础链接参数
MongoClientSettings.Builder settingBuilder = MongoClientSettings
.builder()
.applyToConnectionPoolSettings(builder -> builder.applySettings(poolSetting))
.applyToClusterSettings(builder -> builder.hosts(hosts))
.readPreference(ReadPreference.secondaryPreferred());
//4.初始链接参数以及连接池参数
MongoClientSettings settings;
//5.根据用户名是否为空判定是否鉴权
if (StrUtil.isNotBlank(properties.getUsername())) {
//6.添加授权参数
MongoCredential credential = MongoCredential.createScramSha1Credential(
properties.getUsername(), properties.getDatabase(), properties.getPassword().toCharArray()
);
//7.添加链接参数
settings = settingBuilder.credential(credential).build();
} else {
//7.添加链接参数
settings = settingBuilder.build();
}
//8.创建工厂返回
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), properties.getDatabase());
}
}
添加全局事务管理器
当一个方法中涉及到两个数据源的写操作,就需要指定事务管理器为全局事务管理器
需要注意的是,ChainedTransactionManager这个类现在过时了,但是我还没找到它的替代方法,所以目前先用它吧。
并且@Bean(name = “transactionManagerConfig”)中的”transactionManagerConfig”一定不能改名字!!!!!!!!
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
/**
* @author: 冯铁城 [17615007230@163.com]
* @date: 2022-07-29 16:53:06
* @describe:
*/
@Configuration
public class GlobalTransactionManager {
@Bean(name = "transactionManagerConfig")
public ChainedTransactionManager transactionManagerConfig(
@Qualifier("primaryMongoTransactionManager") PlatformTransactionManager ptm1,
@Qualifier("secondaryMongoTransactionManager") PlatformTransactionManager ptm2) {
return new ChainedTransactionManager(ptm1, ptm2);
}
}
方法添加对应的事务管理器
用到了那个数据源声明对应数据源的事务管理器,如果用到了多数据源,那就声明全局事务管理器
import com.ftc.mongotest.entity.Student;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* @author: 冯铁城 [17615007230@163.com]
* @date: 2022-07-29 14:15:54
* @describe: Student业务实现类
*/
@Service
public class StudentService {
@Resource
@Qualifier(value = "primaryTemplate")
private MongoTemplate primaryTemplate;
@Resource
@Qualifier(value = "secondaryTemplate")
private MongoTemplate secondaryTemplate;
/**
* 主数据源保存
*/
@Transactional(rollbackFor = Exception.class, transactionManager = "primaryMongoTransactionManager")
public void savePrimaryStudent() {
//1.创建对象
Student student = new Student();
student.setId(1);
student.setName("主数据源数据");
//2.保存
primaryTemplate.insert(student);
//3.执行异常
int error = 1 / 0;
}
/**
* 备数据源保存
*/
@Transactional(rollbackFor = Exception.class, transactionManager = "secondaryMongoTransactionManager")
public void saveSecondaryStudent() {
//1.创建对象
Student student = new Student();
student.setId(1);
student.setName("从数据源数据");
//2.保存
secondaryTemplate.insert(student);
//3.执行异常
int error = 1 / 0;
}
@Transactional(rollbackFor = Exception.class, transactionManager = "transactionManagerConfig")
public void saveStudentPrimaryAndSecondary(){
//1.创建主数据源对象
Student student = new Student();
student.setId(1);
student.setName("主数据源数据");
//2.主数据源保存
primaryTemplate.insert(student);
//3.创建从数据源对象
student = new Student();
student.setId(1);
student.setName("从数据源数据");
//4.从数据源保存
secondaryTemplate.insert(student);
//5.执行异常
int error = 1 / 0;
}
}
验证
- 当前两个数据源都没有数据
- 测试主数据源事务
如图,没有数据存入,事务回滚成功
- 测试被数据源事务
如图,没有数据存入,事务回滚成功
- 测试混合数据源事务
3.正则查询
使用Criteria.where().regex()即可
@Test
void testRegex() {
//1.定义存入集合
List<Student> students = CollUtil.newArrayList();
//2.循环封装数据
for (int i = 0; i < 10; i++) {
Student student = new Student();
student.setId(i);
student.setName("ftc" + i);
students.add(student);
}
//3.存入数据
primaryTemplate.insert(students, Student.class);
//4.正则查询校验
Student result = primaryTemplate.findOne(new Query(Criteria.where("name").regex("1")), Student.class);
Assert.isTrue("ftc1".equals(result.getName()));
//5.正则查询携带首选项校验
result = primaryTemplate.findOne(new Query(Criteria.where("name").regex("FTC2", "i")), Student.class);
Assert.isTrue("ftc2".equals(result.getName()));
}
4.MapReduce
与命令行一致,在javaApi中也需要声明map函数,reduce函数,以及对应的options,包括out,query,limit,sort等
查询年龄大于2的同学中,不同性别的人数
@Test
void mapReduce() {
//1.定义存入集合
List<Student> students = CollUtil.newArrayList();
//2.循环封装数据
for (int i = 0; i < 10; i++) {
Student student = new Student();
student.setId(i);
student.setName("ftc" + i);
student.setSex(i % 2 == 0 ? "男" : "女");
student.setAge(i);
students.add(student);
}
//3.测试数据存入集合
primaryTemplate.insert(students, Student.class);
//4.定义Map以及Reduce方法
String mapFunction = "function() { emit(this.sex,1); }";
String reduceFunction = "function(key, values) { return Array.sum(values)}";
//5.定义mapReduce并获取结果
List<JSONObject> mapReduceResult = primaryTemplate
.mapReduce(JSONObject.class)
.map(mapFunction)
.reduce(reduceFunction)
.inCollection("student")
.matching(new Query(Criteria.where("age").gt(2)))
.all();
//6.校验结果
String result = "[{\"_id\":\"女\",\"value\":4},{\"_id\":\"男\",\"value\":3}]";
Assert.isTrue(result.equals(JSONUtil.toJsonStr(mapReduceResult)));
}
查询年龄大于3且成绩排名前三的同学中,不同性别同学的名字集合
使用Query.limit()未生效,只有使用MapReduceOption.limit()生效,具体原因还未知
@Test
void mapReduce() {
//1.定义存入集合
List<Student> students = CollUtil.newArrayList();
//2.循环封装数据
for (int i = 0; i < 10; i++) {
Student student = new Student();
student.setId(i);
student.setName("ftc" + i);
student.setSex(i % 2 == 0 ? "男" : "女");
student.setAge(i);
student.setGrade((double) (i + 10));
students.add(student);
}
//3.测试数据存入集合
primaryTemplate.insert(students, Student.class);
//4.定义Map以及Reduce方法
String mapFunction = "function() { emit(this.sex,this.name); }";
String reduceFunction = "function(key, values) { return values.join('; ')}";
//5.定义集合过滤条件
Query query = new Query(Criteria.where("age").gt(3))
.with(Sort.by(Sort.Direction.DESC, "grade"));
//6.定义mapReduce并获取结果
List<JSONObject> mapReduceResult = primaryTemplate
.mapReduce(Student.class)
.map(mapFunction)
.reduce(reduceFunction)
.with(MapReduceOptions.options().limit(3))
.as(JSONObject.class)
.matching(query)
.all();
//7.校验结果
String result = "[{\"_id\":\"男\",\"value\":\"ftc8\"},{\"_id\":\"女\",\"value\":\"ftc7; ftc9\"}]";
Assert.isTrue(result.equals(JSONUtil.toJsonStr(mapReduceResult)));
}
5.GridFS
单数据源
操作模板引入
无需进行额外配置,直接引入即可
@Autowired
private GridFsTemplate gridFsTemplate;
put数据
其中metadata可以自定义配置,可以理解为对文件的备注,方便在后续进行查询,非必须步骤
@Test
void testPut() {
//1.获取文件输入流
BufferedInputStream inputStream = FileUtil.getInputStream("C:\\Users\\86176\\Desktop\\OIP-C.jpg");
//2.设置文件备注
HashMap<String, Object> metadata = MapUtil.newHashMap(2);
metadata.put("作者", "ftc");
metadata.put("作用", "学习使用");
//3.存入文件
ObjectId store = gridFsTemplate.store(inputStream, "test.jpg", metadata);
Assert.isTrue(ObjectUtil.isNotNull(store));
}
Get数据
其中IOUtil与FileUtil都使用了Hutool来操作
@Test
void testGet() throws IOException {
//1.获取文件
GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("metadata.作用").regex("学")));
//2.获取文件流
GridFsResource resource = gridFsTemplate.getResource(gridFSFile);
//3.文件写入
InputStream inputStream = resource.getInputStream();
byte[] bytes = IoUtil.readBytes(inputStream);
File file = FileUtil.writeBytes(bytes, "C:\\Users\\86176\\Desktop\\copy.jpg");
Assert.isTrue(file.exists());
}
List数据
@Test
void testList() {
//1.获取文件
GridFSFindIterable gridFSFiles = gridFsTemplate.find(new Query());
//2.循环遍历
for (GridFSFile gridFSFile : gridFSFiles) {
System.out.println(gridFSFile.getFilename());
}
}
delete数据
@Test
void testDelete() {
//1.删除文件
gridFsTemplate.delete(new Query());
//2.验证
GridFSFindIterable gridFSFiles = gridFsTemplate.find(new Query());
Assert.isTrue(CollUtil.isEmpty(gridFSFiles));
}
多数据源
操作模板引入
import cn.hutool.core.util.StrUtil;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ConnectionPoolSettings;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author: 冯铁城 [17615007230@163.com]
* @date: 2022-07-29 15:44:02
* @describe: mongo主数据源各种配置类
*/
@Configuration
@EnableMongoRepositories(basePackages = "com.ftc.mongotest", mongoTemplateRef = "primaryTemplate")
public class PrimaryMongoTemplate {
@Resource
@Qualifier("primaryMongoProperties")
private MongoConfigProperties primaryProperties;
@Primary
@Bean(name = "primaryTemplate")
public MongoTemplate primaryTemplate() {
//1.获取primaryTemplate
SimpleMongoClientDatabaseFactory primaryFactory = getFactory(this.primaryProperties);
MongoTemplate primaryTemplate = new MongoTemplate(primaryFactory);
//2.默认数据源监听处理
String type = "_class";
MongoConverter converter = primaryTemplate.getConverter();
if (converter.getTypeMapper().isTypeKey(type)) {
((MappingMongoConverter) converter).setTypeMapper(new DefaultMongoTypeMapper(null));
}
//3.返回
return primaryTemplate;
}
@Bean(name = "primaryMongoTransactionManager")
public MongoTransactionManager primaryTransactionManager() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.primaryProperties);
return new MongoTransactionManager(factory);
}
@Primary
@Bean(name = "primaryGridFsTemplate")
public GridFsTemplate primaryGridFsTemplate() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.primaryProperties);
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext());
return new GridFsTemplate(factory, converter);
}
@Primary
@Bean("primaryFactory")
public SimpleMongoClientDatabaseFactory getFactory(MongoConfigProperties properties) {
//1.设置链接地址
List<ServerAddress> hosts = new ArrayList<>();
properties.getAddress().forEach(address -> {
List<String> addressInfos = StrUtil.split(address, StrUtil.COLON);
hosts.add(new ServerAddress(addressInfos.get(0), Integer.parseInt(addressInfos.get(1))));
});
//2.初始化连接池参数
ConnectionPoolSettings poolSetting = ConnectionPoolSettings
.builder()
.maxWaitTime(10000, TimeUnit.MILLISECONDS)
.build();
//3.构造基础链接参数
MongoClientSettings.Builder settingBuilder = MongoClientSettings
.builder()
.applyToConnectionPoolSettings(builder -> builder.applySettings(poolSetting))
.applyToClusterSettings(builder -> builder.hosts(hosts))
.readPreference(ReadPreference.secondaryPreferred());
//4.初始链接参数以及连接池参数
MongoClientSettings settings;
//5.根据用户名是否为空判定是否鉴权
if (StrUtil.isNotBlank(properties.getUsername())) {
//6.添加授权参数
MongoCredential credential = MongoCredential.createScramSha1Credential(
properties.getUsername(), properties.getDatabase(), properties.getPassword().toCharArray()
);
//7.添加链接参数
settings = settingBuilder.credential(credential).build();
} else {
//7.添加链接参数
settings = settingBuilder.build();
}
//8.创建工厂返回
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), properties.getDatabase());
}
}
import cn.hutool.core.util.StrUtil;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ConnectionPoolSettings;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author: 冯铁城 [17615007230@163.com]
* @date: 2022-07-29 15:44:02
* @describe: mongo主数据源各种配置类
*/
@Configuration
@EnableMongoRepositories(basePackages = "com.ftc.mongotest", mongoTemplateRef = "secondaryTemplate")
public class SecondaryMongoTemplate {
@Resource
@Qualifier("secondaryMongoProperties")
private MongoConfigProperties secondaryProperties;
@Bean(name = "secondaryTemplate")
public MongoTemplate secondaryTemplate() {
//1.获取secondaryTemplate
SimpleMongoClientDatabaseFactory secondaryFactory = getFactory(this.secondaryProperties);
MongoTemplate secondaryTemplate = new MongoTemplate(secondaryFactory);
//2.默认数据源监听处理
String type = "_class";
MongoConverter converter = secondaryTemplate.getConverter();
if (converter.getTypeMapper().isTypeKey(type)) {
((MappingMongoConverter) converter).setTypeMapper(new DefaultMongoTypeMapper(null));
}
//3.返回
return secondaryTemplate;
}
@Bean(name = "secondaryMongoTransactionManager")
public MongoTransactionManager secondaryTransactionManager() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.secondaryProperties);
return new MongoTransactionManager(factory);
}
@Bean(name = "secondaryGridFsTemplate")
public GridFsTemplate secondaryGridFsTemplate() {
SimpleMongoClientDatabaseFactory factory = getFactory(this.secondaryProperties);
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext());
return new GridFsTemplate(factory, converter);
}
@Bean("secondaryFactory")
public SimpleMongoClientDatabaseFactory getFactory(MongoConfigProperties properties) {
//1.设置链接地址
List<ServerAddress> hosts = new ArrayList<>();
properties.getAddress().forEach(address -> {
List<String> addressInfos = StrUtil.split(address, StrUtil.COLON);
hosts.add(new ServerAddress(addressInfos.get(0), Integer.parseInt(addressInfos.get(1))));
});
//2.初始化连接池参数
ConnectionPoolSettings poolSetting = ConnectionPoolSettings
.builder()
.maxWaitTime(10000, TimeUnit.MILLISECONDS)
.build();
//3.构造基础链接参数
MongoClientSettings.Builder settingBuilder = MongoClientSettings
.builder()
.applyToConnectionPoolSettings(builder -> builder.applySettings(poolSetting))
.applyToClusterSettings(builder -> builder.hosts(hosts))
.readPreference(ReadPreference.secondaryPreferred());
//4.初始链接参数以及连接池参数
MongoClientSettings settings;
//5.根据用户名是否为空判定是否鉴权
if (StrUtil.isNotBlank(properties.getUsername())) {
//6.添加授权参数
MongoCredential credential = MongoCredential.createScramSha1Credential(
properties.getUsername(), properties.getDatabase(), properties.getPassword().toCharArray()
);
//7.添加链接参数
settings = settingBuilder.credential(credential).build();
} else {
//7.添加链接参数
settings = settingBuilder.build();
}
//8.创建工厂返回
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), properties.getDatabase());
}
}
Put数据
@Resource
@Qualifier("primaryGridFsTemplate")
private GridFsTemplate primaryGridFsTemplate;
@Resource
@Qualifier("secondaryGridFsTemplate")
private GridFsTemplate secondaryGridFsTemplate;
@Test
void testPut() {
//1.获取文件输入流
BufferedInputStream inputStream = FileUtil.getInputStream("C:\\Users\\86176\\Desktop\\OIP-C.jpg");
//2.设置文件备注
HashMap<String, Object> metadata = MapUtil.newHashMap(2);
metadata.put("作者", "ftc");
metadata.put("作用", "学习使用");
//3.主数据源存入文件
ObjectId store = primaryGridFsTemplate.store(inputStream, "testPrimary.jpg", metadata);
Assert.isTrue(ObjectUtil.isNotNull(store));
//4.获取文件输入流
inputStream = FileUtil.getInputStream("C:\\Users\\86176\\Desktop\\OIP-C.jpg");
//5.设置文件备注
metadata = MapUtil.newHashMap(2);
metadata.put("作者", "ftc");
metadata.put("作用", "学习使用");
//4.备数据源存入文件
store = secondaryGridFsTemplate.store(inputStream, "testSecondary.jpg", metadata);
Assert.isTrue(ObjectUtil.isNotNull(store));
}
Get数据
void testGet() throws IOException {
//1.主数据源获取文件
GridFSFile gridFSFile = primaryGridFsTemplate.findOne(new Query(Criteria.where("metadata.作用").regex("学")));
//2.主数据源获取文件流
GridFsResource resource = primaryGridFsTemplate.getResource(gridFSFile);
//3.主数据源文件写入
InputStream inputStream = resource.getInputStream();
byte[] bytes = IoUtil.readBytes(inputStream);
File file = FileUtil.writeBytes(bytes, "C:\\Users\\86176\\Desktop\\copyPrimary.jpg");
Assert.isTrue(file.exists());
//4.从数据源获取文件
gridFSFile = secondaryGridFsTemplate.findOne(new Query(Criteria.where("metadata.作用").regex("学")));
//5.从数据源获取文件流
resource = secondaryGridFsTemplate.getResource(gridFSFile);
//6.从数据源文件写入
inputStream = resource.getInputStream();
bytes = IoUtil.readBytes(inputStream);
file = FileUtil.writeBytes(bytes, "C:\\Users\\86176\\Desktop\\copySecondary.jpg");
Assert.isTrue(file.exists());
}
List数据
@Test
void testList() {
//1.主数据源获取文件
GridFSFindIterable gridFSFiles = primaryGridFsTemplate.find(new Query());
//2.循环遍历
for (GridFSFile gridFSFile : gridFSFiles) {
System.out.println(gridFSFile.getFilename());
}
//3.备数据源获取文件
gridFSFiles = secondaryGridFsTemplate.find(new Query());
//4.循环遍历
for (GridFSFile gridFSFile : gridFSFiles) {
System.out.println(gridFSFile.getFilename());
}
}
Delete数据
@Test
void testDelete() {
//1.主数据源删除文件
primaryGridFsTemplate.delete(new Query());
//2.验证
GridFSFindIterable gridFSFiles = primaryGridFsTemplate.find(new Query());
Assert.isTrue(CollUtil.isEmpty(gridFSFiles));
//3.备数据源删除文件
secondaryGridFsTemplate.delete(new Query());
//4.验证
gridFSFiles = secondaryGridFsTemplate.find(new Query());
Assert.isTrue(CollUtil.isEmpty(gridFSFiles));
}