Javaweb——Spring Boot 系列(15)与非关系型数据库交互

折月煮酒 提交于 2020-02-13 02:18:58

一、非关系型数据库

  • 目前的数据库都可分为关系型数据库和非关系型数据库,前者严格按照关系数学对数据库进行设计和实现,后者则不是如此。
  • 对不使用关系作为数据管理的数据库管理系统都可以统称为 NoSQL,典型特征就是不使用 SQL 语句作为查询语言,数据存储也不是固定的表、字段。
  • 在关系数据库中,不允许表中有表,但是在非关系数据库中则可以有。
  • NoSQL 数据库根据存储类型的不同可分为:文档存储型,代表有 MongoDB;图形关系存储型,代表有 Neo4j;键值对存储型,代表是 Redis。
  • 考虑到图形比较消耗资源(内存),本文只以 MongoDB 和 Redis 作为后台数据库测试数据交互。

1、MongoDB

  • MongoDB 基于文档存储数据,面向对象的思想,每一条数据记录都是文档的对象。
  • MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。——百度百科

2、Redis

  • edis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。——百度百科

二、Spring Boot 对 MongoDB 和 Redis 的支持

1、对 MongoDB 的支持

  • Spring 框架中提供了一个 Spring Data MongoDB 来对 MongoDB 数据库进行支持,并提供 Object/Document 映射注解支持、MongoDBTemplate 和 Repository 支持。
  • 在 Spring Data MongoDB 中用如下注解完成相关功能
    注解 作用
    @Document 映射领域对象与 MongoDB 的一个文档
    @Id 映射当前属性是 ID
    @DbRef 当前属性参考其他文档
    @Field 为文档的属性定义名称
    @Version 将当前属性作为版本
  • MongoDBTemplate 的使用和 jdbcTemplate 的使用差不多,这里不演示,这里演示的是用 Repository 对数据进行交互。
  • 使用 Repository 与 MongoDB 数据库交互的步骤就是,建立一个领域模型(在 Spring Data JPA 中称为实体类)的 Repository 并继承 MongoRepository 接口,然后用这个 Repository 方法操作数据库,总之和之前的类似。
  • Spring Boot 的 autoconfiguration.mongo 中对 MongoDB 的相关配置进行了大量自动配置,诸如默认 MongoDB 的端口为 27017、默认服务器为 localhost、默认数据库是 test,如果默认属性不满足需求,可以在 application.properties 文件中使用前缀为 spring.data.mongodb._ 进行相关设置,常用的如下:
    spring.data.mongodb.host= #数据库主机地址,默认 localhost
    spring.data.mongodb.port=27017 # 端口
    spring.data.mongodb.uri=mongodb://localhost/test # 连接 URI
    spring.data.mongodb.database=
    spring.data.mongodb.authentication-database=
    spring.data.mongodb.grid-fs-database=
    spring.data.mongodb.username=
    spring.data.mongodb.password=
    spring.data.mongodb.repository.enabled=true # 默认开启对 Repository 的支持
    spring.data.mongodb.field-naming-strategy=org.springframework.boot.autoconfigure.data.mongo
    

2、对 Redis 的支持

  • Spring 通过 Spring Data Redis 实现对 Redis 数据库的支持,Spring Data Redis 根据 Redis 的不同 Java 客户端提供了 JedisConnectionFactory 和 JredisConnectionFactory 两种 ConnectionFactory,前者以 Jedis 作为 Redis 客户端,后者以 Jredis 作为客户端;以及以 Lettuce 为客户端的 LettuceConnectionFactory 和以 Spullara/redis-protocol 为客户端的 SrpConnectionFactory。配置方式大致相同,如下:
    @Bean
    public RedisConnectionFactory redisConnectionFactory(){
    	return new JedisConnectionFactory();
    }
    // template 模式
    @Bean
    public RedisTemplate<Object,Object>redisTemplate() throws UnknownHostException{
    	RedisTemplate<Object,Object>template = new RedisTemplate<>();
    	template.setConnectionFactory(redisConnectionFactory());
    	return template;
    }
    
  • spring 除了提供 RedisTemplate, 还提供了 StringRedisTemplate, 共两个模板来 Redis 数据库进行操作。后者,即 StringRedisTemplate 只针对都是字符型的数据进行操作。
  • 两个 Template 都提供如下的主要方法来操作数据:
    方法 功能
    opsForValue() 操作只有单属性的数据
    opsForList() 操作含有 list 的数据
    opsForSet() 操作含有 set 的数据
    opsForZSet() 操作含有 ZSet(有序的set) 的数据
    opsForHash() 操作含有 hash 的数据
  • Spring 使用 Serializer 将键值对序列化到数据库中,RedisTemplate 默认使用 JdkSerializationRedisSerializer,StringRedisTemplate 则是 StringRedisSerializer。
  • Spring Boot 在 autoconfigure.redis 中对 JedisConnectionFactory、RedisTemplate 和 StringRedisTemplate 进行了默认配置,此外可以在 appliation.properties 文件中使用 spring.redis._ 进行对 Redis 进行配置,主要属性如下:
    spring.redis.database=0 # 数据库名称,默认为 0
    spring.redis.host=localhost #数据库主机,默认 localhost
    spring.redis.password
    spring.redis.port=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=
    

三、MongoDB 项目测试

1、数据库准备

  • 我这里用的是 Docker 安装 MongoDB 数据库,先用命令拉取 MongoDB 镜像,命令如下:
    dokcer pull mongo
    
  • 镜像下载完后,运行一个 Mongol 容器,使用如下命令:
    docker run -d -p 27017:27017 mongo
    
  • 为了方便再物理主机中操作数据库,在 VirtualBox 做一次端口映射。
  • 为了在程序运行后校验数据,使用 Robot 3T 作为可视化管理工具,可以去官网下载。

2、新建项目

  • 新建一个 Spring 项目,仍旧是用 IDEA 的项目新建向导,初始依赖选择 MongoDB 和 Web,项目的 POM 文件如下:
    <?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 https://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>1.3.0.M2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.pyc</groupId>
        <artifactId>mongo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>mongo</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.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  • 使用 Spring Boot 对 MongoDB 的默认配置,因此无需编辑 application.properties。

3、准备领域模型

  • 领域模型为人(Person)和其工作地点(Location),相当于表中有表。
  • Person 类
    package com.pyc.mongo.domain;
    
    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;
    import org.springframework.data.mongodb.core.mapping.Field;
    
    
    import java.util.Collection;
    import java.util.LinkedHashSet;
    
    
    //映射领域和MongoDB的文档
    // Location map and document of MongoDB
    @Document
    public class Person {
        @Id
        private String id;
        private String name;
        private Integer age;
        @Field("locs")
        private Collection<Location> locations = new LinkedHashSet<Location>();
    
        public Person(String name, Integer age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getId() {
            return id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setLocations(Collection<Location> locations) {
            this.locations = locations;
        }
    
        public Collection<Location> getLocations() {
            return locations;
        }
    }
    
  • Location 类
    package com.pyc.mongo.domain;
    
    public class Location {
        private String place;
        private String year;
    
        public Location(String place, String year) {
            super();
            this.place = place;
            this.year = year;
        }
    
        public void setPlace(String place) {
            this.place = place;
        }
    
        public void setYear(String year) {
            this.year = year;
        }
    
        public String getPlace() {
            return place;
        }
    
        public String getYear() {
            return year;
        }
    }
    

4、数据访问接口和视图层控制器

  • 首先是一个数据访问接口,即 Repository,如下:
    package com.pyc.mongo.dao;
    
    import com.pyc.mongo.domain.Person;
    import org.springframework.data.mongodb.repository.MongoRepository;
    import org.springframework.data.mongodb.repository.Query;
    
    import java.util.List;
    
    public interface PersonRepository extends MongoRepository<Person, String> {
        // query by using function
        Person findByName(String name);
    
        // query by using @Query,query parameter is constructed by json string
        @Query("{'age':?0}")
        List<Person> withQueryFindByAge(Integer age);
    }
    
  • 视图层控制器
    package com.pyc.mongo.web;
    
    import com.pyc.mongo.dao.PersonRepository;
    import com.pyc.mongo.domain.Location;
    import com.pyc.mongo.domain.Person;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Collection;
    import java.util.LinkedHashSet;
    import java.util.List;
    
    @RestController
    public class DataController {
        @Autowired
        PersonRepository personRepository;
    
        // testing the function of save data into database
        @RequestMapping("/save")
        public Person save(){
            Person p = new Person("pyc",22);
            Collection<Location> locations = new LinkedHashSet<Location>();
            Location loc1 = new Location("陆丰","2017");
            Location loc2 = new Location("肇庆","2018");
            Location loc3 = new Location("肇庆","2019");
            Location loc4 = new Location("肇庆","2020");
            Location loc5 = new Location("肇庆","2021");
            locations.add(loc1);
            locations.add(loc2);
            locations.add(loc3);
            locations.add(loc4);
            locations.add(loc5);
            p.setLocations(locations);
            return personRepository.save(p);
        }
    
        // testing query by using function
        @RequestMapping("/q1")
        public Person q1(String name){
            return personRepository.findByName(name);
        }
    
        // testing query by using @Query
        @RequestMapping("/q2")
        public List<Person> q2(Integer age){
            return personRepository.withQueryFindByAge(age);
        }
    }
    
  • 入口类无需修改。

5、运行测试

  • 先访问 localhost:8080/save 测试保存,浏览器页面如下:
    在这里插入图片描述
  • 用可视化管理工具连接数据库,查看数据库中的情况,在 test 数据库中新建了一个 Person 文档,文档的数据如下:
    在这里插入图片描述
  • 用 JSON 格式呈现如下:
    在这里插入图片描述
  • 分别测试方法名查询和 @Query 查询都无错误正常执行,其 url 分别为 localhost:8080/q1?name=pyc 和 localhost:8080/q2?age=22

四、Redis 项目测试

1、数据库准备

  • 同样的,这里还是用 Docker 安装 Redis 数据库,版本是 2.8.21,先用命令拉取 Redis-2.8.21 的镜像,命令如下:
    docker pull redis:2.8.21
    
  • 当镜像下载好之后,用 Docker 命令启动一个 Redis 容器,命令如下:
    docker run -d -p 6379:6379 redis:2.8.21
    
  • 记住,第一次运行容器时如果不指定容器镜像的版本,那么默认用最新版本。
  • 在上面的命令中,将 Redis 绑定到 6379 端口运行,同样的,为了物理主机中可以访问数据库,用 VirtualBox 做一次端口映射,将虚拟机的 6379 端口映射到主机的 6379 端口。
  • 管理工具,我用的是 Redis Desktop Manager。

2、新建项目

  • 新建一个 Spring Boot 项目,初始依赖选择 Redis 和 Web,POM 文件如下:
    <?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 https://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>1.3.0.M2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.pyc</groupId>
        <artifactId>redis</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>redis</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-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
        <repositories>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
            </repository>
        </repositories>
        <pluginRepositories>
            <pluginRepository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
            </pluginRepository>
        </pluginRepositories>
    
    </project>
    
  • 使用 Spring Boot 对 Redis 的默认配置,因此无需编辑 application.properties 文件,需要的话可以自己添加关于日志的配置。

3、领域模型

  • Redis 数据库作为后台数据库,同样需要一个领域模型,其代码如下:
    package com.pyc.redis.domain;
    
    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;
        }
    
    }
    
  • 与 MongoDB 不同的是,这里需要 implements Serialiable 类,这是一个用于序列化的接口,更多知识这里不详细说了。

4、Repository

  • Redis 的 Repository 不是通过继承接口来实现,而是用一个 @Repository 注解,如下:
    package com.pyc.redis.dao;
    
    import com.pyc.redis.domain.Person;
    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 javax.annotation.Resource;
    
    @Repository
    public class PersonRepository {
        @Autowired
        StringRedisTemplate stringRedisTemplate;
    
        // 注入基于字符串的简单属性操作方法
        // Injecting simple attribute manipulation methods based on strings
        @Resource(name = "stringRedisTemplate")
        ValueOperations<String, String> valOpsStr;
    
        @Autowired
        RedisTemplate<Object,Object> redisTemplate;
    
        @Resource(name="redisTemplate")
        ValueOperations<Object, Object> valOps;
    
        public void stringRedisTemplateDemo(){
            valOpsStr.set("中国","北京");
        }
        public void save(Person person){
            valOps.set(person.getId(),person);
        }
        public String getString(){
            return valOpsStr.get("xx");
        }
        public Person getPerson(){
            return (Person)valOps.get("1");
        }
    }
    

5、自定义 Serializer

  • 由于 JdkSerializationRedisSerializer 使用二进制存储数据,不利于阅读,因此在这里进行修改配置,在入口类中增加如下的 Bean
    @Bean
    @SuppressWarnings({"rawtypes","unchecked"})
    public RedisTemplate<Object, Object>redisTemplate(RedisConnectionFactory redisConnectionFactory)
        throws UnknownHostException{
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        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);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    
    }
    

6、Controller

  • Spring Boot 项目少不了一个 Controller 的,这在 MVC 中是重要组成。
    package com.pyc.redis.web;
    
    import com.pyc.redis.dao.PersonRepository;
    import com.pyc.redis.domain.Person;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class DataController {
    
        @Autowired
        PersonRepository personRepository;
    
        @RequestMapping("/set")
        public void set(){
            Person person = new Person("2", "ycy", 22);
            personRepository.save(person);
            personRepository.stringRedisTemplateDemo();
        }
    
        @RequestMapping("/getStr")
        public String getStr(){
            return personRepository.getString();
        }
    
        @RequestMapping("/getPerson")
        public Person getPerson(){
            return personRepository.getPerson();
        }
    }
    
  • set 方法用于初始化数据库,另外两个方法分别测试获取对象的操作和获取简单字符串值得操作。

7、测试运行

  • 运行项目,在浏览器访问 localhost:8080/set ,这个路径浏览器不会显示任何内容,但是用管理软件查看后台数据库,则发现在 db0 中增加了两个键值对,如下:
    在这里插入图片描述
    在这里插入图片描述
  • 分别就是刚刚用来初始化得数据。
  • 测试获取简单字符串数据,访问 URL localhost:8080/getStr,浏览器显示一个 “yy" 字符串。
  • 测试获取对象,localhost:8080/getPerson 浏览器获取如下数据:
    ["com.pyc.redis.domain.Person",{"id":"2","name":"ycy","age":22}]
    

上一篇

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!