目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Spring for MongoDB

常见数据库分类

ClassificationName
Key-ValueRedis、Memcached
DocumentMongoDB、CouchDB
ColumnarHBase、Cassandra
GraphNeo4J

Docker部署MongoDB

# 拉取image
[root@master ~]# docker pull mongo
[root@master ~]# docker images | grep mongo
mongo	latest	a0e2e64ac939	3 weeks ago	64MB

# 创建本地挂载目录
[root@master ~]# mkdir /data/mongodb -pv

# 创建容器
[root@master ~]# docker run --name mongo -v /data/mongod:/data/db -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo
0503c6157ef3b7e7cea96be9d193567543cfb52698ca294f10bb45ac886ddb28

# 登录到MongoDB容器中
[root@master ~]# docker exec -it mongo bash

# 通过 Shell 连接 MongoDB
root@752473287d58:/# mongo -u admin -p 123456
---
> 

Spring 对 MongoDB的支持

 Spring Data 的目标就是在不失各个数据库特性的基础上增加一层相对统一的封装和抽象。
 通过Spring Data MongoDB 项目。也提供了和JdbcTemplate类似的MongoTemplate,通过MongoTemplate对数据进行CRUD。同时Spring Data MongoDB 也提供了像 JPA Repository这样的Repository的支持。

注解

  • @Document:作用类似于@Entity
  • @Id:文档的Id

MongoTemplate

  • save / remove
  • Update / Query / Criteria

MongoTemplate

初始化MongoDB的库及权限

# 创建库
use springbucks;

# 创建用户
db.createUser(
	{
		user: "springbucks",
		pwd: "springbucks",
		roles: [
			{ role: "readWrite", db: "springbucks" }
		]
	}
)

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>geektime.spring.data</groupId>
	<artifactId>mongo-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>mongo-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb</artifactId>
		</dependency>

		<dependency>
			<groupId>org.joda</groupId>
			<artifactId>joda-money</artifactId>
			<version>RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

application.properties

spring.data.mongodb.uri=mongodb://springbucks:springbucks@192.168.31.201:27017/springbucks
spring.data.mongodb.username=admin
spring.data.mongodb.password=123456

Coffee 实体类

package geektime.spring.data.mongodemo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.joda.money.Money;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;

@Document //表明和 Coffee collection是对应的
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Coffee {
    @Id //会自动转换成MongoDB中的object id     org.springframework.data.annotation.Id;
    private String id;
    private String name;
    private Money price;
    private Date createTime;
    private Date updateTime;
}

MoneyReadConverter 类型转换处理类

package geektime.spring.data.mongodemo.converter;

import org.bson.Document;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.core.convert.converter.Converter;

public class MoneyReadConverter implements Converter<Document, Money> {
    /**
     * 类型转换:用于处理 Document => Money
     * @param source
     * @return
     */
    @Override
    public Money convert(Document source) {
        Document money = (Document) source.get("money");
        double amount = Double.parseDouble(money.getString("amount"));
        String currency = ((Document) money.get("currency")).getString("code");
        return Money.of(CurrencyUnit.of(currency), amount);
    }
}

启动类

package geektime.spring.data.mongodemo;

import com.mongodb.client.result.UpdateResult;
import geektime.spring.data.mongodemo.converter.MoneyReadConverter;
import geektime.spring.data.mongodemo.model.Coffee;
import lombok.extern.slf4j.Slf4j;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
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 java.util.Arrays;
import java.util.Date;
import java.util.List;

import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

@SpringBootApplication
@Slf4j
public class MongoDemoApplication implements ApplicationRunner {
	@Autowired
	private MongoTemplate mongoTemplate;

	public static void main(String[] args) {
		SpringApplication.run(MongoDemoApplication.class, args);
	}

	/**
	 * 注入 MongoCustomConversions  用于类型转换
	 * @return
	 */
	@Bean
	public MongoCustomConversions mongoCustomConversions() {
		return new MongoCustomConversions(Arrays.asList(new MoneyReadConverter()));
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		//save : espresso
		Coffee espresso = Coffee.builder()
				.name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.createTime(new Date())
				.updateTime(new Date()).build();
		Coffee saved = mongoTemplate.save(espresso);
		log.info("Coffee {}", saved);
		//Coffee Coffee(id=5e1c1985a99c044c4825a017, name=espresso, price=CNY 20.00, createTime=Mon Jan 13 15:17:25 CST 2020, updateTime=Mon Jan 13 15:17:25 CST 2020)

		//Query
		List<Coffee> list = mongoTemplate.find(
				Query.query(Criteria.where("name").is("espresso")), Coffee.class);
		log.info("Find {} Coffee", list.size());
		//Find 1 Coffee
		list.forEach(c -> log.info("Coffee {}", c));
		//Coffee Coffee(id=5e1c1985a99c044c4825a017, name=espresso, price=CNY 20.00, createTime=Mon Jan 13 15:17:25 CST 2020, updateTime=Mon Jan 13 15:17:25 CST 2020)


		//update
		Thread.sleep(1000); // 为了看更新时间
		UpdateResult result = mongoTemplate.updateFirst(query(where("name").is("espresso")),
				new Update().set("price", Money.ofMajor(CurrencyUnit.of("CNY"), 30))
						.currentDate("updateTime"),
				Coffee.class);
		log.info("Update Result: {}", result.getModifiedCount());
		//Update Result: 1

		//findById
		Coffee updateOne = mongoTemplate.findById(saved.getId(), Coffee.class);
		log.info("Update Result: {}", updateOne);
		//Update Result: Coffee(id=5e1c214ba99c04325c0ae2b4, name=espresso, price=CNY 30.00, createTime=Mon Jan 13 15:50:35 CST 2020, updateTime=Mon Jan 13 15:50:37 CST 2020)
		
		//remove
		mongoTemplate.remove(updateOne);
	}
}

登录容器中查看保存的文档信息

> use springbucks
switched to db springbucks
> show collections;
coffee
> db.coffee.find();
{ "_id" : ObjectId("5e1c1985a99c044c4825a017"), "name" : "espresso", "price" : { "money" : { "currency" : { "code" : "CNY", "numericCode" : 156, "decimalPlaces" : 2 }, "amount" : "20.00" } }, "createTime" : ISODate("2020-01-13T07:17:25.919Z"), "updateTime" : ISODate("2020-01-13T07:17:25.919Z"), "_class" : "geektime.spring.data.mongodemo.model.Coffee" }

//删除espresso
> db.coffee.remove({"name":"espresso"});
WriteResult({ "nRemoved" : 1 })
> db.coffee.find();

MongoRepository

@EnableMongoRepositories
对应接⼝

  • MongoRepository<T, ID>
  • PagingAndSortingRepository<T, ID>
  • CrudRepository<T, ID>

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>geektime.spring.data</groupId>
	<artifactId>mongo-repository-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>mongo-repository-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb</artifactId>
		</dependency>

		<dependency>
			<groupId>org.joda</groupId>
			<artifactId>joda-money</artifactId>
			<version>RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

application.properties

spring.data.mongodb.uri=mongodb://springbucks:springbucks@192.168.31.201:27017/springbucks
spring.data.mongodb.username=admin
spring.data.mongodb.password=123456

Coffee

package geektime.spring.data.mongodemo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.joda.money.Money;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;

@Document
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Coffee {
    @Id
    private String id;
    private String name;
    private Money price;
    private Date createTime;
    private Date updateTime;
}

MoneyReadConverter 类型转换处理类

package geektime.spring.data.mongodemo.converter;

import org.bson.Document;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class MoneyReadConverter implements Converter<Document, Money> {
    @Override
    public Money convert(Document source) {
        Document money = (Document) source.get("money");
        double amount = Double.parseDouble(money.getString("amount"));
        String currency = ((Document) money.get("currency")).getString("code");
        return Money.of(CurrencyUnit.of(currency), amount);
    }
}

CoffeeRepository

package geektime.spring.data.mongodemo.repository;

import geektime.spring.data.mongodemo.model.Coffee;
import org.springframework.data.mongodb.repository.MongoRepository;

import java.util.List;

public interface CoffeeRepository extends MongoRepository<Coffee, String> {
    List<Coffee> findByName(String name);
}

启动类

package geektime.spring.data.mongodemo;

import geektime.spring.data.mongodemo.converter.MoneyReadConverter;
import geektime.spring.data.mongodemo.model.Coffee;
import geektime.spring.data.mongodemo.repository.CoffeeRepository;
import lombok.extern.slf4j.Slf4j;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

import java.util.Arrays;
import java.util.Date;

@Slf4j
@SpringBootApplication
@EnableMongoRepositories
public class MongoRepositoryDemoApplication implements CommandLineRunner {
	@Autowired
	private CoffeeRepository coffeeRepository;

	public static void main(String[] args) {
		SpringApplication.run(MongoRepositoryDemoApplication.class, args);
	}

	@Bean
	public MongoCustomConversions mongoCustomConversions() {
		return new MongoCustomConversions(Arrays.asList(new MoneyReadConverter()));
	}

	@Override
	public void run(String... args) throws Exception {
		//init: espresso 、 latte
		Coffee espresso = Coffee.builder()
				.name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.createTime(new Date())
				.updateTime(new Date()).build();
		Coffee latte = Coffee.builder()
				.name("latte")
				.price(Money.of(CurrencyUnit.of("CNY"), 30.0))
				.createTime(new Date())
				.updateTime(new Date()).build();
		//insert
		coffeeRepository.insert(Arrays.asList(espresso, latte));

		//find
		coffeeRepository.findAll(Sort.by("name"))
				.forEach(c -> log.info("Saved Coffee {}", c));
		//Saved Coffee Coffee(id=5e1c28b1a99c0451e446ac1d, name=espresso, price=CNY 20.00, createTime=Mon Jan 13 16:22:09 CST 2020, updateTime=Mon Jan 13 16:22:09 CST 2020)
		//Saved Coffee Coffee(id=5e1c28b1a99c0451e446ac1e, name=latte, price=CNY 30.00, createTime=Mon Jan 13 16:22:09 CST 2020, updateTime=Mon Jan 13 16:22:09 CST 2020)

		//update
		Thread.sleep(1000);
		latte.setPrice(Money.of(CurrencyUnit.of("CNY"), 35.0));
		latte.setUpdateTime(new Date());
		coffeeRepository.save(latte);
		coffeeRepository.findByName("latte")
				.forEach(c -> log.info("Coffee {}", c));
		//Coffee Coffee(id=5e1c28b1a99c0451e446ac1e, name=latte, price=CNY 35.00, createTime=Mon Jan 13 16:22:09 CST 2020, updateTime=Mon Jan 13 16:22:10 CST 2020)

		//delete
		coffeeRepository.deleteAll();
	}
}

作者:Soulboy