Spring操作MongoDB数据库

Mongodb是一个文档型NoSQL数据库,大致有Database,Collection,Document这几个概念组成,他们之间的关系下面这张图很清晰

NewImage

很多Document组成了一个Collection,而Database里面有很多的Collection

而Spring对MongoDB是通过Spring Data MongoDB来支持,对比Spring Dota JPA,Spring Data MongoDB也可以通过MongoTemplate来操作MongoDB数据库进行CRUD

 

本地起一个MongoDB,创建一个lihuia的Database,创建一个user_center的Collection,插入三条Document

> db.createCollection("user_center")
{ "ok" : 1 }
> db.user_center.insert({"name":"lihui", "age":NumberInt(18), "phone":"17777777777"})
WriteResult({ "nInserted" : 1 })
> db.user_center.insert({"name":"lilei", "age":NumberInt(19), "phone":"18888888888"})
WriteResult({ "nInserted" : 1 })
> db.user_center.insert({"name":"lucy", "age":NumberInt(16), "phone":"16666666666"})
WriteResult({ "nInserted" : 1 })
>
> db.user_center.find({}).pretty()
{
	"_id" : ObjectId("5dc420cc7967a093aafc1edd"),
	"name" : "lihui",
	"age" : 18,
	"phone" : "17777777777"
}
{
	"_id" : ObjectId("5dc420e17967a093aafc1ede"),
	"name" : "lilei",
	"age" : 19,
	"phone" : "18888888888"
}
{
	"_id" : ObjectId("5dc420f27967a093aafc1edf"),
	"name" : "lucy",
	"age" : 16,
	"phone" : "16666666666"
}
> show dbs
admin   0.000GB
lihuia  0.000GB
local   0.000GB

 

要通过 Spring来进行访问,首先添加两个依赖

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.11.2</version>
</dependency>

定义一个User类,和Document映射,其中@Document注解和MongoDB里的文档对应映射关系,同时指定Collection为user_center,这里重写toString方法,方便打印

package com.lihuia.mongo;

import lombok.Builder;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
* Copyright (C), 2018-2019
* FileName: User
* Author: lihui
* Date: 2019/11/7
*/

@Document(collection = "user_center")
@Builder
public class User {

@Id
private String id;
private String name;
private Integer age;
private String phone;

@Override
public String toString() {
return "User: id=" + id + ", name=" + name + ", age=" + age + ", phone=" + phone;
}
}

接着就是持久层MongoDB连接,JPA相关的,还是直接通过Spring的MongoTemplate来操作

可以简单看下spring.io官方说明

NewImage

想要注入mongoTemplate,只需要依赖一个mongoClient的注入,主要是MongoDB连接的相关信息

但是要注意的是这里的MongoClient是来自mongo-java-driver的,所以pom里才需要新增mongo-driver的依赖

可是源码里,MongoTemplate传入mongoclient和database构造方法已经备注为Deprecated了

/**
* Constructor used for a basic template configuration.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @deprecated since 2.2 in favor of {@link #MongoTemplate(com.mongodb.client.MongoClient, String)}.
*/
@Deprecated
public MongoTemplate(MongoClient mongoClient, String databaseName) {
this(new SimpleMongoDbFactory(mongoClient, databaseName), (MongoConverter) null);
}

因此可以换下面一个试试,传入了一个MongoDbFactory接口

/**
* Constructor used for a basic template configuration.
*
* @param mongoDbFactory must not be {@literal null}.
*/
public MongoTemplate(MongoDbFactory mongoDbFactory) {
this(mongoDbFactory, (MongoConverter) null);
}

那么可以找下这个接口的实现类,如果有传入MongoClient实例的构造方法,那么也能够达到目的,不出所料,找到了一个SimpleMongoClientDbFactory类

public class SimpleMongoClientDbFactory extends MongoDbFactorySupport<MongoClient> implements DisposableBean {

它继承的基类MongoDbFactorySupport是实现了MongoDbFactory接口

public abstract class MongoDbFactorySupport<C> implements MongoDbFactory {

找到一个注入MongoClient的构造方法

/**
* Creates a new {@link SimpleMongoClientDbFactory} instance from the given {@link MongoClient}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
*/
public SimpleMongoClientDbFactory(MongoClient mongoClient, String databaseName) {
this(mongoClient, databaseName, false);
}

传入的MongoClient是个接口

 * <p>
* Instances of this class serve as factories for {@link MongoDatabase} instances.
* </p>
* <p>
* Instances of this class can be created via the {@link MongoClients} factory.
* </p>
* @see MongoClients
* @since 3.7
*/
@Immutable
public interface MongoClient extends Closeable {

在接口MongoClient上面注释上清楚地写着MongoClients可以实例化,于是看看这个类

/**
* A factory for {@link MongoClient} instances. Use of this class is now the recommended way to connect to MongoDB via the Java driver.
*
* @see MongoClient
* @since 3.7
*/
public final class MongoClients {

/**
* Creates a new client with the default connection string "mongodb://localhost".
*
* @return the client
*/
public static MongoClient create() {
return create(new ConnectionString("mongodb://localhost"));
}

终于发现了新大路,不出意外这个create()肯定是接口实现类进行实例化,它调用了下面

 * @param connectionString the settings
* @return the client
* @throws IllegalArgumentException if the connection string's stream type is not one of "netty" or "nio2"
*
* @see com.mongodb.MongoClientSettings.Builder#applyConnectionString(ConnectionString)
*/
public static MongoClient create(final ConnectionString connectionString) {
return create(connectionString, null);
}

不用管,继续看调用关系

/**
* Create a new client with the given connection string.
*
* <p>Note: Intended for driver and library authors to associate extra driver metadata with the connections.</p>
*
* @param connectionString the settings
* @param mongoDriverInformation any driver information to associate with the MongoClient
* @return the client
* @throws IllegalArgumentException if the connection string's stream type is not one of "netty" or "nio2"
* @see MongoClients#create(ConnectionString)
*/
public static MongoClient create(final ConnectionString connectionString,
@Nullable final MongoDriverInformation mongoDriverInformation) {
return create(MongoClientSettings.builder().applyConnectionString(connectionString).build(), mongoDriverInformation);
}

可以看到下面,最终是实例化了MongoClientImpl

/**
* Creates a new client with the given client settings.
*
* <p>Note: Intended for driver and library authors to associate extra driver metadata with the connections.</p>
*
* @param settings the settings
* @param mongoDriverInformation any driver information to associate with the MongoClient
* @return the client
*/
public static MongoClient create(final MongoClientSettings settings, @Nullable final MongoDriverInformation mongoDriverInformation) {
MongoDriverInformation.Builder builder = mongoDriverInformation == null ? MongoDriverInformation.builder()
: MongoDriverInformation.builder(mongoDriverInformation);
return new MongoClientImpl(settings, builder.driverName("sync").build());
}

 

这样com.mongodb.client.MongoClient的实例可以创建,依次注入即可

package com.lihuia.mongo;

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.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

/**
* Copyright (C), 2018-2019
* FileName: MongoConfig
* Author: lihui
* Date: 2019/11/7
*/

@Configuration
@EnableMongoRepositories(basePackages = "com.lihuia.mongo")
public class MongoConfig {

@Bean
public MongoClient mongoClient() {
return MongoClients.create();
}

@Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoClientDbFactory(mongoClient(), "lihuia");
}

@Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
}

还有一种方法,不需要Factory过渡,在MongoTemplate里可以找到一个传入com.mongodb.client.MongoClient实例的构造方法

/**
* Constructor used for a basic template configuration.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @since 2.1
*/
public MongoTemplate(com.mongodb.client.MongoClient mongoClient, String databaseName) {
this(new SimpleMongoClientDbFactory(mongoClient, databaseName), (MongoConverter) null);
}

如此一来就简单了,Mongo相关配置类注入如下也是可以的

@Configuration
@EnableMongoRepositories(basePackages = "com.lihuia.mongo")
public class MongoConfig {

@Bean
public MongoClient mongoClient() {
return MongoClients.create();
}

@Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), "lihuia");
}
}

加了一个@EnableMongoRepositories注解,开启了Spring Data的MongoDB的Repository功能,指定包

 

最后就是测试类,数据库的具体操作就比较简单了,比如查询

package com.lihuia.mongo;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: UserTest
* Author: lihui
* Date: 2019/11/7
*/


@ContextConfiguration(classes = {MongoConfig.class})
@Slf4j
public class UserTest extends AbstractTestNGSpringContextTests {

@Resource
private MongoTemplate mongoTemplate;

@Test
public void mongodbTest() {
Criteria where = Criteria.where("name").is("lihui");
Query query = Query.query(where);

log.info(mongoTemplate.findOne(query, User.class).toString());
}
}

执行结果就是

[main] INFO com.lihuia.mongo.UserTest - User: id=5dc420cc7967a093aafc1edd, name=lihui, age=18, phone=17777777777

 

其它CRUD接口可以查看MongoTemplate即可

package com.lihuia.mongo;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;

/**
* Copyright (C), 2018-2019
* FileName: UserTest
* Author: lihui
* Date: 2019/11/7
*/


@ContextConfiguration(classes = {MongoConfig.class})
@Slf4j
public class UserTest extends AbstractTestNGSpringContextTests {

@Resource
private MongoTemplate mongoTemplate;

@Test
public void mongodbTest() {
String collectionName = "user_center";
log.info("清理Collection...");
mongoTemplate.dropCollection(collectionName);
log.info("创建Collection...");
mongoTemplate.createCollection(collectionName);
log.info("插入Document...");
mongoTemplate.insert(User.builder().name("lihui").age(18).phone("17777777777").build(), collectionName);
mongoTemplate.insert(User.builder().name("lilei").age(19).phone("18888888888").build(), collectionName);
mongoTemplate.insert(User.builder().name("lucy").age(16).phone("16666666666").build(), collectionName);
log.info("插入后查询所有:");
mongoTemplate.findAll(User.class, collectionName).forEach(e->log.info(e.toString()));
log.info("删除lihui这条document...");
mongoTemplate.remove(Query.query(Criteria.where("name").is("lihui")), collectionName);
log.info("删除后查询所有:");
mongoTemplate.findAll(User.class, collectionName).forEach(e->log.info(e.toString()));
log.info("修改lucy年龄...");
mongoTemplate.updateFirst(Query.query(Criteria.where("name").is("lucy")),
Update.update("age", 19), collectionName);
log.info("修改lucy的年龄,修改后查询所有:");
mongoTemplate.findAll(User.class, collectionName).forEach(e->log.info(e.toString()));
log.info("修改年龄为19岁的手机号...");
mongoTemplate.updateMulti(Query.query(Criteria.where("age").is(19)),
Update.update("phone", "13333333333"), collectionName
);
log.info("年龄为19修改手机号后,查询最终结果:");
mongoTemplate.findAll(User.class, collectionName).forEach(e->log.info(e.toString()));
}
}

执行结果:

[main] INFO com.lihuia.mongo.UserTest - 清理Collection...
[connectionId{localValue:2, serverValue:5}] to localhost:27017
[main] INFO com.lihuia.mongo.UserTest - 创建Collection...
[main] INFO com.lihuia.mongo.UserTest - 插入Document...
[main] INFO com.lihuia.mongo.UserTest - 插入后查询所有:
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a2, name=lihui, age=18, phone=17777777777
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a3, name=lilei, age=19, phone=18888888888
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a4, name=lucy, age=16, phone=16666666666
[main] INFO com.lihuia.mongo.UserTest - 删除lihui这条document...
[main] INFO com.lihuia.mongo.UserTest - 删除后查询所有:
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a3, name=lilei, age=19, phone=18888888888
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a4, name=lucy, age=16, phone=16666666666
[main] INFO com.lihuia.mongo.UserTest - 修改lucy年龄...
[main] INFO com.lihuia.mongo.UserTest - 修改lucy的年龄,修改后查询所有:
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a3, name=lilei, age=19, phone=18888888888
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a4, name=lucy, age=19, phone=16666666666
[main] INFO com.lihuia.mongo.UserTest - 修改年龄为19岁的手机号...
[main] INFO com.lihuia.mongo.UserTest - 年龄为19修改手机号后,查询最终结果:
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a3, name=lilei, age=19, phone=13333333333
[main] INFO com.lihuia.mongo.UserTest - User: id=5dc5608aae26b17144bd32a4, name=lucy, age=19, phone=13333333333

OVER

发表回复