前面章节分别介绍了怎样基于 Java 和 XML 去创建 Mongo 的实例。下面将介绍怎样获取 MongoDB 数据库对象 MongoDatabase。
虽然 com.mongodb.client.MongoClient 是 MongoDB 驱动程序 API 的入口,但连接到特定的 MongoDB 数据库实例需要额外的信息,例如数据库名称以及可选的用户名和密码。
有了这些信息,你就可以获得 com.mongodb.client.MongoDatabase 对象并访问特定 MongoDB 数据库实例的所有功能。
Spring 提供了 org.springframework.data.mongodb.core.MongoDatabaseFactory 接口,用于创建与 MongoDB 数据库的连接。它的定义如下:
public interface MongoDatabaseFactory {
// 从底层工厂获取一个 MongoDatabase
MongoDatabase getDatabase() throws DataAccessException;
// 使用给定的 dbName 从底层工厂获取一个 MongoDatabase
MongoDatabase getDatabase(String dbName) throws DataAccessException;
//...
}下面将介绍如何使用基于 Java 或基于 XML 的方式来配置容器 MongoDatabaseFactory 接口的实例。反过来,你还可以使用 MongoDatabaseFactory 实例来配置 MongoTemplate。
与其使用 IoC 容器来创建 MongoTemplate 的实例,你可以在标准 Java 代码中使用它们,如下所示:
package com.hxstrive.springdata.mongodb.demo;
import com.mongodb.client.MongoClients;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Criteria.where;
/**
* 在标准 Java 中使用 MongoTemplate
* @author hxstrive.com 2022/12/22
*/
public class MongoApp {
public static void main(String[] args) {
MongoOperations mongoOps = new MongoTemplate(
new SimpleMongoClientDatabaseFactory(MongoClients.create(), "test"));
// 插入一条记录
mongoOps.insert(User.builder().id(100).name("Tom").build());
// 查询 name 为 Tom 的用户信息
User user = mongoOps.findOne(new Query(where("name").is("Tom")), User.class);
System.out.println(user);
// 删除集合
mongoOps.dropCollection("user");
}
@ToString
@Data
@Builder
static class User {
private int id;
private String name;
}
}在上面代码中,我们使用了 SimpleMongoClientDbFactory 工厂类,它是 MongoDatabaseFactory 的实现,如下:
// 抽象类,MongoDatabaseFactory 的抽象实现
public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFactory {}
// SimpleMongoClientDatabaseFactory 的实现
public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySupport<MongoClient>
implements DisposableBean {}注意:选择 com.mongodb.client.MongoClient 作为入口点时,请使用 SimpleMongoClientDbFactory 工厂类。
你可以通过基于 Java 的 @Configuration 配置类在容器中注册一个 MongoDatabaseFactory 实例,代码如下:
package com.hxstrive.springdata.mongodb.config;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoClientFactoryBean;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
/**
* 配置类
* @author hxstrive.com 2022/12/21
*/
@Configuration
public class BaseJavaConfig {
/**
* 注册一个 MongoDatabaseFactory
* @return
*/
@Bean
public MongoDatabaseFactory mongoDatabaseFactory() {
return new SimpleMongoClientDatabaseFactory(MongoClients.create(), "test");
}
}创建一个客户端,验证我们注册的 MongoDatabaseFactory 是否可用,代码如下:
package com.hxstrive.springdata.mongodb;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Criteria.where;
/**
* 使用注册的 MongoDatabaseFactory
* @author hxstrive.com 2022/12/22
*/
@SpringBootTest
public class MongoDatabaseFactoryTest {
@Autowired
private MongoDatabaseFactory mongoDatabaseFactory;
@Test
void contextLoads() {
MongoOperations mongoOps = new MongoTemplate(mongoDatabaseFactory);
// 插入一条记录
mongoOps.insert(User.builder().id(100).name("Helen").build());
// 查询 name 为 Tom 的用户信息
User user = mongoOps.findOne(new Query(where("name").is("Helen")), User.class);
System.out.println(user);
// 删除集合
mongoOps.dropCollection("user");
}
@ToString
@Data
@Builder
static class User {
private int id;
private String name;
}
}注意:MongoDB 服务器第三代改变了连接到数据库时的认证模式。因此,一些可用于身份验证的配置选项已不再有效。你应该使用 MongoClient 特有的选项,通过 MongoCredential 设置证书,以提供数据库认证数据。如下例所示:
package com.hxstrive.springdata.mongodb.config;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
/**
* MongoDB 授权信息配置
* @author hxstrive.com 2022/12/22
*/
@Configuration
public class MongoAuthConfig extends AbstractMongoClientConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(MongoAuthConfig.class);
@Override
public String getDatabaseName() {
LOG.info("getDatabaseName()");
return "test";
}
@Override
protected void configureClientSettings(MongoClientSettings.Builder builder) {
LOG.info("configureClientSettings(MongoClientSettings.Builder builder)");
// 设置用户名、数据库、密码信息
builder.credential(MongoCredential.createCredential(
"hxstrive", "test", "aaaaaa".toCharArray()))
.applyConnectionString(new ConnectionString("mongodb://127.0.0.1:27017"));
}
}编写客户端,测试上面的授权信息是否可用。代码如下:
package com.hxstrive.springdata.mongodb;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Criteria.where;
/**
* 测试通过继承 AbstractMongoClientConfiguration 为 Mongo 提供授权信息
* @author hxstrive.com 2022/12/22
*/
@SpringBootTest
public class MongoAuthConfigTest {
@Autowired
private MongoDatabaseFactory mongoDbFactory;
@Test
void contextLoads() {
MongoOperations mongoOps = new MongoTemplate(mongoDbFactory);
// 插入一条记录
mongoOps.insert(User.builder().id(100).name("Helen").build());
// 查询 name 为 Tom 的用户信息
User user = mongoOps.findOne(new Query(where("name").is("Helen")), User.class);
System.out.println(user);
// 删除集合
mongoOps.dropCollection("user");
}
@ToString
@Data
@Builder
static class User {
private int id;
private String name;
}
}注意,在执行客户端代码之前,你需要开启 MongoDB 的权限校验和创建用户。
重点:上面的配置信息是怎样帮我们设置到 Mongo 连接中的呢?部分源码片段如下:
// MongoAuthConfigTest -> AbstractMongoClientConfiguration -> MongoConfigurationSupport
@Configuration(proxyBeanMethods = false)
public abstract class AbstractMongoClientConfiguration extends MongoConfigurationSupport {
// 创建 Mongo
public MongoClient mongoClient() {
// mongoClientSettings() 这个方法在父类中是一个抽象方法,由我们去实现
return createMongoClient(mongoClientSettings());
}
// 启示可以直接使用 MongoTemplate,他帮我们创建好了
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory databaseFactory, MappingMongoConverter converter) {
return new MongoTemplate(databaseFactory, converter);
}
// 我们需要使用到的工厂
@Bean
public MongoDatabaseFactory mongoDbFactory() {
// getDatabaseName() 方法在父类中是一个抽象方法,由我们去实现
return new SimpleMongoClientDatabaseFactory(mongoClient(), getDatabaseName());
}
//...
protected MongoClient createMongoClient(MongoClientSettings settings) {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
}
}
public abstract class MongoConfigurationSupport {
// 我们自己实现
protected abstract String getDatabaseName();
//...
// 被子类 AbstractMongoClientConfiguration 的 mongoClient() 方法调用
protected MongoClientSettings mongoClientSettings() {
MongoClientSettings.Builder builder = MongoClientSettings.builder();
builder.uuidRepresentation(UuidRepresentation.JAVA_LEGACY);
configureClientSettings(builder);
return builder.build();
}
// 我们自己实现
protected void configureClientSettings(MongoClientSettings.Builder builder) {
// customization hook
}
}如果我们的 MongoDB 配置信息在外部属性文件中,可以通过 @PropertySource 注解导入,并且还能通过 builder.applyToConnectionPoolSettings() 去配置 Mongo 连接池等信息,如下:
@Configuration
@PropertySource("classpath:/com/myapp/mongodb/config/mongo.properties")
public class ApplicationContextEventTestsAppConfig extends AbstractMongoClientConfiguration {
@Autowired
private Environment env;
@Override
public String getDatabaseName() {
return "database";
}
@Override
protected void configureClientSettings(Builder builder) {
builder.applyToClusterSettings(settings -> {
settings.hosts(
singletonList(
new ServerAddress(
env.getProperty("mongo.host"),
env.getProperty("mongo.port", Integer.class)
)
)
);
});
builder.applyToConnectionPoolSettings(settings -> {
settings.maxConnectionLifeTime(
env.getProperty("mongo.pool-max-life-time", Integer.class),
TimeUnit.MILLISECONDS
)
.minSize(env.getProperty("mongo.pool-min-size", Integer.class))
.maxSize(env.getProperty("mongo.pool-max-size", Integer.class))
.maintenanceFrequency(10, TimeUnit.MILLISECONDS)
.maintenanceInitialDelay(11, TimeUnit.MILLISECONDS)
.maxConnectionIdleTime(30, TimeUnit.SECONDS)
.maxWaitTime(15, TimeUnit.MILLISECONDS);
});
}
}上面介绍了基于 Java 来注册 MongoDatabaseFactory 工厂,当然,我们也可以通过 XML 文件进行注册,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/mongo
https://www.springframework.org/schema/data/mongo/spring-mongo.xsd">
<!-- 配置 MongoDatabaseFactory -->
<!-- ${mongo.username}:${mongo.password}@${mongo.dbname} -->
<mongo:mongo-client id="mongoClient" host="127.0.0.1" port="27017" credential="hxstrive:aaaaaa@test">
<!-- 在 com.mongodb.client.MongoClient 实例上配置额外的选项 -->
<mongo:client-settings
connection-pool-max-connection-life-time="120"
connection-pool-min-size="5"
connection-pool-max-size="20"
connection-pool-maintenance-frequency="10"
connection-pool-maintenance-initial-delay="11"
connection-pool-max-connection-idle-time="30"
connection-pool-max-wait-time="15" />
</mongo:mongo-client>
<mongo:db-factory id="mongoDatabaseFactory" dbname="test" mongo-client-ref="mongoClient"/>
<!-- 配置自己的 MongoTempalte -->
<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDatabaseFactory"/>
</bean>
</beans>此时,我们就可以这样去使用它们,如下:
@Autowired private MongoDatabaseFactory mongoDatabaseFactory; @Autowired private MongoTemplate anotherMongoTemplate; @Autowired private MongoClient mongoClient;
注意:在基于 XML 的配置中,使用的用户名和密码凭证,如果包含保留字符(如:, %, @, 或空格),必须进行 URL 编码。例如:
原始密码:
m0ng0@dmin:mo_res:bw6},Qsdxx@admin@database
编码后的密码:
m0ng0%40dmin:mo_res%3Abw6%7D%2CQsdxx%40admin@database
注意:用户名中的 @ 被编码成 %40,密码中的 “:” 被编码成 “%3A”,“}” 被编码成 “%2C”,“,” 被编码成 “%7D”,更多关于编码细节,你可以参见 RFC 3986 文档的 2.2节。