Mongodb是一个文档型NoSQL数据库,大致有Database,Collection,Document这几个概念组成,他们之间的关系下面这张图很清晰
很多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官方说明
想要注入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