Spring Boot实战(八)8.6 非关系型数据库NoSQL

本文探讨了NoSQL数据库和Redis在Spring Boot框架下的集成与使用,包括MongoDB的数据访问、对象文档映射、Repository支持以及Redis的数据操作、序列化配置等实战案例。

NoSQL是对于不使用关系作为数据管理的数据库系统的统称。NoSQL的主要特点是不使用SQL语言作为查询语言,数据存储也不是固定的表、字段。
NoSQL数据库主要有文档存储型(MongoDB)、图形关系存储型(Neo4j)和键值对存储型(Redis)。
本节将演示基于MongoDB的数据访问以及基于Redis的数据访问。

8.6.1 MongoDB

MongoDB是一个基于文档(Document)的存储型的数据库,使用面向对象的思想,第一条数据都是文档的对象。
在本节我们不会介绍太多关于MongoDB数据库本身的知识,本节主要讲述Spring及Spring Boot对MongoDB的支持,以衣基于Spring Boot和MongoDBr的实战例子。

1. Spring 的支持

Spring对MongoDB的支持主要是通过Spring Data MongoDB来实现的,Spring Data MongoDB为我们提供了如下功能。

(1)Object/Document映射注解支持

JPA提供了一套Object/Relation映射的注解(@Entity、@Id),而Spring Data MongoDB也提供了下表所示的注解。

注解描述
@Document映射领域对象与MongoDB的一个文档
@Id映射当前属性是ID
@DbRef当前属性将参考其他的文档
@Field为文档的属性定义名称
@Version将当前属性作为版本
(2)MongoTemplate

像JdbcTemplate一样,Spring Data MongoDB也为我们提供了一个MongoTemplate,MongoTemplate为我们提供了数据访问的方法。我们还需要为MongoClient以及MongoDBFactory来配置数据库连接属性,例如:

@Bean
public MongoClient client() throws UnknowHostException {
		MogoClient client = new MongoClient(new ServerAddress("127.0.0.1",27017));	
		return client;
}

@Bean
public MongoDbFactory mongoDbFactory throws Exception {
	String database = new MongoClientURI(mongodb://localhost/test").getDatabase();
	return new SimpleMongoDbFactory(client() , database);
}

@Bean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) throws UnknownHostException{
return new MongoTemplate(mongoDbFactory);
}
(3)Repository的支持

类似于Spring Data JPA,Spring Data MongoDB也提供了Repository的支持,使用方式和Spring Data JPA一致,定义如下:

public interface PersonRepository extends MongoRepository<Person,String>{

}

类似于Spring Data JPA的开启支持方式,MongoDB的Repository的支持开启需在配置类上注解@EnableMongoRepositories

@Configuration
@EnableMongoRepositories
public class AppConfig{
}

2.Spring Boot的支持

Spring Boot对MongoDB的支持,分别位于:

org.springframework.boot.autoconfigure.mongo

主要配置数据库连接、MongoTemplate。我们可以使用以“spring.data.mongodb”为前缀的属性来配置MongoDB相关的信息。Spring Boot为我们提供了一些默认属性,如默认MongoDB的端口为27017、默认服务器为localhost、默认数据库为test。Spring Boot的主要配置如下:

spring.data.mongodb.host= # 数据库主机地址默认localhost
spring.data.mongodb.port=27017 # 数据库连接端口默认27017
spring.data.mongodb.uri=mongodb://localhost/test # connection URL
spring.data.mongodb.database=
spring.data.mongodb.authentication-dattabase=
spring.data.mongodb.grid-fs-database=
spring.data.mongodb.username=
spring.data.mongodb.password=
spring.data.mongodb.repositories.enbled=true # repository支持是否开启,默认为已开启
spring.data.mongodb.field-naming-strategy=
org.springframework.boot.autoconfigure.data.mongo

为我们开启了对Repository的支持,即自动为我们配置了@EnableMongoRepositories。
所以我们在Spring Boot下使用MongoDB只需引入spring-boot-starter-data-mongodb依赖即可,无须任何配置。

3.实战

(1)安装MongoDB

1)非Docker安装。若不使用Docker作为安装方式,则我们可以访问http://www.mongodb.org/downloads来下载适合自己当前操作系统的版本来安装MongoDB。
2)Docker安装。前面已经下载好了MongoDB的Docker镜像,接下来需通过下面命令运行Docker容器:

docker run -d -p 27017:27017 mongo

运行好以后,记得在VirtualBox再做一次端口映射如图

在这里插入图片描述

MongoDB数据库管理软件可使用Robomongo,下载地址是http://www.robomongo.org,我这里使用的是最新的Studio 3T 如图

在这里插入图片描述

(2)搭建Spring Boot项目

搭建Spring Boot项目,依赖为MongoDB(spring-boo-starter-data-mongodb)和Web(spring-boot-starter-web)。
项目信息:

groupId:com.wisely
arctifactId:ch8_6_1
package:com.wisely.ch8_6_1

因为Spring Boot的默认数据库连接满足我们当前测试的要求,所以将不在为appliction.properties配置连接信息。

(3)领域模型

本例的领域模型是人(Person),包含他工作过的地点(Location)。这个虽然和关系型数据库的一对多类似,但还是不一样的,Location的数据只属于某个人。
Person源码:

package com.wisely.ch8_6_1.domain;

import java.util.Collection;
import java.util.LinkedHashSet;


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

@Document  //映射领域模型和MongoDB的文档
public class Person {
	@Id  //表明这个属性为文档的Id
	private String id;
	private String name;
	private Integer age;
	@Field("locs")  //此属性在文档中的名称为locs,locations属性将以数组形式存在当前数据记录中。
	private Collection<Location>locations = new LinkedHashSet<>();
	
	public Person(String name,Integer age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Collection<Location> getLocations() {
		return locations;
	}

	public void setLocations(Collection<Location> locations) {
		this.locations = locations;
	}
	
	
	
	
	

}

Location源码:

package com.wisely.ch8_6_1.domain;

public class Location {
	
	private String place;
	
	private String year;
	public Location(String place,String year) {
		super();
		this.place = place;
		this.year = year;
	}
	public String getPlace() {
		return place;
	}
	public void setPlace(String place) {
		this.place = place;
	}
	public String getYear() {
		return year;
	}
	public void setYear(String year) {
		this.year = year;
	}
	
	

}

(4)数据访问
package com.wisely.ch8_6_1;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

import com.wisely.ch8_6_1.domain.Person;

public interface PersonRepository extends MongoRepository<Person,String> {
	
	Person findByName(String name);  //支持方法名查询
	
	@Query("{'age':?0}")   //支持@Query查询,查询参数构造JSON字符串即可。
	List<Person> withQueryFindByAge(Integer age);

}

(5)控制器
package com.wisely.ch8_6_1.web;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

import com.wisely.ch8_6_1.PersonRepository;
import com.wisely.ch8_6_1.domain.Location;
import com.wisely.ch8_6_1.domain.Person;
@RestController
public class DataController {
	@Autowired
	PersonRepository personReposityro;
	
	@RequestMapping("/save")  //测试保存数据
	public Person save() {
		Person p = new Person("lmz", 32);
		Collection<Location> locations = new LinkedHashSet<Location>();
		Location loc1 = new Location("上海", "2009");
		Location loc2 = new Location("合肥", "2010");
		Location loc3 = new Location("广州", "2011");
		Location loc4 = new Location("马鞍山", "2012");
		locations.add(loc1);
		locations.add(loc2);
		locations.add(loc3);
		locations.add(loc4);
		p.setLocations(locations);
		return personReposityro.save(p);
	}
	
	@RequestMapping("/q1")  //测试方法名查询
	public Person q1(String name) {
		return personReposityro.findByName(name);
	}
	
	@RequestMapping("/q2")  //测试@Query方法
	public List<Person> q2(Integer age){
		return personReposityro.withQueryFindByAge(age);
	}

}

(6)运行

测试保存数据
访问http://localhost:8080/save测试保存,页面如图
在这里插入图片描述

我们在Studio 3T中查看保存后的数据,如图
在这里插入图片描述
测试方法名查询
访问http://localhost:8080/q1?name=lmz 页面结果如图
在这里插入图片描述
测试@Query查询
访问http://localhost:8080/q2?age=32,页面结果如图
在这里插入图片描述

8.6.2 Redis

redis是一上基于键值对的开源内存数据存储,当然Redis也可以做数据缓存。

1.Spring的支持
(1)配置

spring对Redistribute的支持也是通过Spring Data Redis来实现的,Spring Data JPA为我们提供了连接相关的ConnectionFactory和数据操作相关的RedisTemplate。在此特别指出,Spring Data Redis只对Redis2.6和2.8版本做过测试。
根据Redis的不同的Java客户端,Spring Data Redis提供了如下的ConnectionFactory:
JedisConnectionFactory:使用Jedis作为Redis客户端。
JredisConnectionFactory:使用Jredis作为Redis客户端。
LettuceConnectionFactory:使用Lettuce作为Redis客户端。
SrpConnectionFactory:使用Spullara/redis-protocol作为Redis客户端。

配置方式如下:

@Bean
public RedisConnectionFactory redisConnectionFactory(){
		return new JedisConnectionFactory();
}

RedisTemplate配置方式如下:

@Bean
public RedisTemplate<Object,Object> redisTemplate() throws UnknownHostException{
	RedisTemplate<Object,Object>template = new RedisTemplate<Object,Object>();
	template.setConnectionFactory(redisConnectionFactory());
	return template;
}
(2)使用

Spring Data Redis为我们提供了RedisTemplate和StringRedisTemplate两个模板来进行数据操作,其中,StringRediTemplate只针对键值都是字符型的数据进行操作。
RedisTemplate和StringRedisTemplate提供的主要数据访问方法如表

方法说明
opsForValue()操作只有简单属性的数据
opsForList()操作含有list的数据
opsForSet()操作含有set的数据
opsForZSet()操作含有ZSet(有序的set)的数据
opsForHash()操作含有hash的数据

更多关于Spring Data Redis的操作,请查看Spring Data Redis的官方文档。

(3)定义Serializer

当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。
Spring Data JPA为我们提供了下面的Serializer:
GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。

2.Spring Boot的支持

Spring Boot对Redis的支持,org.springframework.boot.autoconfigure.redis包如图所示
在这里插入图片描述
RedisAutoConfiguration为我们默认配置了JedisConnectionFactory、RedisTemplate以及StringRedisTemplate,让我们可以直接使用Redis作为数据存储。
RedisProperties向我们展示了可以使用以“spring.redis”为前缀的属性在application.properties中配置Redis,主要属性如下

spring.redis.database = 0 #数据库名称,默认为db0
spring.redis.host = localhost #服务器地址,默认为localhost
spring.redis.password = # 数据库密码d
spring.redis.port = 6379 #连接端口号,默认为6379
spring.redis.pool.max-idle=8 #连接池设置
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max.wait=-1
spring.redis.sentinel.master=
spring.redis.sentinel.nodes=
spring.redis.timeout=
3.实战
(1)安装Redis

1)非Docker安装。若不基于Docker安装的话,我们可以到http://redis.io/download 下载合适版本的Redis。注意不要下载最新的3.0.x版本。
2)Docker安装。前面我们已经下载好了Redis镜像,通过下面命令运行容器:

docker run -d -p 6379:6379 redis:2.8.21

并在VirtualBox配置端口映射,如图
在这里插入图片描述
Redis数据管理可以使用Redis Client,下载地址为https://github.com/caoxinyu/RedisClient,这是一个可以独立运行的jar包,如图
在这里插入图片描述

(2)新建Spring Boot项目

搭建Spring Boot项目,依赖为Redis(spring-boot-starter-redis)和Web(spring-boot-starter-web)。
项目信息

groupId:com.wisely
arctifactId:ch8_6_2
package:com.wisely.ch8_6_2

因为Spring Boot的默认数据库连接满足我们当前测试的要求,所以无须在application.properties配置连接信息。

(3)领域模型类:
package com.wisely.ch8_6_2.dao;

import java.io.Serializable;

public class Person implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String id;
	private String name;
	private Integer age;
	
	public Person() {
		super();
	}
	
	public Person(String id,String name,Integer age) {
		super();
		this.id=id;
		this.name=name;
		this.age=age;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}
	
	
	

}

注意,此类必须 用时间序列化接口,因为使用jackson做序列化需要一个空构造。

(4)数据访问:
package com.wisely.ch8_6_2.domain;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import com.wisely.ch8_6_2.dao.Person;

@Repository
public class PersonDao {
	
	@Autowired
	StringRedisTemplate stringRedisTemplate;   //Spring Boot已为我们配置StringRedisTemplate,在此处可以直接注入。
	
	@Resource(name="stringRedisTemplate")
	ValueOperations<String, String> valOpsStr;   //可以使用@Resource注解指定stringRedisTemplate,可注入基于字符串的简单属性操作方法。
	
	@Autowired
	RedisTemplate<Object, Object> redisTemplate;  //Spring Boot已为我们配置RedisTemplate,在此处可以直接注入。
	
	@Resource(name="redisTemplate")
	ValueOperations<Object, Object> valOps;   //可以使用@Resource注解指定redisTemplate,可注入基于对象的简单属性操作方法;
	
	public void stringRedisTemplateDemo() {   //通过set方法,存储字符串类型。
		valOpsStr.set("xx", "yy");
	}
	
	public void save(Person person) {
		valOps.set(person.getId(), person);  //通过set方法存储对象类型
	}
	
	public String getString() {
		return valOpsStr.get("xx");     //通过get方法获得字符串
	}
	
	public Person getPerson() {
		return (Person) valOps.get("1");   //通过get方法,获得对象。
	}

}

(5)配置

Spring Boot为我们自动配置了RedisTemplate,而RedisTemplate使用的是JdkSerializationRedisSerializer,这个对我们演示Redis Client很不直观,因为JdkSerializationRedisSerializer使用二级制形式存储数据,在此我们将自己配置RedisTemplate并定义Serializer。

package com.wisely.ch8_6_2;

import java.net.UnknownHostException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

@SpringBootApplication
public class Ch88612Application {

	public static void main(String[] args) {
		SpringApplication.run(Ch88612Application.class, args);
	}
	
	@Bean
	@SuppressWarnings({"rawtypes","unchecked"})
	public RedisTemplate<Object, Object>redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException{
		RedisTemplate<Object, Object> template = new RedisTemplate<Object,Object>();
		template.setConnectionFactory(redisConnectionFactory);
		
		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		
		template.setValueSerializer(jackson2JsonRedisSerializer);  //设置值(value)的序列化采用Jackson2JsonRediSerializer。
		template.setKeySerializer(new StringRedisSerializer());  //设置键(key)的序列化采用StringRedisSerializer。
		
		return template;
		
	}

}

(6)控制器:
package com.wisely.ch8_6_2.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.wisely.ch8_6_2.dao.Person;
import com.wisely.ch8_6_2.domain.PersonDao;

@RestController
public class DataController {
	
	@Autowired
	PersonDao personDao;
	
	@RequestMapping("/set")  //演示设置字符及对象 。
	public void set() {
		Person person = new Person("1","lmz",32);
		personDao.save(person);
		personDao.stringRedisTemplateDemo();
	}
	
	@RequestMapping("/getStr")  //演示获得字符
	public String getStr() {
		return personDao.getString();
	}
	
	@RequestMapping("/getPerson")  //演示获得对象
	public Person getPerson() {
		return personDao.getPerson();
	}

}

(7)运行

演示设置字符及对象,访问http://localhost:8080/set 此时查看 Redis Client 字符存储如图
在这里插入图片描述
演示获得字符,访问http://localhost:8080/getStr,页面显示如图
在这里插入图片描述

演示获得对象,访问http://localhost:8080/getPerson,页面显示如图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值