数据层解决方案
# SQL
**sql 是关系型数据库,可以操作结构化数据,但是访问速度不是很快 **
**mysql 默认端口号: 8080 **
# 数据层解决方案技术选型
Druid+MyBatis-Plus+MySQL
数据源
Druid+Hikari
持久化技术
MyBatis-Plus/MyBatis+JdbcTemplate
数据库
MySQL+H2

# 数据源配置
引入Druid的三种方式
直接引入Druid
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.9</version> </dependency>spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///test?useSSL=false&useServerPrepStmts=true username: root password: 1234 type: com.alibaba.druid.pool.DruidDataSource引入Druid-starter
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency>#引入Druid-starter spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///test?useSSL=false&useServerPrepStmts=true username: root password: 1234使用自动配置
只要引入了Druid的坐标,SpirngBoot会自动配置Druid数据源,在yml文件中不需要体现出任何Druid的信息,也可以装配上
不引入Druid,使用内置数据源
HikariCP————轻量级的,速度很快,可以考虑SpringBoot默认内置数据源对象———— 最佳轻量级数据库
com.zaxxer.hikari.HikariDataSourceTomcat 提供DataSource
在HikariCP不可用的情况下,且在web环境中,可以使用tomcat服务器内置的数据源对象
Commons DBCP
一般不会用这个,除非上面两个用不了
配置代码
通用配置最好写在外面,让数据源自己去读即可
HikariCP 的url写在里面会报错,其他三个不会报错,所以尽量把通用配置写在最外面即可
#使用默认的 HikariCP 不导入任何 druid坐标 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///test?useSSL=false&useServerPrepStmts=true username: root password: 1234 hikari: maximum-pool-size: 50
# 持久化技术

# 数据库
内嵌数据库—— 内存级数据库,方便小巧,可用来测试
H2 ————测试的时候 快很多,可以考虑使用,但是不适合上线
导入坐标
<!-- h2 数据库--> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>yml配置
在上线时,一定记得吧 enabled属性设置为false
loacalhost/h2 即可访问,进入h2数据库操作页面
#h2 数据库 spring: h2: console: enabled: true path: /h2 datasource: driver-class-name: org.h2.Driver url: jdbc:h2:~/test username: sa password: 123456测试的时候,跟使用
mysql一模一样
HSQL
Derby
# NoSQL
# Redis
Redis 是一款 key-value 存储结构的 内存级 NoSQL 数据库,访问速度极快
命令行
- 端口号:6379
- 启动服务

运行指令
set key value——》 一个key 对 应一个valueset keys key value——》 一个keys 里面存有多个key-value键值对

redis 基础
- 数据类型

基本命令——String
set key value——设置指定key的值get key——获取指定key的值setex key seconds value——设置指定key的值,并将key的过期时间设置为seconds秒setnx key value——只有在key不存在时设置key,用于分布式锁
基本命令——Hash
hset key field value——将哈希表key中的字段field的值设为value
hget key field——获取存储在哈希表中指定字段的值hdel key field——删除存储在hash表中的指定字段hkeys key——获取哈希表中所有字段hvals key——获取哈希表中所有值
hgetall key——获取在哈希表中指定key的所有字段和值基本命令——List(顺序,string类型)(可重复)
lpush key value[value2]——将一个或多个值插入到列表头部lrange key start stop——获取列表指定范围内的元素lrange key 0 -1——获取全部元素
rpop key——移除并获取列表最后一个元素llen key——获取列表长度brpop key1 [key2] timeout——移出并获取最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
基本命令——Set(String)(唯一,不能重复)
sadd key member1 [member2]——向集合添加一个或多个成员smembers key——返回集合中的所有成员scard key——获取集合的成员数sinter key1 [key2]——返回给定所有集合的交集sunion key [key2]——返回给定所有集合的并集sdiff key1 [key2]——返回给定所有集合的差集(key1-key2)srem key member1 [member2]——移除集合中一个或多个成员
基本命令——sorted set(String)(有序)(唯一,不重复)
每个元素都会关联一个double类型分数(score),通过分数对集合中的成员进行从小到大的排序,成员唯一,分数可重复
zadd key score1 member1 [score2 member2]——向有序集合中添加一个或多个成员,或者更新已存在成员的分数zrange key start stop [withscores]——通过索引区间返回有序集合中指定区间内的成员(携带分数返回)zincrby key increment member——有序集合中对指定成员的分数加上增量increment(一个数,原分数+增量=现分数)zincrby user 20 b
zrem key member [member]——移除有序集合中的一个或多个成员
通用命令
keys pattern——查找所有复合给定模式(pattern)的可以keys *——得到所有key
exists key——检测给定key是否存在type key——返回key所存储值的类型ttl key——返回给定key的剩余时间(time to live) ,以秒为单位del key——该命令用于在可以存在时删除keyselect 1——选择1号数据库
SpringBoot整合Redis
导入坐标
<!-- 引入redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>配置yml
#配置redis spring: redis: host: localhost port: 6379提供操作Redis接口对象
RedisTemplate- 先使用
opsFor-xx获取操作类型,然后在进行读取操作 
- 对象型模板————以这种方式存入的数据库,在命令行窗口直接读取不出来
//需要先启动redis服务 //测试 Redis ------ RedisTemplate 传入的是对象 //以这种方式存入的数据库,在命令行窗口直接读取不出来 @SpringBootTest class RedisTest { @Autowired private RedisTemplate redisTemplate; @Test//普通值 void set() { ValueOperations ops = redisTemplate.opsForValue(); ops.set("age",41); } @Test//普通值 void get() { ValueOperations ops = redisTemplate.opsForValue(); System.out.println(ops.get("age")); } @Test//哈希值 void hset() { HashOperations ops = redisTemplate.opsForHash(); ops.put("info","value","diana"); ops.put("info","name","diane"); ops.put("info","chinese","凉冰"); } @Test//哈希值 void hget() { HashOperations ops = redisTemplate.opsForHash(); System.out.println(ops.get("info","value")); System.out.println(ops.get("info","chinese")); } }- 字符串型模板 ————常用————在命令行窗口可以直接读取出来
//需要先启动redis服务 //测试 Redis ------ StringRedisTemplate 传入的是字符串 //以这种方式存入的数据库,在命令行窗口可以直接读取出来 —————— 常用 @SpringBootTest class RedisTest2 { @Autowired private StringRedisTemplate stringRedisTemplate; @Test//普通值 void set() { ValueOperations<String,String> ops = stringRedisTemplate.opsForValue(); ops.set("age","41"); } @Test//普通值 void get() { ValueOperations<String,String> ops = stringRedisTemplate.opsForValue(); System.out.println(ops.get("k2")); } @Test//哈希值 void hset() { HashOperations<String,String,String> ops = stringRedisTemplate.opsForHash(); ops.put("info","value","diana"); ops.put("info","name","diane"); ops.put("info","chinese","凉冰"); } @Test//哈希值 void hget() { HashOperations<String,String,String> ops = stringRedisTemplate.opsForHash(); System.out.println(ops.get("info","value")); System.out.println(ops.get("info","chinese")); } /** * 操作List类型的数据 */ @Test public void testList(){ ListOperations<String, String> listOperations = stringRedisTemplate.opsForList(); //存值 listOperations.leftPush("mylist","a");//存一个 listOperations.leftPushAll("mylist","b","c","d");//存一堆 //取值(东西还在队列里) List<String> mylist = listOperations.range("mylist", 0, -1); for (String s : mylist) { System.out.println(s); } //获取列表长度 Long size = listOperations.size("mylist"); int isize = size.intValue(); for (int i=0;i<isize;i++){ //出队列 listOperations.rightPop("mylist");//出一个 } } /** * 操作Set类型的数据 ——不能有重复值 */ @Test public void testSet(){ SetOperations<String, String> setOperations = stringRedisTemplate.opsForSet(); //存值 setOperations.add("myset","diana","diane"); //取值 Set<String> myset = setOperations.members("myset"); for (String s : myset) { System.out.println(s); } //删除 setOperations.remove("myset","diana"); } /** * 操作Zset类型 sorted set */ @Test public void testZset(){ ZSetOperations<String, String> zSet = stringRedisTemplate.opsForZSet(); //存值 zSet.add("myzset","a",100); zSet.add("myzset","b",90); zSet.add("myzset","c",80); zSet.add("myzset","d",70); //取值 Set<String> myzset = zSet.range("myzset", 0, -1); for (String s : myzset) { System.out.println(s); } //修改分数(增量) zSet.incrementScore("myzset","b",10); //删除 zSet.remove("myzset","a","c"); //取值 Set<String> myzset1 = zSet.range("myzset", 0, -1); for (String s : myzset1) { System.out.println(s); } } /** * 通用操作 */ @Test public void testCommon(){ //获取redis中的所有key Set<String> keys = stringRedisTemplate.keys("*"); for (String key : keys) { System.out.println(key); } //判断某个key是否存在 Boolean aBoolean = stringRedisTemplate.hasKey("mylist"); System.out.println(aBoolean); //删除指定key stringRedisTemplate.delete("mylist"); //获取指定key对应的value的数据类型 DataType dataType = stringRedisTemplate.type("myset"); System.out.println(dataType.name()); } }- 先使用
Redis客户端方式选择 ——两种方式的操作API是一样的
lettuce(默认)jedis导入坐标
<!-- 引入jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>配置
#配置redis spring: redis: host: localhost port: 6379 client-type: jedis #lettuce 默认 #两种模式操作API是一样的 #配置 lettuce lettuce: pool: max-active: 16 #配置 jedis jedis: pool: max-active: 8
二者区别

# Mongo
Mongo 介于SQL 与Redis之间,既可以操作结构性数据,访问速度也很快
应用场景

命令行
端口号 27017
启动服务
mongod --dbpath=..\data\db
执行客户端
mongo

基本操作
Robo 3T 1.4—— 可视化操作客户端基本的增删改查
//添加数据(文档) 数据类型可以不统一 db.book.save({"name":"SpringBoot"}) db.book.save({"name":"SpringBoot","type":"code"}) //删除操作 db.book.remove({type:"code"}) //修改单个操作 只修改满足条件的第一条数据 第一个{}里是条件,第二个{}里是要修改的值 db.book.update({name:"SpringBoot22"},{$set:{name:"SpringBoot"}}) //修改多个操作 可以修改所有满足条件的数据 db.book.updateMany({name:"SpringBoot"},{$set:{name:"SpringBoot22"}}) //查询所有 db.book.find() //按条件查询 db.book.find({type:"code"})常用命令

SpringBoot整合Mongo
导入坐标
<!-- 引入mongo--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>配置文件
#配置mongo spring: data: mongodb: uri: mongodb://localhost/diana测试————
mongoTemplate每一个操作,都对应一个接口//需要先启动mongo服务 //测试 Mongo @SpringBootTest class MongoTest { @Autowired private MongoTemplate mongoTemplate; @Test //插入 void testSave(){ Book book=new Book(); book.setId(2); book.setName("崩坏三"); book.setType("手游"); book.setDescription("好玩"); mongoTemplate.save(book); } @Test //查询所有 void testFindAll(){ List<Book> all = mongoTemplate.findAll(Book.class); System.out.println(all); } }
# ES
Elasticsearch 是一个分布式全文搜索引擎;;;;全文搜索—— 牺牲空间,换取时间
全文搜索 概念
- 索引 ——id就是一个索引,根据id找数据的过程称为索引—— 红框的右半部分
- 倒排索引—— 根据内容(Spring)找到id的过程称为倒排索引—— 红框的左半部分
- 创建文档——根据spring-1-1xx(即id为1的部分数据) 创建一个文档 (实战-1-1xx)
- 使用文档—— 根据文档去查出所有数据的过程

命令行
- 端口号 9200
- 直接双击
elasticsearch.bat,开启服务
创建索引
使用postman给es创建索引,全面支持restful
使用
get请求,得到索引http://localhost:9200/books——GET使用
delete请求,删除索引http://localhost:9200/books——DELETE使用
put请求,带上下面的json字符串,创建索引http://localhost:9200/books——PUTtypekeyword: 作为被查询的关键字text: 普通文本
analyzer——ik_max_word- 表示使用ik分词器,es文件夹要存放在不带空格的目录下,要不然插件用不了,会报错
copy_to- 表示将搜索内容传给 all一份
allall作为一个虚拟的属性,用来保证可以同时查询name和description,和copy_to配合使用
{ "mappings":{ "properties":{ "id":{ "type":"keyword" }, "name":{ "type":"text", "analyzer":"ik_max_word", "copy_to":"all" }, "type":{ "type":"keyword" }, "description":{ "type":"text", "analyzer":"ik_max_word", "copy_to":"all" }, "all":{ "type":"text", "analyzer":"ik_max_word" } } } }创建文档
POSThttp://localhost:9200/books/_doc——使用系统生成idPOSThttp://localhost:9200/books/_doc/10——使用指定id,不存在则创建,存在则修改POSThttp://localhost:9200/books/_create/1——使用系统生成id ,只能创建
删除文档
DELETEhttp://localhost:9200/books/_doc/1——按照id删除
更新文档
PUThttp://localhost:9200/books/_doc/1——全覆盖修改,提交的json数据会全面覆盖原来的数据{ "name":"springBoot very good", "type":"diana", "description":"pringBoot2 very good" }POSThttp://localhost:9200/books/_update/1——修改单个属性/_doc/id也可以{ "doc":{ "name":"springBoot" } }
查询文档
GEThttp://localhost:9200/books/_search——查询全部GEThttp://localhost:9200/books/_doc/1——按照id查询GEThttp://localhost:9200/books/_search?q=description:good——按照参数值进行条件查询
SpringBoot整合es
引入坐标
<!-- 引入es high-level--> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency>配置yml
SpringBoot没有整合,所以不能直接在这里配置了
创建简单索引
要手动创建对象,手动关闭
- 写在一起 不推荐
@Test //使用es高版本客户端,因为springboot没有整合,所以 要使用硬编码的方式 来进行配置 void testCreateIndex() throws IOException { //创建客户端 HttpHost host=HttpHost.create("http://localhost:9200"); RestClientBuilder builder= RestClient.builder(host); client=new RestHighLevelClient(builder); //创建索引 CreateIndexRequest request=new CreateIndexRequest("diana"); client.indices().create(request, RequestOptions.DEFAULT); client.close(); }- 使用AOP思想,实现,前置和后置方法 ————》推荐
private RestHighLevelClient client; @BeforeEach //所有方法执行前 --AOP思想 void setUp() { //创建客户端 HttpHost host=HttpHost.create("http://localhost:9200"); RestClientBuilder builder= RestClient.builder(host); client=new RestHighLevelClient(builder); } @AfterEach //所有方法执行后 --AOP思想 void tearDown() throws IOException { //关闭客户端 client.close(); } @Test //使用es高版本客户端,因为springboot没有整合,所以 要使用硬编码的方式 来进行配置 void testCreateIndex() throws IOException { //创建客户端 //创建索引 CreateIndexRequest request=new CreateIndexRequest("users"); client.indices().create(request, RequestOptions.DEFAULT); //关闭客户端 }创建带参索引
request.source(json, XContentType.JSON);//前面是参数,后面是类型
@Test //带上配置创建索引 void testCreateIndexJson() throws IOException { //创建客户端 String json="{\n" + " \"mappings\":{\n" + " \"properties\":{\n" + " \"id\":{\n" + " \"type\":\"keyword\"\n" + " },\n" + " \"name\":{\n" + " \"type\":\"text\",\n" + " \"analyzer\":\"ik_max_word\",\n" + " \"copy_to\":\"all\"\n" + " },\n" + " \"type\":{\n" + " \"type\":\"keyword\"\n" + " },\n" + " \"description\":{\n" + " \"type\":\"text\",\n" + " \"analyzer\":\"ik_max_word\",\n" + " \"copy_to\":\"all\"\n" + " },\n" + " \"all\":{\n" + " \"type\":\"text\",\n" + " \"analyzer\":\"ik_max_word\"\n" + " }\n" + " }\n" + " }\n" + "}"; //创建索引 CreateIndexRequest request=new CreateIndexRequest("books"); request.source(json, XContentType.JSON);//前面是参数,后面是类型 client.indices().create(request, RequestOptions.DEFAULT); //关闭客户端 }创建单条文档
@Test //创建一条文档 void testCreateOneDoc() throws IOException { Book book = mapper.selectById(3); String id=book.getId().toString(); //获取id Integer 类型 的数据 有toString 方法 因为他是个对象 String json = JSON.toJSONString(book); //获取json数据 //创建请求 // /users/_doc/1 IndexRequest request=new IndexRequest("books").id(id); //携带参数 request.source(json,XContentType.JSON); // 创建文档 client.index(request,RequestOptions.DEFAULT); }批量创建所有文档
BulkRequest bulkRequest=new BulkRequest();——创建批处理请求容器bulkRequest.add(request);———— 将单个请求加入容器
@Test //创建所有数据的文档 void testCreateAllDoc() throws IOException { List<Book> books = mapper.selectList(null); //创建批处理请求容器 BulkRequest bulkRequest=new BulkRequest(); for(Book book:books){ String id=book.getId().toString(); //获取id Integer 类型 的数据 有toString 方法 因为他是个对象 String json = JSON.toJSONString(book); //获取json数据 //创建请求 // /users/_doc/1 IndexRequest request=new IndexRequest("books").id(id); //携带参数 request.source(json,XContentType.JSON); //将请求加入容器 bulkRequest.add(request); } //创建批量文档 一次性完成多个请求 client.bulk(bulkRequest,RequestOptions.DEFAULT); }按id查询文档
@Test //按id查询 void testGetById() throws IOException { GetRequest request= new GetRequest("books","2");//索引名称,id GetResponse res = client.get(request, RequestOptions.DEFAULT); //得到返回数据中的source————里面存储的是数据信息 String json = res.getSourceAsString(); System.out.println(json); }按条件查询文档
@Test //按条件查询 void testGetByCon() throws IOException { SearchRequest request= new SearchRequest("books"); SearchSourceBuilder builder=new SearchSourceBuilder(); //插入查询条件 builder.query(QueryBuilders.termQuery("name","丽"));//查找name中包含丽字的词条 request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); //查询得到所有的hits 命中 SearchHits hits = response.getHits(); //遍历得到的所有hits,从中取出Source 并转换成字符串 for(SearchHit hit:hits){ String json = hit.getSourceAsString(); Book book = JSON.parseObject(json, Book.class); System.out.println(book); } }更新文档(修改单个属性)
@Test //更新文档———— 修改单个属性 void testUpdateAll() throws IOException { UpdateRequest request=new UpdateRequest("books","3"); String json=" {\n" + " \"name\":\"diane\"\n" + " }"; request.doc(json, XContentType.JSON); client.update(request, RequestOptions.DEFAULT); }删除文档
@Test //删除文档 void testDelete() throws IOException { DeleteRequest request=new DeleteRequest("books","4"); client.delete(request, RequestOptions.DEFAULT); }
