SpringBoot开发实用篇之数据层解决方案

本文最后更新于:13 天前

springboot技术提供了3种内置的数据源技术,分别是Hikari、tomcat内置数据源、DBCP,可以根据需要自行选择。

SpringBoot内置JdbcTemplate持久化解决方案,使用JdbcTemplate需要导入spring-boot-starter-jdbc的坐标。

springboot提供了3款内置的java语言开发的数据库,分别是H2、HSQL、Derby。内置数据库仅用于开发阶段,线上项目请务必关闭控制台功能。

Redis是一款采用key-value数据存储格式的内存级NoSQL数据库,支持多种数据存储格式、支持持久化、注册集群。可以有效的提高数据访问速度,但数据格式单一性,无法操作结构化数据。

springboot整合redis步骤:

  1. 导入springboot整合redis的starter坐标
  2. 进行基础配置
  3. 使用springboot整合redis的专用客户端接口RedisTemplate、StringRedisTemplate操作。

springboot整合MongoDB步骤:

  1. 导入springboot整合MongoDB的starter坐标;
  2. 进行基础配置;
  3. 使用springboot整合MongoDB的专用客户端接口MongoTemplate操作。

SQL

前面做SSMP整合的时候数据层使用的是MySQL数据库与MyBatisPlus框架,后面又学了Druid数据源的配置,所以现在数据层解决方案可以说是Mysql+Druid+MyBatisPlus。而三个技术分别对应了数据层操作的三个层面:

  • 数据源技术:Druid、Hikari、tomcat DataSource、DBCP
  • 持久化技术:MyBatisPlus、MyBatis、JdbcTemplate
  • 数据库技术:MySQL、H2、HSQL、Derby

数据源技术

目前我们使用的数据源技术是Druid,yml中配置方式如下:

运行时可以在日志中看到对应的数据源初始化信息,具体如下:

如果不使用Druid数据源,程序运行后是什么样子呢?是独立的数据库连接对象还是有其他的连接池技术支持呢?将Druid技术对应的starter去掉再次运行程序可以在日志中找到如下初始化信息:

虽然没有DruidDataSource相关的信息了,但是我们发现日志中有HikariDataSource这个信息,这就是springboot的内嵌数据源。


springboot提供了3款内嵌数据源技术,分别如下:

  • HikariCP:是springboot官方推荐的数据源技术,作为默认内置数据源使用。不配置数据源,默认使用这个。
  • Tomcat提供的DataSource:不想用HikartCP,并且使用tomcat作为web服务器进行web程序的开发时,使用这个。要使用tomcat提供的默认数据源对象时,只需要把HikartCP技术的坐标排除掉就可以了。
  • Commons DBCP:不使用HikartCP也不使用tomcat的DataSource时,默认使用Commons DBCP。

配置druid时使用druid的starter后对应的配置如下:

1
2
3
4
5
6
7
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root

换成是默认的数据源HikariCP后,只需druid的starter删掉即可,配置如下:

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root

也可以写上是对hikari做的配置,但是url地址要单独配置:

1
2
3
4
5
6
7
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root

如果想对hikari做进一步的配置,可以继续配置其独立的属性。例如:

1
2
3
4
5
6
7
8
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
maximum-pool-size: 50

如果不想使用hikari数据源,使用tomcat的数据源或者DBCP配置格式也是一样的。以后我们做数据层时,数据源对象的选择就不再是单一的使用druid数据源技术了,可以根据需要自行选择。

持久化技术

Spring给开发者提供了一套现成的数据层技术,叫做JdbcTemplate。在springboot中,它也是存在的。所谓JdbcTemplate其实就是回归到jdbc最原始的编程形式来进行数据层的开发。使用步骤如下:

步骤①:导入jdbc对应的坐标,记得是starter

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency

步骤②:自动装配JdbcTemplate对象

1
2
3
4
5
6
@SpringBootTest
class Springboot15SqlApplicationTests {
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
}
}

步骤③:使用JdbcTemplate实现查询操作(非实体类封装数据的查询操作)

1
2
3
4
5
6
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
String sql = "select * from tbl_book";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
System.out.println(maps);
}


步骤④:使用JdbcTemplate实现查询操作(实体类封装数据的查询操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){

String sql = "select * from tbl_book";
RowMapper<Book> rm = new RowMapper<Book>() {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book temp = new Book();
temp.setId(rs.getInt("id"));
temp.setName(rs.getString("name"));
temp.setType(rs.getString("type"));
temp.setDescription(rs.getString("description"));
return temp;
}
};
List<Book> list = jdbcTemplate.query(sql, rm);
System.out.println(list);
}


步骤⑤:使用JdbcTemplate实现增删改操作

1
2
3
4
5
@Test
void testJdbcTemplateSave(@Autowired JdbcTemplate jdbcTemplate){
String sql = "insert into tbl_book values(null,'springboot1','springboot2','springboot3')";
jdbcTemplate.update(sql);
}

如果想对JdbcTemplate对象进行相关配置,可以在yml文件中进行设定,具体如下:

1
2
3
4
5
6
spring:
jdbc:
template:
query-timeout: -1 # 查询超时时间
max-rows: 500 # 最大行数
fetch-size: -1 # 缓存行数

数据库技术

springboot不仅给开发者提供了内置的数据源解决方案和持久化解决方案,还提供有内置的数据库解决方案。

springboot提供了3款内置的java语言开发的数据库,分别是

  • H2
  • HSQL
  • Derby

以上三款数据库除了可以独立安装之外,还可以像tomcat服务器一样,采用内嵌的形式运行在spirngboot容器中。由于运行在内存中,进行测试工作时数据无需存储在磁盘上,方便进行功能测试,这也是内嵌数据库的最大优点。


步骤①:导入H2数据库对应的坐标,一共2个

1
2
3
4
5
6
7
8
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

步骤②:将工程设置为web工程,启动工程时启动H2数据库

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

步骤③:通过配置开启H2数据库控制台访问程序,也可以使用其他的数据库连接软件操作

1
2
3
4
5
spring:
h2:
console:
enabled: true
path: /h2

web端访问路径 /h2,访问密码 123456,如果访问失败,先配置下列数据源,启动程序运行后再次访问/h2路径就可以正常访问了

1
2
3
4
5
6
datasource:
url: jdbc:h2:~/test
hikari:
driver-class-name: org.h2.Driver
username: sa
password: 123456


步骤④:使用JdbcTemplate或MyBatisPlus技术操作数据库

创建表

插入数据

查询数据

测试案例

其实我们只是换了一个数据库而已,其他的东西都不受影响。一个重要提醒,H2数据库控制台仅用于开发阶段,线上项目请务必关闭控制台功能,改用MySQL数据库作为数据持久化方案。关闭方式就是设置enabled属性为false即可。

NoSQL

NoSQL是什么意思呢?从字面来看,No表示否定,NoSQL就是非关系型数据库解决方案,意思就是数据该存存该取取,只是这些数据不放在关系型数据库中了。在springboot官方文档中提供了10种相关技术的整合方案,我们将讲解国内市场上最流行的几款NoSQL数据库整合方案,分别是Redis、MongoDB、ES。

SpringBoot整合Redis

Redis是一款采用key-value数据存储格式的内存级NoSQL数据库,支持多种数据存储格式、支持持久化、注册集群。

Redis数据存储格式为1key-value格式,也就是键值对的存储形式。与MySQL数据库不同,MySQL数据库有表、有字段、有记录,Redis没有这些东西,就是一个名称对应一个值,并且数据以存储在内存中使用为主。

什么叫以存储在内存中为主?其实Redis有它的数据持久化方案,分别是RDB和AOF,但是Redis自身并不是为了数据持久化而生的,主要是在内存中保存数据,加速数据访问的,所以说是一款内存级数据库。

Redis支持多种数据存储格式,比如可以直接存字符串,也可以存一个map集合,list集合,后面会涉及到一些不同格式的数据操作。

安装

windows版安装包下载地址:https://github.com/tporadowski/redis/releases

下载的安装包有两种形式,一种是一键安装的msi文件,还有一种是解压缩就能使用的zip文件。本课程采用的是msi一键安装的msi文件进行安装的。(msi文件不仅安装软件,还帮你把安装软件时需要的功能关联在一起,比如如安装序列、创建和设置安装路径、设置系统依赖项、默认设定安装选项和控制安装过程的属性。)

安装完毕后会得到如下文件,其中有两个文件对应两个命令,是启动Redis的核心命令,需要再CMD命令行模式执

启动服务器命令为:

1
redis-server.exe redis.windows.conf

如果启动redis服务器失败,可以先在新的cmd窗口执行命令redis-cli.exe启动客户端,然后执行shutdown命令后退出,此时redis服务器就可以正常执行了。

redis服务器对外服务端口,默认为6379。

基本操作

服务器启动后,使用客户端就可以连接服务器,类似于启动完MySQL数据库,然后启动SQL命令行操作数据库。

放置一个字符串数据到redis中,先为数据定义一个名称,比如name或age ,然后使用命令set设置数据到redis服务器中即可:

1
2
set name gaojie
set age 22

从redis中取出已经放入的数据,根据名称取,就可以得到对应数据。如果没有对应数据就会得到 (nil):

1
2
get name
get age

以上使用的数据存储是一个名称对应一个值,如果要维护的数据过多,可以使用别的数据存储结构。例如 hash,它是一种一个名称下可以存储多个数据的存储模型,并且每个数据也可以有自己的二级存储名称。向hash结构中存储数据格式如下:

1
2
hset keya a1 aa1		#对外key名称是a,在名称为a的存储模型中,a1这个key中保存了数据aa1
hset keyb b2 bb2

获取hash结构中的数据命令如下

1
2
hget a a1			#得到aa1
hget a a2 #得到aa2

整合

springboot整合任何技术其实就是在springboot中使用对应技术的API。所谓整合其实就是使用springboot技术去管理其他技术,几个问题是躲不掉:

  • 需要先导入对应技术的坐标,而整合之后,这些坐标都有了一些变化;

  • 任何技术通常都会有一些相关的设置信息,整合之后,这些信息如何写,写在哪里;

  • 第三,没有整合之前操作如果是模式A的话,整合之后如果没有给开发者带来一些便捷操作,那整合将毫无意义,所以整合后操作肯定要简化一些,那对应的操作方式自然也有所不同;

按上面的三个问题去思考springboot整合所有技术是一种通用思想,在整合的过程中会逐步摸索出整合的套路,而且适用性非常强,经过若干种技术的整合后基本上可以总结出一套固定思维。


RedisTemplate

RedisTemplate是以对象作为key和value,内部对数据进行序列化。

步骤①:导入springboot整合redis的starter坐标

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

上述坐标也可以在创建模块的时候通过勾选的形式进行选择,归属NoSQL分类中


步骤②:进行基础配置

1
2
3
4
spring:
redis:
host: localhost
port: 6379

操作redis,最基本的信息就是操作哪一台redis服务器,所以服务器地址属于基础配置信息,不可缺少。但是即便你不配置,目前也是可以用的。因为以上两组信息都有默认配置,刚好就是上述配置值。


步骤③:使用springboot整合redis的专用客户端接口操作,此处使用的是RedisTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@SpringBootTest
class Springboot16RedisApplicationTests {

@Autowired
private RedisTemplate redisTemplate;

@Test
void set() {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("age",41);
}
@Test
void get() {
ValueOperations ops = redisTemplate.opsForValue();
Object age = ops.get("name");
System.out.println(age);
}
@Test
void hset() {
HashOperations ops = redisTemplate.opsForHash();
ops.put("info","a","aa");
}
@Test
void hget() {
HashOperations ops = redisTemplate.opsForHash();
Object val = ops.get("info", "a");
System.out.println(val);
}
}


在操作redis时,需要先确认操作何种数据,根据数据种类得到操作接口。例如使用 opsForValue() 获取string类型的数据操作接口,使用opsForHash()获取hash类型的数据操作接口,剩下的就是调用对应api操作了。各种类型的数据操作接口如下:



StringRedisTemplate(常用)

由于redis内部不提供java对象的存储格式,因此当操作的数据以对象的形式存在时,会进行转码,转换成字符串格式后进行操作。为了方便开发者使用基于字符串为数据的操作,springboot整合redis时提供了专用的API接口StringRedisTemplate,以字符串作为key和value,与Redis客户端操作等效。可以理解为这是RedisTemplate的一种指定数据泛型的操作API。

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
public class StringRedisTemplateTest {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Test
void get(){
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
String name = ops.get("name");
System.out.println(name);
}
}

redis客户端选择

springboot整合redis技术提供了多种客户端兼容模式,默认提供的是lettucs客户端技术,也可以根据需要切换成指定客户端技术,例如jedis客户端技术,切换成jedis客户端技术操作步骤如下:

步骤①:导入jedis坐标

1
2
3
4
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

jedis坐标受springboot管理,无需提供版本号。


步骤②:配置客户端技术类型,设置为jedis

1
2
3
4
5
spring:
redis:
host: localhost
port: 6379
client-type: jedis

步骤③:根据需要设置对应的配置

1
2
3
4
5
6
7
8
9
10
11
spring:
redis:
host: localhost
port: 6379
client-type: jedis
lettuce:
pool:
max-active: 16
jedis:
pool:
max-active: 16


lettcus与jedis区别

  • jedis连接Redis服务器是直连模式,当多线程模式下使用jedis会存在线程安全问题,解决方案可以通过配置连接池使每个连接专用,这样整体性能就大受影响。
  • lettcus基于Netty框架进行与Redis服务器连接,底层设计中采用StatefulRedisConnection。StatefulRedisConnection自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。当然lettcus也支持多连接实例一起工作。

SpringBoot整合MongoDB

Redis技术可以有效的提高数据访问速度,但由于Redis的数据格式单一性,无法操作结构化数据,当操作对象型的数据时,Redis就无法满足要求了,下面了解springboot如何整合MongoDB技术。

MongoDB是一个开源、高性能、无模式的文档型数据库,它是NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库。

所谓无模式,简单说就是作为一款数据库,没有固定的数据存储结构,第一条数据可能有A、B、C一共3个字段,第二条数据可能有D、E、F也是3个字段,第三条数据可能是A、C、E3个字段,也就是说数据的结构不固定,这就是无模式。无模式的优点是灵活,随时变更,不受约束。

基于上述特点,MongoDB的应用面也会产生一些变化。以下列出了一些可以使用MongoDB作为数据存储的场景,但是并不是必须使用MongoDB的场景:

  • 淘宝用户数据
    • 存储位置:数据库
    • 特征:永久性存储,修改频度极低
  • 游戏装备数据、游戏道具数据
    • 存储位置:数据库、Mongodb
    • 特征:永久性存储与临时存储相结合、修改频度较高
  • 直播数据、打赏数据、粉丝数据
    • 存储位置:数据库、Mongodb
    • 特征:永久性存储与临时存储相结合,修改频度极高
  • 物联网数据
    • 存储位置:Mongodb
    • 特征:临时存储,修改频度飞速

安装

windows版安装包下载地址:https://www.mongodb.com/try/download

采用解压缩zip文件进行安装,解压缩完毕后会得到如下文件,其中bin目录包含了所有mongodb的可执行命令:

mongodb在运行时需要指定一个数据存储的目录,所以创建一个数据存储目录,通常放置在安装目录中,此处创建 data\db 的目录用来存储数据,具体如下:


启动服务器

1
mongod --dbpath=..\data\db

启动服务器时需要指定数据存储位置,通过参数 –dbpath 进行设置,可以根据需要自行设置数据存储路径。默认服务端口27017。

启动客户端

1
mongo --host=127.0.0.1 --port=27017

基本操作

MongoDB虽然是一款数据库,但是它的操作并不是使用SQL语句进行的,好在有一些类似于Navicat的数据库客户端软件,能够便捷的操作MongoDB,先安装一个客户端,再来操作MongoDB。

本次安装的软件时Robo3t,Robot3t是一款绿色软件,无需安装,解压缩即可。解压缩完毕后进入安装目录双击robot3t.exe即可使用:

打开软件首先要连接MongoDB服务器,选择【File】菜单,选择【Connect…】:

进入连接管理界面后,选择左上角的【Create】链接,创建新的连接设置:

如果输入设置值即可连接(默认不修改即可连接本机27017端口):

连接成功后在命令输入区域输入命令即可操作MongoDB。


  • 创建数据库:在左侧菜单中使用右键创建,输入数据库名称即可

  • 创建集合:在Collections上使用右键创建,输入集合名称即可,集合等同于数据库中的表的作用

  • 新增文档:(文档是一种类似json格式的数据,可以先把数据理解为就是json数据)

    1
    db.集合名称.insert/save/insertOne(文档)

    查询全部

    条件查询

  • 删除文档

    1
    db.集合名称.remove(条件)

  • 修改文档

    1
    db.集合名称.update(条件,{操作种类:{文档}})

  • 查询文档

    • 基础查询

      • 查询全部:db.集合.find()
      • 查第一条:db.集合.findOne()
      • 查询指定数量文档:db.集合.find().limit(10) //查10条文档
      • 跳过指定数量文档:db.集合.find().skip(20) //跳过20条文档
      • 统计:db.集合.count()
      • 排序:db.集合.sort({age:1}) //按age升序排序
      • 投影:db.集合名称.find(条件,{name:1,age:1}) //仅保留name与age域
    • 条件查询

      • 基本格式:db.集合.find({条件})
      • 模糊查询:db.集合.find({域名:/正则表达式/}) //等同SQL中的like,比like强大,可以执行正则所有规则
      • 条件比较运算:db.集合.find({域名:{$gt:值}}) //等同SQL中的数值比较操作,例如:name>18
      • 包含查询:db.集合.find({域名:{$in:[值1,值2]}}) //等同于SQL中的in
      • 条件连接查询:db.集合.find({$and:[{条件1},{条件2}]}) //等同于SQL中的and、or

整合

使用springboot整合MongDB该如何进行呢?导入坐标,做配置,使用API接口操作。整合Redis如此,整合MongoDB同样如此。

下面就开始springboot整合MongoDB,操作步骤如下:

步骤①:导入springboot整合MongoDB的starter坐标

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

上述坐标也可以在创建模块的时候通过勾选的形式进行选择,同样归属NoSQL分类中:


步骤②:进行基础配置

1
2
3
4
spring:
data:
mongodb:
uri: mongodb://localhost/itheima

操作MongoDB需要的配置与操作redis一样,最基本的信息都是操作哪一台服务器,区别就是连接的服务器IP地址和端口不同,书写格式不同而已。


步骤③:使用springboot整合MongoDB的专用客户端接口MongoTemplate来进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@SpringBootTest
class Springboot17MongodbApplicationTests {

@Autowired
private MongoTemplate mongoTemplate;

@Test
void testSave() {
Book book = new Book();
book.setId(2);
book.setName("springboot2");
book.setType("springboot2");
book.setDescription("springboot2");
mongoTemplate.save(book);
}

@Test
void testFind(){
List<Book> all = mongoTemplate.findAll(Book.class);
System.out.println(all);
}
}

SpringBoot整合ES

Redis可以使用内存加载数据并实现数据快速访问,MongoDB可以在内存中存储类似对象的数据并实现数据的快速访问,在企业级开发中对于速度的追求是永无止境的。下面是另一款NoSQL解决方案,只不过他的作用不是为了直接加速数据的读写,而是加速数据的查询,叫做ES技术。

ES(Elasticsearch)是一个分布式全文搜索引擎,重点是全文搜索。

什么是全文搜索呢?比如用户要买一本书,以Java为关键字进行搜索,不管是书名中还是书的介绍中,甚至是书的作者名字,只要包含java就作为查询结果返回给用户查看,上述过程就使用了全文搜索技术。搜索的条件不再是仅用于对某一个字段进行比对,而是在一条数据中使用搜索条件去比对更多的字段,只要能匹配上就列入查询结果,这就是全文搜索的目的。而ES技术就是一种可以实现上述效果的技术。

要实现全文搜索的效果,不可能使用数据库中like操作去进行比对,这种效率太低了。ES设计了一种全新的思想,来实现全文搜索。具体操作过程如下:

  1. 将被查询的字段的数据全部文本信息进行查分,分成若干个词

    • 例如“中华人民共和国”就会被拆分成三个词,分别是“中华”、“人民”、“共和国”,此过程有专业术语叫做分词。分词的策略不同,分出的效果不一样,不同的分词策略称为分词器。
  2. 将分词得到的结果存储起来,对应每条数据的id

    • 例如id为1的数据中名称这一项的值是“中华人民共和国”,那么分词结束后,就会出现“中华”对应id为1,“人民”对应id为1,“共和国”对应id为1

    • 例如id为2的数据中名称这一项的值是“人民代表大会“,那么分词结束后,就会出现“人民”对应id为2,“代表”对应id为2,“大会”对应id为2

    • 此时就会出现如下对应结果,按照上述形式可以对所有文档进行分词。需要注意分词的过程不是仅对一个字段进行,而是对每一个参与查询的字段都执行,最终结果汇总到一个表格中

      分词结果关键字 对应id
      中华 1
      人民 1,2
      共和国 1
      代表 2
      大会 2
  3. 当进行查询时,如果输入“人民”作为查询条件,可以通过上述表格数据进行比对,得到id值1,2,然后根据id值就可以得到查询的结果数据了。

上述过程中分词结果关键字内容每一个都不相同,作用有点类似于数据库中的索引,是用来加速数据查询的。但是数据库中的索引是对某一个字段进行添加索引,而这里的分词结果关键字不是一个完整的字段值,只是一个字段中的其中的一部分内容。并且索引使用时是根据索引内容查找整条数据,全文搜索中的分词结果关键字查询后得到的并不是整条的数据,而是数据的id,要想获得具体数据还要再次查询,因此这里为这种分词结果关键字起了一个全新的名称,叫做倒排索引

通过上述内容的学习,发现使用ES其实准备工作还是挺多的,必须先建立文档的倒排索引,然后才能继续使用。

安装

windows版安装包下载地址:https://www.elastic.co/cn/downloads/elasticsearch

下载的安装包是解压缩就能使用的zip文件,解压缩完毕后会得到如下文件:

  • bin目录:包含所有的可执行命令
  • config目录:包含ES服务器使用的配置文件
  • jdk目录:此目录中包含了一个完整的jdk工具包,版本17,当ES升级时,使用最新版本的jdk确保不会出现版本支持性不足的问题
  • lib目录:包含ES运行的依赖jar文件
  • logs目录:包含ES运行后产生的所有日志文件
  • modules目录:包含ES软件中所有的功能模块,也是一个一个的jar包。和jar目录不同,jar目录是ES运行期间依赖的jar包,modules是ES软件自己的功能jar包
  • plugins目录:包含ES软件安装的插件,默认为空

启动服务器

双击elasticsearch.bat文件即可启动ES服务器,默认服务端口9200。

通过浏览器访问http://localhost:9200看到如下信息视为ES服务器正常启动:

基本操作

ES中保存有我们要查询的数据,只不过格式和数据库存储数据格式不同而已。在ES中我们要先创建倒排索引,这个索引的功能又点类似于数据库的表,然后将数据添加到倒排索引中,添加的数据称为文档。所以要进行ES的操作要先创建索引,再添加文档,这样才能进行后续的查询操作。

要操作ES可以通过Rest风格的请求来进行,也就是说发送一个请求就可以执行一个操作。比如新建索引,删除索引这些操作都可以使用发送请求的形式来进行。

索引操作

  • 创建索引(books是索引名称,下同)

    1
    PUT请求		http://localhost:9200/books

    发送请求后,看到如下信息即索引创建成功:

    重复创建已经存在的索引会出现错误信息,reason属性中描述错误原因

  • 查询索引

    1
    GET请求		http://localhost:9200/books

    查询索引得到索引相关信息,如下:

    如果查询了不存在的索引,会返回错误信息,例如查询名称为book的索引后信息如下:

  • 删除索引

    1
    DELETE请求	http://localhost:9200/books

    删除所有后,给出删除结果:

    如果重复删除,会给出错误信息,同样在reason属性中描述具体的错误原因:

  • 创建索引并指定分词器

    前面创建的索引是未指定分词器的,可以在创建索引时添加请求参数,设置分词器。目前国内较为流行的分词器是IK分词器,使用前先在下对应的分词器,然后使用。

    IK分词器下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

    分词器下载后解压到ES安装目录的plugins目录中即可,安装分词器后需要重新启动ES服务器。使用IK分词器创建索引格式:

    1
    PUT请求		http://localhost:9200/books

    请求参数如下(注意是json格式的参数):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    {
        "mappings":{ #定义mappings属性,替换创建索引时对应的mappings属性
            "properties":{ #定义索引中包含的属性设置
                "id":{ #设置索引中包含id属性
                    "type":"keyword" #当前属性可以被直接搜索
                },
                "name":{ #设置索引中包含name属性
                    "type":"text",              #当前属性是文本信息,参与分词  
    "analyzer":"ik_max_word",   #使用IK分词器进行分词             
    "copy_to":"all" #分词结果拷贝到all属性中
                },
                "type":{
                    "type":"keyword"
                },
                "description":{
                    "type":"text",                 
    "analyzer":"ik_max_word",                
    "copy_to":"all"
                },
                "all":{ #定义属性,用来描述多个字段的分词结果集合,当前属性可以参与查询
                    "type":"text",                 
    "analyzer":"ik_max_word"
                }
            }
        }
    }

    创建完毕后返回结果和不使用分词器创建索引的结果是一样的,此时可以通过查看索引信息观察到添加的请求参数mappings已经进入到了索引属性中:


目前我们已经有了索引了,但是索引中还没有数据,所以要先添加数据,ES中称数据为文档,下面进行文档操作。

文档操作

  • 添加文档(三种方式)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    POST请求	http://localhost:9200/books/_doc		#使用系统生成id
    POST请求 http://localhost:9200/books/_create/1 #使用指定id
    POST请求 http://localhost:9200/books/_doc/1 #使用指定id,不存在创建,存在更新(版本递增)

    文档通过请求参数传递,数据格式json
    {
        "name":"springboot",
        "type":"springboot",
        "description":"springboot"
    }

  • 查询文档

    1
    2
    GET请求	http://localhost:9200/books/_doc/1		 #查询单个文档 		
    GET请求 http://localhost:9200/books/_search #查询全部文档

  • 条件查询

    1
    GET请求	http://localhost:9200/books/_search?q=name:springboot	# q=查询属性名:查询属性值

  • 删除文档

    1
    DELETE请求	http://localhost:9200/books/_doc/1

  • 修改文档(全量更新)

    1
    2
    3
    4
    5
    6
    PUT请求	http://localhost:9200/books/_doc/1

    //文档通过请求参数传递,数据格式json
    {
        "name":"springboot 好用", //未提供的属性值直接抹掉
    }

  • 修改文档(部分更新)

    1
    2
    3
    4
    5
    6
    7
    8
    POST请求	http://localhost:9200/books/_update/1

    //文档通过请求参数传递,数据格式json
    {
    "doc":{ //部分更新并不是对原始文档进行更新,而是对原始文档对象中的doc属性中的指定属性更新
         "name":"springboot" //仅更新提供的属性值,未提供的属性值不参与更新操作
    }
    }

整合

使用springboot整合ES该如何进行呢?导入坐标,做配置,使用API接口操作。整合Redis如此,整合MongoDB如此,整合ES依然如此。这就是springboot的强大之处,所有东西都做成相同规则,对开发者来说非常友好。

下面就开始springboot整合ES,操作步骤如下:

步骤①:导入springboot整合ES的starter坐标

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

步骤②:进行基础配置

1
2
3
4
spring:
elasticsearch:
rest:
uris: http://localhost:9200

配置ES服务器地址,端口9200。


步骤③:使用springboot整合ES的专用客户端接口ElasticsearchRestTemplate来进行操作

1
2
3
4
5
6
7
@SpringBootTest
class Springboot18EsApplicationTests {

@Autowired
private ElasticsearchRestTemplate template;

}

上述操作形式是ES早期的操作方式,使用的客户端被称为Low Level Client,这种客户端操作方式性能方面略显不足,于是ES开发了全新的客户端操作方式,称为High Level Client。高级别客户端与ES版本同步更新,但是springboot最初整合ES的时候使用的是低级别客户端,所以企业开发需要更换成高级别的客户端模式。



下面使用高级别客户端方式进行springboot整合ES,操作步骤如下:

步骤①:导入springboot整合ES高级别客户端的坐标,此种形式目前没有对应的starter

1
2
3
4
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

步骤②:使用编程的形式设置连接的ES服务器,并获取客户端对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootTest
class Springboot18EsApplicationTests {

private RestHighLevelClient client;

@Test
void testCreateClient() throws IOException {
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);

client.close();
}
}

配置ES服务器地址与端口9200,记得客户端使用完毕需要手工关闭。由于当前客户端是手工维护的,因此不能通过自动装配的形式加载对象。


步骤③:使用客户端对象操作ES,例如创建索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SpringBootTest
class Springboot18EsApplicationTests {

private RestHighLevelClient client;

@Test
void testCreateIndex() throws IOException {
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);

CreateIndexRequest request = new CreateIndexRequest("books");
client.indices().create(request, RequestOptions.DEFAULT);

client.close();
}
}

高级别客户端操作是通过发送请求的方式完成所有操作的,ES针对各种不同的操作,设定了各式各样的请求对象,上例中创建索引的对象是CreateIndexRequest,其他操作也会有自己专用的Request对象。

当前操作我们发现,无论进行ES何种操作,第一步永远是获取RestHighLevelClient对象,最后一步永远是关闭该对象的连接。在测试中可以使用测试类的特性去帮助开发者一次性的完成上述操作,但是在业务书写时,还需要自行管理。将上述代码格式转换成使用测试类的初始化方法和销毁方法进行客户端对象的维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@SpringBootTest
class Springboot18EsApplicationTests {

@BeforeEach //在测试类中每个操作运行前运行的方法
void setUp() {
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
}

@AfterEach //在测试类中每个操作运行后运行的方法
void tearDown() throws IOException {
client.close();
}

private RestHighLevelClient client;

@Test
void testCreateIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest("books");
client.indices().create(request, RequestOptions.DEFAULT);
}
}

下面使用上述模式将所有的ES操作执行一遍,测试结果:

  • 创建索引(IK分词器)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    @Test
    void testCreateIndexByIK() throws IOException {
    CreateIndexRequest request = new CreateIndexRequest("books");
    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" +
    "}";
    //设置请求中的参数
    request.source(json, XContentType.JSON);
    client.indices().create(request, RequestOptions.DEFAULT);
    }

    IK分词器是通过请求参数的形式进行设置的,设置请求参数使用request对象中的source方法进行设置,至于参数是什么,取决于你的操作种类。当请求中需要参数时,均可使用当前形式进行参数设置。

  • 添加文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    //添加文档
    void testCreateDoc() throws IOException {
    Book book = bookDao.selectById(1);
    IndexRequest request = new IndexRequest("books").id(book.getId().toString());
    String json = JSON.toJSONString(book);
    request.source(json,XContentType.JSON);
    client.index(request,RequestOptions.DEFAULT);
    }

    添加文档使用的请求对象是IndexRequest,与创建索引使用的请求对象不同。

  • 批量添加文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Test
    //批量添加文档
    void testCreateDocAll() throws IOException {
    List<Book> bookList = bookDao.selectList(null);
    BulkRequest bulk = new BulkRequest();
    for (Book book : bookList) {
    IndexRequest request = new IndexRequest("books").id(book.getId().toString());
    String json = JSON.toJSONString(book);
    request.source(json,XContentType.JSON);
    bulk.add(request);
    }
    client.bulk(bulk,RequestOptions.DEFAULT);
    }

    批量做时,先创建一个BulkRequest的对象,可以将该对象理解为是一个保存request对象的容器,将所有的请求都初始化好后,添加到BulkRequest对象中,再使用BulkRequest对象的bulk方法,一次性执行完毕。

  • 按id查询文档

    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    //按id查询
    void testGet() throws IOException {
    GetRequest request = new GetRequest("books","1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    String json = response.getSourceAsString();
    System.out.println(json);
    }

​ 根据id查询文档使用的请求对象是GetRequest。

  • 按条件查询文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Test
    //按条件查询
    void testSearch() throws IOException {
    SearchRequest request = new SearchRequest("books");

    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.termQuery("all","spring"));
    request.source(builder);

    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    SearchHits hits = response.getHits();
    for (SearchHit hit : hits) {
    String source = hit.getSourceAsString();
    //System.out.println(source);
    Book book = JSON.parseObject(source, Book.class);
    System.out.println(book);
    }
    }

    按条件查询文档使用的请求对象是SearchRequest,查询时调用SearchRequest对象的termQuery方法,需要给出查询属性名,此处支持使用合并字段,也就是前面定义索引属性时添加的all属性。

springboot整合ES的操作到这里就结束了,与前期进行springboot整合redis和mongodb的差别还是很大的,主要原因就是我们没有使用springboot整合ES的客户端对象。至于操作,由于ES操作种类过多,所以显得操作略微有点复杂。


总结

springboot整合ES步骤

  1. 导入springboot整合ES的High Level Client坐标
  2. 手工管理客户端对象,包括初始化和关闭操作
  3. 使用High Level Client根据操作的种类不同,选择不同的Request对象完成对应操作