本文讲述了NoSQL(非关系型数据库)与构建Restful服务等,有一点的挑战性。
基本上,到目前为止,spring boot体系讲得也差不多了,当然后续还有一系列进阶的内容:例如单元测试,缓存与安全管理等。对于这里讲到的NoSQL,后期也会再深入,这里都是最基本的用法。
同时在RESTful服务中也使用到了新工具(postman)来进行接口测试等工作,也是非常方便。
希望以下内容对你有所帮助!
 
目录
前言
NoSQL是非关系型数据库。非关系和关系型数据库存在不同点,其中最重要的是NoSQL不使用SQL作为查询语句。其数据存储可以不需要固定的表格模式,有以下分类:
 1 Key/Value 键值存储。数据存储通常是无数结构的,一般被当做字符串等,但是数据加载快,典型使用场景就是处理高并发或者用于日志系统,这一类数据库有Redis,Tokyo Cabinet等。
 2 列存储数据库。一般用于分布式文件系统
 3 文档型数据库。和Key / Value 键值存储类似。这一类有MongoDB等。
 4 图形数据库,专注于构建关系图谱。有Neo4J,DEX等。
REST 是一种Web软件架构风格,(注意不是标准)匹配或兼容这种架构的网络服务是REST服务,服务简洁且有层次。在REST中,资源是由URL指定的,对资源增删改查操作可以通过HTTP协议提供的GET,POST,PUT,DELETE等方法实现。可以利用缓存来提高响应速度,同时REST通信会话由客户端来实现。
整合Redis
是一个使用C编写的基于内存的NoSQL数据库,目前最流行的键值对存储数据库。
安装Redis
安装环境为Centos 7
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
# 如果没找到命令
yum install wget
 
不过我安装的时候遇到了这个问题:
 
 使用 vim /etc/resolv.conf 打开配置文件
 输入nameserver 8.8.8.8 以及 nameserver 8.8.4.4就可以了
 
 首先解压下载的文件,然后进入解压目录进行编译,执行语句:
tar -zxvf redis-4.0.10.tar.gz
cd redis-4.0.10
make MALLOC=libc
make install
# 如果提示gcc 未找到命令
yum install gcc
 
配置Redis
安装成功后,需要进行配置,打开redis,解压src/redis.conf文件
vim redis.conf
# i 插入模式:
daemonize yes # 表示允许redis在后台启动
# bind 127.0.0.1 # 允许连接的实例地址,默认情况下只允许本地连接,将默认配置注释,外网就可以连接
requirepass ***** # 登陆的密码
protected-mode no # 关闭保护模式
 
为了远程连接上redis,需要关闭Centos防火墙,执行命令如下:
systemctl stop firewalld.service # 关闭防火墙
systemctl disable firewalld.service # 禁止防火墙开机启动
 
执行启动redis
redis-server redis.conf
redis-cli -a *** # 进入控制台,这个表示密码
 

 windows版本连接就更简单了,之间去redis-windows选择zip下载就可以。
 不过注意要在cmd管理员下运行
redis-server.exe redis.windows.conf
 
一定要保持开启才能运行项目
整合Spring Boot
redis的Java客户端有很多,例如Jedis ,JRedis,Spring Data Redis等。Spring Boot借助于Spring Data Redis为Redis提供了开箱即用的自动化配置,开发者只需要添加相关依赖既可。
 添加依赖:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
 
在application.properties配置redis连接信息:
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=qazwsx123
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
 
创建一个实体类:
import java.io.Serializable;
/**
 * Created by sang on 2018/7/18.
 */
public class Book implements Serializable {
    private Integer id;
    private String name;
    private String author;
    //省略getter/setter
    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}
 
创建BookController进行测试:
@RestController
public class BookController {
    @Autowired(
            required = false
    )
    RedisTemplate redisTemplate;
    @Autowired(
            required = false
    )
    StringRedisTemplate stringRedisTemplate;
    @GetMapping("/test1")
    public  void test1() {
        ValueOperations<String,String> ops1 = stringRedisTemplate.opsForValue();
        ops1.set("name","三国演义");
        String name = ops1.get("name");
        ValueOperations ops2 = redisTemplate.opsForValue();
        Book b1 = new Book();
        b1.setId(1);
        b1.setName("红楼梦");
        b1.setAuthor("曹雪芹");
        ops2.set("b1", b1);
        Book book = (Book) ops2.get("b1");
        System.out.println(book);
    }
}
 
不过当我运行的时候发现了这个错误:
 Description:
Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class

 解决办法在启动程序上面加:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
 
不过可能再次运行的时候出现8080端口被占用情况(原因可能是没有杀死程序)那么最简单的办法就是重启或者在配置修改端口为8081
 输入8081/test1便可以看到控制台信息
 
redis集群整合
上文是单个redis实例整合,在实际项目中,开发者为了提高redis扩展性,往往搭建redis集群。
 原理:所有的redis节点彼此互联,节点内部使用二进制协议优化传输速度与宽带。当一个节点挂掉后,集群超过半数的节点检测失效时才认为该节点失效。不同于Tomcat集群需要使用反向代理服务器,redis集群中的任意节点都可以直接与java客户端连接。数据分配是采用哈希槽(Hash SLOT)。
 集群中的相关知识,将在后期补充。
整合MongoDB
MongoDB是一种面向文档的数据库管理系统,它是一个介于关系型数据库和非关系型数据库之间的产品,支持一种类似JSON的BSON数据格式,既可以存储简单的数据格式,也可以存储复杂的数据类型。总的来说,MongoDB是一款应用广泛的NoSQL数据库。
MongoDB安装
windows 去此处下载:MongoDB
 安装完以后可以用Navicat 或者datagrip进行打开,注意此数据库一开始是没有密码的。
 所以这在生产环境中非常不安全的,但是不同于关系型数据库,MongoDB每一库都有一个独立的密码,在哪一个库创建用户就要在哪一个库验证密码。要配置密码,首先创建一个用户,例如在admin库中创建一个用户:
use admin;
db.createUser({user:"jacin",pwd:"qazwsx123",roles:[{role:"readWrite",db:"test"}]})
 
对数据库test具有读写两项权限
整合Spring Boot
引入依赖:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
 
配置MongoDB,在application.properties:
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.database=test
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.username=jacin
spring.data.mongodb.password=******
 
创建的实体库Book和之前一样:
 创建BookDao:类似于Spring Data JPA的repository定义
 MongoRepository已经预定义了针对实体类的查询、添加、删除等操作,可以按照命名规则定义方法。
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface BookDao extends MongoRepository<Book,Integer> {
    List<Book> findByAuthorContains(String author);
    Book findByNameEquals(String name);
}
 
创建Controller:
 insert方法,插入集合中的数据。
@RestController
public class BookController {
    @Autowired
    BookDao bookDao;
    @Autowired(
            required = false
    )
    MongoTemplate mongoTemplate;
    @GetMapping("/test2")
    public void test2() {
        List<Book> books = new ArrayList<>();
        Book b1 = new Book();
        b1.setId(3);
        b1.setName("围城");
        b1.setAuthor("钱钟书");
        books.add(b1);
        Book b2 = new Book();
        b2.setId(4);
        b2.setName("宋诗选注");
        b2.setAuthor("钱钟书");
        books.add(b2);
        mongoTemplate.insertAll(books); //插入数据
        List<Book> list = mongoTemplate.findAll(Book.class);
        System.out.println(list);
        Book book = mongoTemplate.findById(3, Book.class);
        System.out.println(book);
    }
    @GetMapping("/test1")
    public void test1() {
        List<Book> books = new ArrayList<>();
        Book b1 = new Book();
        b1.setId(1);
        b1.setName("朝花夕拾");
        b1.setAuthor("鲁迅");
        books.add(b1);
        Book b2 = new Book();
        b2.setId(2);
        b2.setName("呐喊");
        b2.setAuthor("鲁迅");
        books.add(b2);
        bookDao.insert(books);
        List<Book> books1 = bookDao.findByAuthorContains("鲁迅");
        System.out.println(books1);
        Book book = bookDao.findByNameEquals("朝花夕拾");
        System.out.println(book);
    }
}
 
可以使用命令db.book.find()进行查看。
 
Session 共享
正常情况下,HttpSession是通过Servlet容器创建来进行管理的,创建成功后,都是保存在内存中,如果开发者需要对项目横向扩展搭建集群,利用一些硬件或者软件来做负载均衡。使用Redis来方便解决问题,就是把原本存储在不同服务器上的session拿出来放在一个独立服务器上。
 
 需要添加redis 和session依赖:
 下面创建一个controller来测试操作:
@RestController
public class HelloController {
    @Value("${server.port}")
    String port;
    @PostMapping("/save")
    public String saveName(String name, HttpSession session) {
        session.setAttribute("name", name);
        return port;
    }
    @GetMapping("/get")
    public String getName(HttpSession session) {
        return port + ":"
                + session.getAttribute("name").toString();
    }
}
 
一个save接口用来向session存储数据,还有一个get接口用来从session 获取数据,这里注入了项目启动的端口号server.port,主要区分是哪个服务器提供的服务,真正的session此时存在了redis服务器上了。
Nginx负载均衡
本次使用nginx来进行负载均衡,环境是centos 7,关于安装与编译不再多说了。
项目完成后,将项目打成jar包上传到Centos上,然后执行如下两条命令启动项目:
nohup java -jar session-0.0.1-SNAPSHOT.jar --server.port=8080 &
nohup java -jar session-0.0.1-SHAPSHOT.jar --server.port=8081 &
 
nohup就是不挂使程序在后台运行,本次设置了2个端口,启动成功后开始配置负载均衡器了。
进入nginx修改配置文件:
 /conf/nginx.conf:
upstream jacin.com {
	server 192.168.66.130:8080 weight =1;
	server 192.168.66.130:8081 weight =1;
}
server {
	listen 80;
	server_name  localhost;
	location / {
		proxy_pass http://jacin.com
		proxy_redirect default;
	}
}
 
在修改的配置中,首先配置上游服务器,即两个real server,权重都是1(意味平均分配)然后在server中设置拦截规则,将请求转发到real server上。
记得重启nginx。
经过以上步骤,便可以利用redis实现session共享的功能。
JPA实现REST
基本实现
添加依赖:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.2</version>
        </dependency>
 
相关application配置如下:
server.port=8082
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///jparestful
spring.datasource.username=root
spring.datasource.password=qazwsx123
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
 
创建实体类:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity(name = "t_book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String author;
    //省略getter/setter
    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}
 
创建BookRepository:
 由于是继承,所以下面的方法也都可以不写,写只是告诉有一些基本的操作方法例如增删改查等。
import org.springframework.data.jpa.repository.JpaRepository;
//继承JpaRepository默认提供的一些基本的数据库操作方法
public interface BookRepository extends JpaRepository<Book,Integer> {
}
 
以上就是RESTful服务了。注意我一开始启动项目的时候并没有生成表格,后面才发现是我的启动程序多了东西,只需要@SpringBootApplication就可以了。
下面使用PostMan进行接口测试(注意这是软件,根据需要来下载,chrome也有,不过你懂的)
 我们可以在t_book添加一些数据:
 
 打开postman,
 RESTFul API默认请求路径是实体类名小写再加上s后缀,本文实体类为book,加上s后缀,即默认路径名是books。
 使用get命令来查询结果:
 
 新建测试,我们在postman创建一个post请求,body放入新增的数据,选择json类型.
 添加一个数据,此时已经写入数据库中。
 
 按照id查询,主要在books后面加id号即可了。
 
 修改测试:
 发送Put请求,url是请求id号
自定义请求路径:
默认情况下,请求路径为实体类名加小写s,如果开发者想对请求路径进行重定义,可以通过@RepositoryRestResource注解即可实现。
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
 
注解的path属性表示将所有请求路径中的books全都修改为bs,返回的JSON集合单个book的key修改为b.
 
自定义查询方法
默认的查询方法支持分页查询,排序查询以及按照id查询,如果开发者想要按照某个属性查询,只需在定义相关方法并暴露出去即可:
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book, Integer> {
    @Override
    @RestResource(exported = false)
    void deleteById(Integer integer);
    @RestResource(path = "author",rel = "author")
    List<Book> findByAuthorContains(@Param("author") String author);
    @RestResource(path = "name",rel = "name")
    Book findByNameEquals(@Param("name") String name);
}
 
只需要在BookRepository定义相关查询方法即可了,可以不用添加restresource注解。
 通过/bs/search 来查看该实体类暴露了哪些查询方法。
 
MongoDB 实现REST
快速构建RESTful服务除了结合Spring Data JPA之外,也可以结合MongoDB来实现,步骤如下:
 添加依赖:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
 
和之前的MongoDB设置一样,配置见上文:
创建实体类:
public class Book {
    private Integer id;
    private String name;
    private String author;
    //省略getter/setter
}
 
创建BookRepository:
import org.springframework.data.mongodb.repository.MongoRepository;
public interface BookRepository extends MongoRepository<Book,Integer> {
}
 
这样一个RESTful服务就搭建成功了。(注意要启动MongoDB)
浅谈RESTful服务
对于上面的restful服务可能理解起来有困难,所以我单独写了这个模块来加强认识。
 RESTful可以通过一套统一的接口为Web,iOS和Android提供服务。另外对于广大平台来说,比如微博开放平台,微信开放平台等,它们不需要有显式的前端,只需要一套提供服务的接口,RESTful无疑是最好的选择。(所以这就是我们使用PostMan来进行接口测试的原因)
传统的API接口:一般来说删除一个数据delete/{id} ,更新就是update/{id}等
 url是一种资源,在之前一直用的是get和post.
 基于REST构建的api就是restful风格:
GET /rest/api/getDogs --> GET /rest/api/dogs 获取所有小狗狗 
GET /rest/api/addDogs --> POST /rest/api/dogs 添加一个小狗狗 
GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一个小狗狗 
GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 删除一个小狗狗
 
REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露。
 区别HTTP(面向无状态的)
对于RESTful 需要在日后继续加强。










