Jedis

Jedis是Redis官方推荐的Java连接开发工具。 要在Java开发中使用好Redis中间件,必须对Jedis熟悉才能写成漂亮的代码。

Jedis使用教程

Jedis: 一款java操作redis数据库的工具.

使用步骤:

  1. 下载jedis的jar包并导入

  2. 使用

    1
    2
    3
    4
    5
    6
    //1. 获取连接
    Jedis jedis = new Jedis("localhost",6379);
    //2. 操作
    jedis.set("username","zhangsan");
    //3. 关闭连接
    jedis.close();

Jedis快速入门

我们创建JedisTest,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package test;

import org.junit.Test;
import redis.clients.jedis.Jedis;

public class JedisTest {
@Test
public void test1(){
// 1.获取连接
Jedis jedis = new Jedis("localhost", 6379);
// 2.操作
jedis.set("username","rick");
// 3.关闭连接
jedis.close();
}
}

在执行代码前我们先查看一下redis数据库的键:

执行前redis的keys

执行后结果为:

Jedis操作

菜鸟教程

Jedis方法介绍

jedis 各类方法示例

Jedis对redis的五大类型操作代码详解

之前的笔记中已经提到了,redis的value有五种类型:

  • 字符串类型:string
  • 哈希类型 hash:map格式
  • 列表类型 list:linkedlist格式。支持重复元素
  • 集合类型 set:不允许重复元素
  • 有序集合类型 sortedset:不允许重复元素,且元素有顺序

下面就来看一下如何在Jedis中操作这五种类型。其实Jedis中的方法和之前操作redis的名称是一样的

视频讲解

字符串类型

  • 存储:set()
  • 获取:
    • get()
    • setex():存储可以指定过期时间的 key value
  • 删除:del()

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package test;

import org.junit.Test;
import redis.clients.jedis.Jedis;

public class JedisTest {
@Test
public void test1(){
// 1.获取连接
Jedis jedis = new Jedis("localhost", 6379);
// 2.操作
jedis.set("username","rick");
String username = jedis.get("username");
System.out.println("username为:"+username);
// 删除username键
jedis.del("username");
//可以使用setex()方法存储可以指定过期时间的 key value
jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对
// 3.关闭连接
jedis.close();
}
}

执行后结果为:

成功get到username键的value

成功删除username键

哈希类型

  • hset()
  • hget()
  • hgetAll()

例子:(这里我们只给test2方法)

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
    @Test
public void test2(){
// 1.获取连接
Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
// 2.操作
// 存储hash
jedis.hset("user","name","jerry");
jedis.hset("user","age","19");
jedis.hset("user","gender","female");

// 获取hash
String name = jedis.hget("user", "name");
System.out.println("name是:"+name);
// 获取hash的所有map中的数据(即一个Map集合)
Map<String, String> user = jedis.hgetAll("user");
// 遍历Map对象user(这里我们使用Entry对象遍历)
Set<Map.Entry<String, String>> entrySet = user.entrySet();
System.out.println("user的值为:");
for (Map.Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey()+":"+entry.getValue());
}

// 删除
// 删除user的age这个field
// jedis.hdel("user","age");
// 删除user键
jedis.del("user");
//3. 关闭连接
jedis.close();
}

结果为:

列表类型

就直接上代码吧。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @Test
public void test3(){
// 1.获取连接
Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
// 2.操作
// list存储
jedis.lpush("mylist","hello","world"); // 从左边插入
jedis.rpush("mylist","123","456"); // 从右边插入
List<String> mylist = jedis.lrange("mylist", 0, -1); // list范围获取
// list弹出(删除)
// String lpop = jedis.lpop("mylist");// 删除(弹出)最左边的元素
// System.out.println("被删除的为:"+lpop);
System.out.println("mylist为:"+mylist);

// 删除mylist
jedis.del("mylist");
//3. 关闭连接
jedis.close();
}

结果如下:

集合类型

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void test4(){
// 1.获取连接
Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
// 2.操作
// set 存储
jedis.sadd("myset","java","php","c++");
// set 获取
Set<String> myset = jedis.smembers("myset");
System.out.println(myset);
//3. 删除myset键
jedis.del("myset");

//3. 关闭连接
jedis.close();
}

结果如下:

有序集合类型

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void test5(){
// 1.获取连接
Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
// 2.操作
// sortedset 存储
jedis.zadd("mysortedset",3,"亚瑟");
jedis.zadd("mysortedset",30,"后裔");
jedis.zadd("mysortedset",55,"孙悟空");

// sortedset 获取
Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);
System.out.println(mysortedset);
// 删除mysortedset键
jedis.del("mysortedset");

//3. 关闭连接
jedis.close();
}

结果如下:

Jedis连接池

JedisPool是一个线程安全的网络连接池。 可以用JedisPool创建一些可靠Jedis实例,可以从池中获取Jedis实例,使用完后再把Jedis实例还回JedisPool。 这种方式可以避免创建大量socket连接并且会实现高效的性能。

jedis:连接池(JedisPool)使用示例

Jedis连接池的使用

jedis连接池: JedisPool

使用:

  1. 创建JedisPool连接池对象
  2. 调用方法 getResource()方法获取Jedis连接

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void test6(){
// 0.创建一个配置对象
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(50); // 最大允许连接数
jedisPoolConfig.setMaxIdle(10); // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例

// 1.创建JedisPool连接池对象
JedisPool jedisPool = new JedisPool(jedisPoolConfig,"localhost",6379);
// 2.获取连接
Jedis jedis = jedisPool.getResource();
// 3.使用jedis
jedis.set("password","1234");
String password = jedis.get("password");
System.out.println("password键的value是:"+password);
jedis.del("password"); // 删除password键
// 4.关闭 归还到连接池中
jedis.close();
}

结果如下:

这里说一下JedisPoolConfig的配置参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#最大活动对象数     
redis.pool.maxTotal=1000
#最大能够保持idel状态的对象数
redis.pool.maxIdle=100
#最小能够保持idel状态的对象数
redis.pool.minIdle=50
#当池内没有返回对象时,最大等待时间
redis.pool.maxWaitMillis=10000
#当调用borrow Object方法时,是否进行有效性检查
redis.pool.testOnBorrow=true
#当调用return Object方法时,是否进行有效性检查
redis.pool.testOnReturn=true
#“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
redis.pool.timeBetweenEvictionRunsMillis=30000
#向调用者输出“链接”对象时,是否检测它的空闲超时;
redis.pool.testWhileIdle=true
# 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.
redis.pool.numTestsPerEvictionRun=50
#redis服务器的IP
redis.ip=xxxxxx
#redis服务器的Port
redis1.port=6379

关于JedisPoolConfig的配置参数也可以参考文章:Jedis连接池的使用

Jedis连接池工具类

JedisPool工具类,用来:

  • 加载配置文件,配置连接池的参数
  • 提供获取连接的方法

例子:

首先我们在src目录下创建配置文件jedis.properties,代码如下:

1
2
3
4
host=localhost
port=6379
maxTotal=50
maxIdle=10

然后创建Jedis连接池工具类JedisPoolUtils代码如下:

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
34
35
36
37
38
39
40
41
package util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class JedisPoolUtils {
private static JedisPool jedisPool;

// 加载配置文件,配置连接池的参数
static {
// 读取配置文件
InputStream inputStream = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
// 创建Properties对象
Properties properties = new Properties();
// 读取文件
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
// 使用getProperty方法获取数据,设置到JedisPoolConfig中
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(Integer.parseInt(properties.getProperty("maxTotal"))); // 字符串转数字
jedisPoolConfig.setMaxIdle(Integer.parseInt(properties.getProperty("maxIdle")));
// 创建JedisPool连接池对象
jedisPool = new JedisPool(jedisPoolConfig,properties.getProperty("host"),Integer.parseInt(properties.getProperty("port")));
}

/**
* 提供获取连接的方法
* */
public static Jedis getJedis(){
Jedis jedis = jedisPool.getResource();
return jedis;
}
}

最后我们执行代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Jedis连接池工具类使用
* */
@Test
public void test7(){
// 通过连接池工具类获取
Jedis jedis = JedisPoolUtils.getJedis();
// 使用jedis
jedis.set("username","jerry");
String username = jedis.get("username");
System.out.println("username键的value是:"+username);
// 关闭 归还到连接池中
jedis.close();
}

结果如下:

案例

本案例仓库地址:https://gitee.com/qingyu1011/springboot_study/tree/master/J2EE/Jedis

案例需求:

  1. 提供index.html页面,页面中有一个省份的下拉列表
  2. 当页面加载完成后 ,发送ajax请求,加载所有省份

案例分析

案例需求

视频讲解

代码实现

首先还是要导入相关jar包:

需要导入的jar包

然后创建配置文件:druid.properties和jedis.properties,代码分别如下:

1
2
3
4
5
6
7
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_study
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
1
2
3
4
host=localhost
port=6379
maxTotal=50
maxIdle=10

然后在dao包下创建ProvinceDao接口和其实现类ProvinceDaoImpl,代码分别如下:

1
2
3
4
5
6
7
8
9
package dao;

import domain.Province;

import java.util.List;

public interface ProvinceDao {
public List<Province> findAll();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package dao.impl;

import dao.ProvinceDao;
import domain.Province;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import util.JDBCUtils;

import java.util.List;

public class ProvinceDaoImpl implements ProvinceDao {
// 1.创建JDBCTemplate对象
private JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List<Province> findAll() {
// 1.定义sql
String sql = "select * from province ";
// 2.调用JDBCTemplate的方法来完成CRUD
List<Province> provinces = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Province>(Province.class));
return provinces;
}
}

在domain包下创建Province类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package domain;

public class Province {
private Integer id;
private String name;

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;
}
}

在service包下创建ProvinceService接口和其实现类ProvinceServiceImpl,代码分别如下:

1
2
3
4
5
6
7
8
9
10
11
12
package service;

import domain.Province;

import java.util.List;

public interface ProvinceService {

public List<Province> findAll();

public String findAllJson();
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.ProvinceDao;
import dao.impl.ProvinceDaoImpl;
import domain.Province;
import jedis.util.JedisPoolUtils;
import redis.clients.jedis.Jedis;
import service.ProvinceService;

import java.util.List;

public class ProvinceServiceImpl implements ProvinceService {
// 声明dao
private ProvinceDao provinceDao = new ProvinceDaoImpl(); // 多态

@Override
public List<Province> findAll() {
return provinceDao.findAll();
}

/**
* 使用redis缓存
* */
@Override
public String findAllJson() {
// 1.先从redis中查询数据
// 1-1 获取redis客户端连接
Jedis jedis = JedisPoolUtils.getJedis();
String provinc_json = jedis.get("province");
// 2.判断 provinc_json是否为null
if (provinc_json == null || provinc_json.length() == 0){ // redis中没有数据
System.out.println("redis中没有数据,查询数据库");
// 2-1 从MySQL中查询
List<Province> provinces = provinceDao.findAll();
// 2-2 序列化provinces为json
ObjectMapper objectMapper = new ObjectMapper();
try {
provinc_json = objectMapper.writeValueAsString(provinces);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// 2-3 将provinc_json数据存入redis中
jedis.set("province",provinc_json);
// 3.归还连接
jedis.close();
}else { // redis中有数据
System.out.println("redis中有数据,查询缓存");
}
return provinc_json;
}

}

在util包下创建JDBCUtils工具类如下:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
* JDBC工具类 使用Durid连接池
*/
public class JDBCUtils {

private static DataSource ds ;

static {

try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);

//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);

} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取连接池对象
*/
public static DataSource getDataSource(){
return ds;
}


/**
* 获取连接Connection对象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}

在web包下的servlet包下创建ProvinceServlet代码如下:

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
34
35
36
37
38
39
40
package web.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import domain.Province;
import service.ProvinceService;
import service.impl.ProvinceServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/provinceServlet")
public class ProvinceServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// // 1.调用service查询
// ProvinceService provinceService = new ProvinceServiceImpl();
// List<Province> provinceList = provinceService.findAll();
// // 2.序列化list为json
// ObjectMapper objectMapper = new ObjectMapper();
// String json = objectMapper.writeValueAsString(provinceList);
// System.out.println(json);

// 1.调用service查询
ProvinceService provinceService = new ProvinceServiceImpl();
String json = provinceService.findAllJson();
// 2.序列化list为json(在ProvinceServiceImpl中的findAllJson()方法中实现了)
// 3.响应结果
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

在浏览器中访问:http://localhost:8080/jedis/index.html

结果为:

浏览器中成功获取到数据

第一次是从MySQL中获取数据,之后便是到redis中获取数据了,提高了数据获取的速度

注意:

  • 千万要注意:druid.properties配置文件中一些数字后面不要加空格,不然会报错java.lang.NumberFormatException: For input string: "10 # ×??ó??????"错,就是因为之前这样写的:maxActive=10 # 最大连接数
  • 使用redis缓存一些不经常发生变化的数据
    • 因为数据库的数据一旦发生改变,则需要更新缓存。在service对应的增删改方法中,需要将redis数据删除。