JavaWeb第二十天

Redis

概念

一款高性能的NoSQL系列的非关系型数据库
关系型:MySQL Oracle
    数据之间有关联关系
    数据存储在硬盘的文件中
非关系型:Redis HBase
    数据使用键值对/列/文档/图形进行存储,数据之间没有关联关系
    数据存储在内存中
数据量很大时,操作非关系型数据库很耗时,因此如果经常查询一些不太经常发生变化的数据,可以使用缓存的思想
    在内存中开辟一块区域作为缓存
    从缓存中获取数据
        有数据
            返回数据
        没有数据
            查询数据库
            将数据放入缓存
            返回数据
一般将数据存储在关系型数据库中,在非关系型数据库中备份这些数据

应用场景

缓存(数据查询、短连接、新闻内容、商品内容等等)
聊天室的在线好友列表
任务队列(秒杀、抢购、12306等等)
应用排行榜
网站访问统计
数据过期处理(可以精确到毫秒)
分布式集群架构中的session分离

下载安装

官网 https://redis.io
中文网 http://www.redis.net.cn
redis一般部署在Linux系统上
解压即安装
解压后的文件
    redis.windows.conf 配置文件
    redis-cli.exe redis的客户端
    redis-server.exe redis的服务器端
双击redis-server.exe启动服务器
双击redis-cli.exe启动客户端,输入命令
    set username zhangsan 存储键值对
    get username 获取值

命令操作

redis的数据结构
redis存储的是key,value格式的数据
key都是字符串,value有5种数据类型
    字符串string 哈希hash(map) 列表list(linkedlist) 集合set(hashset) 有序集合sortedset
        list允许重复元素,set不允许重复元素,sortedset不允许重复元素且存入数据会自动排序
key             value
myString        zhangsan
myHash          name    lisi
                age     23
                gender  female
myList          [zhangsan, lisi, wangwu, zhangsan] 元素可重复
mySet           [zhangsan, lisi, wangwu] 元素不可重复
mySortedSet     [zhangsan, lisi, wangwu] 元素不可重复且会自动排序
命令操作5种类型数据 更多命令查看redis中文网的教程
string
存储 set key value 设置同一key的不同value会覆盖前面的
获取 get key 获取不存在的key会返回<nil>
删除 del key
hash
存储 hset key field value
    hset myHash username zhangsan
    hset myHash password 123
获取hash中某个field对应的值 hget key field
    hget myHash username
获取整个hash hgetall key
    hgetall myHash
    返回1>"username"
       2>"zhangsan"
       3>"password"
       4>"123"
删除 hdel key field
    hdel myHash username
list
添加 lpush key value
    lpush myList a
    lpush myList b
    rpush myList c
    元素顺序 b a c
获取 lrange key start end
    获取所有元素 lrange myList 0 -1
删除
    lpop key 删除列表左边的第一个元素并返回该元素
        lpop myList
    rpop key 删除列表右边的第一个元素并返回该元素
    rpop myList
set
存储 sadd key value
    sadd mySet a b c 获取元素的顺序和插入顺序不一定一致
获取set中所有元素 smembers key
删除某个元素 srem key value
    srem mySet a
sortedset
存储 zadd key score value
    zadd mySortedSet 60 zhangsan
    zadd mySortedSet 70 lisi
    zadd mySortedSet 80 wangwu
获取 zrange key start end
    zrange mySortedSet 0 -1 获取所有元素
    返回1>"zhangsan"
       2>"lisi"
       3>"wangwu"
    zrange mySortedSet 0 -1 withscore
    返回1>"zhangsan"
       2>"60"
       3>"lisi"
       4>"70"
       5>"wangwu"
       6>"80"
删除 zrem key value
    zrem mySortedSet zhangsan
通用命令
keys * 获取所有的键
type key 获取键对应值的数据类型
del key 删除键

持久化

redis是一个内存数据库,数据是临时的,关电脑或重启redis服务器数据就会丢失
可以将redis内存中的数据持久化保存到硬盘的文件中
redis持久化机制
    RDB 默认方式,不需要进行配置,默认使用这种机制
        在一定的时间间隔内,检测key的变化情况,然后持久化数据
        对性能的影响比较低,推荐使用
    AOF 日志记录的方式
        记录每一条命令,在每一次命令操作后,持久化数据
        对性能的影响比较高,不推荐使用
RDB使用
编辑redis.windows.conf
    save 900 1 15分钟后有1次key改变就持久化一次
    save 300 10 5分钟后有10次key改变就持久化一次
    save 60 10000 1分钟有10000次key改变就持久化一次
在redis安装位置启动命令行
    redis-server.exe redis.windows.conf 启动服务器
双击启动客户端
    改变键
达到上述持久化要求后会在安装位置生成一个dump.rdb文件来存储数据
之后重启服务器和客户端,数据就不会丢失,可以获取
AOF使用
编辑redis.windows.conf
    appendonly no改为appendonly yes 开启AOF
    # appendfsync always 每一次命令操作都进行持久化
    appendfsync everysec 每隔一秒持久化一次
    # appendfsync no 不进行持久化 redis就是一个很大的map集合
在redis安装位置启动命令行
    redis-server.exe redis.windows.conf 启动服务器
双击启动客户端
    改变键
达到上述持久化要求后会在安装位置生成一个appendonly.aof文件来存储数据
之后重启服务器和客户端,数据就不会丢失,可以获取

Jedis

一款Java操作redis数据库的工具
类似于JDBC,导入mysql-connector的jar包后就可以使用Java操作MySQL数据库

使用

导入jar包
commons-pool2-2.3.jar
jedis-2.7.0.jar
使用
//获取连接
Jedis jedis = new Jedis("localhost", 6379);//空参默认使用localhost和6379
//操作
jedis.set("username", "zhangsan");
//关闭连接
jedis.close();
操作5种数据类型 命令和方法名一样
字符串string
set
get
setex 存储可以指定过期时间的key和value 时间到了自动删除
    可以用于邮箱激活链接
哈希hash(map)
hset
hget
hgetall hgetAll
列表list(linkedlist)
lpush
rpush
lpop
rpop
lrange start end
集合set(hashset)
sadd
smembers
有序集合sortedset
zadd
package jedis.test;

import jedis.util.JedisPoolUtils;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class JedisTest {
    //快速入门
    @Test
    public void test1() {
        //获取连接
        Jedis jedis = new Jedis("localhost", 6379);
        //操作
        jedis.set("username", "zhangsan");
        //关闭连接
        jedis.close();
    }
    //字符串string
    @Test
    public void test2() {
        //获取连接
        Jedis jedis = new Jedis();
        //操作
        jedis.set("username", "zhangsan");
        String username = jedis.get("username");
        System.out.println(username);//zhangsan
        //使用setex()方法存储可以指定过期时间的key和value 时间到了自动删除
        jedis.setex("activecode", 20, "hehe");//20s过期的激活码
        //关闭连接
        jedis.close();
    }
    //hash
    @Test
    public void test3() {
        //获取连接
        Jedis jedis = new Jedis();
        //操作
        jedis.hset("user", "name", "lisi");
        jedis.hset("user", "age", "24");
        jedis.hset("user", "gender", "female");
        String name = jedis.hget("user", "name");
        String age = jedis.hget("user", "age");
        String gender = jedis.hget("user", "gender");
        System.out.println("name:" + name + ", age:" +age + ", gender:" + gender);
        //获取hash中的所有键值对
        Map<String, String> user = jedis.hgetAll("user");
        for (String key : user.keySet()) {
            String value = user.get(key);
            System.out.println(key + ":" + value);
        }
        //关闭连接
        jedis.close();
    }
    //list
    @Test
    public void test4() {
        //获取连接
        Jedis jedis = new Jedis();
        //操作
        jedis.lpush("myList", "a", "b", "c");
        jedis.rpush("myList", "a", "b", "c");
        List<String> myList = jedis.lrange("myList", 0, -1);
        System.out.println(myList);//[c, b, a, a, b, c]
        String leftElem = jedis.lpop("myList");
        System.out.println(leftElem);//c
        String rightElem = jedis.rpop("myList");
        System.out.println(rightElem);//c
        List<String> myList1 = jedis.lrange("myList", 0, -1);
        System.out.println(myList1);//[b, a, a, b]
        //关闭连接
        jedis.close();
    }
    //set
    @Test
    public void test5() {
        //获取连接
        Jedis jedis = new Jedis();
        //操作
        jedis.sadd("mySet", "java", "php", "c++");
        Set<String> mySet = jedis.smembers("mySet");
        System.out.println(mySet);//[c++, java, php]
        //关闭连接
        jedis.close();
    }
    //sortedset
    @Test
    public void test6() {
        //获取连接
        Jedis jedis = new Jedis();
        //操作
        jedis.zadd("mySortedSet", 3, "亚瑟");
        jedis.zadd("mySortedSet", 30, "后羿");
        jedis.zadd("mySortedSet", 25, "孙悟空");
        Set<String> mySortedSet = jedis.zrange("mySortedSet", 0, -1);
        System.out.println(mySortedSet);//[亚瑟, 孙悟空, 后羿]
        jedis.zadd("mySortedSet", 55, "孙悟空");
        Set<String> mySortedSet1 = jedis.zrange("mySortedSet", 0, -1);
        System.out.println(mySortedSet1);//[亚瑟, 后羿, 孙悟空]
        //关闭连接
        jedis.close();
    }
    //Jedis连接池
    @Test
    public void test7() {
        //创建配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(50);//最大连接数
        config.setMaxIdle(10);//最大空闲连接数
        //创建Jedis连接池对象
        JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
        //获取连接
        Jedis jedis = jedisPool.getResource();
        //操作
        jedis.set("hehe", "haha");
        String hehe = jedis.get("hehe");
        System.out.println(hehe);//haha
        //归还连接
        jedis.close();
    }
    //JedisPoolUtils的使用
    @Test
    public void test8() {
        //使用工具类的静态方法获取连接
        Jedis jedis = JedisPoolUtils.getJedis();
        jedis.set("hello", "world");
        String hello = jedis.get("hello");
        System.out.println(hello);//world
        jedis.close();
    }
}

Jedis连接池 JedisPool 对连接进行复用

使用
    创建配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(50);//最大连接数
        config.setMaxIdle(10);//最大空闲连接数
    创建JedisPool连接池对象
        参数传递配置对象,主机地址和端口号
        JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
    调用getResource方法获取Jedis连接
        Jedis jedis = jedisPool.getResource();
    操作数据库
    将连接归还给连接池
        jedis.close();
创建JedisPool的工具类JedisPoolUtils,用于加载配置文件,配置连接池的参数

Jedis工具类

package jedis.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;

/*
JedisPool工具类
    用于加载配置文件,配置连接池的参数
    提供获取连接的方法
 */
public class JedisPoolUtils {

    private static JedisPool jedisPool;

    static {
        //读取配置文件
        InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
        //创建Properties对象
        Properties properties = new Properties();
        //关联文件
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取数据,设置到Jedis配置对象JedisPoolConfig中
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.parseInt(properties.getProperty("maxTotal")));
        config.setMaxIdle(Integer.parseInt(properties.getProperty("maxIdle")));
        //初始化JedisPool对象
        jedisPool = new JedisPool(config, properties.getProperty("host"), Integer.parseInt(properties.getProperty("port")));
    }
    /**
     * 获取连接的方法
     */
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }
}

案例

需求
1、提供index.html页面,页面中有一个省份下拉列表
2、当页面加载完成后,发送ajax请求,加载所有省份
分析
数据库
    CREATE DATABASE day20; -- 创建数据库
    USE day20; -- 使用数据库
    CREATE TABLE province( -- 创建表
        ID INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(20) NOT NULL
    );
    -- 插入数据
    INSERT INTO province VALUES(NULL, "北京");
    INSERT INTO province VALUES(NULL, "上海");
    INSERT INTO province VALUES(NULL, "广州");
    INSERT INTO province VALUES(NULL, "深圳");
导入jar包
复制druid.properties到项目中
index.html
    有一个下拉列表,只有请选择省份这一行
    页面加载完成后,index.html中的JavaScript代码会发送一个ajax请求
        url是findProvinceServlet,获取数据库中的省份信息
findProvinceServlet
    调用service层的方法,返回List<Province>
    将返回的数据序列化为json,并填充到响应数据中
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getWriter(), mapper);
ProvinceService
    查询redis是否存在数据
        不存在
            调用dao层的方法
            将数据写入redis
            返回数据
        存在
            直接返回数据
ProvinceDao
    使用JDBC操作数据库
注意
在$.get参数的最后加上"json"会报错
使用redis缓存一些不经常发生变化的数据
    数据库的数据一旦发生变化(数据库的表增删改),需要更新缓存(清空redis缓存的数据,再次存入)
        更新缓存:在service中的增删改相关方法中删除对应的键 这样在查询方法中找不到对应的键就会自动再次查询数据库并将数据存入缓存
开发
配置文件

druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day20
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000

jedis.properties

host=127.0.0.1
port=6379
maxTotal=50
maxIdle=10
html

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>选择省份页面</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script>
        $(function () {
            $.get("findProvincesServlet", {}, function (data) {
                //获取select
                var province = $("#province");
                //遍历返回的json数组,每次遍历创建一个option,并使用append方法追加到select中
                $(data).each(function () {
                    var option = "<option name='" + this.id + "'>" + this.name + "</option>";
                    province.append(option);
                });
            });
        });
    </script>
</head>
<body>
    <select id="province">
        <option>--请选择省份--</option>
    </select>
</body>
</html>
util

JDBCUtils

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

domain

Province

package domain;

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Province{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

web.servlet

FindProvincesServlet

package web.servlet;

import service.ProvinceService;
import service.impl.ProvinceServiceImpl;

import java.io.IOException;

@javax.servlet.annotation.WebServlet("/findProvincesServlet")
public class FindProvincesServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        response.setContentType("application/json;charset=utf-8");
        //调用service层的方法查询所有省份
        ProvinceService service = new ProvinceServiceImpl();
        //List<Province> provinces = service.findProvinces();
        String provinces_json = service.findProvincesJson();
        //将返回的数据序列化为json,并填充到响应数据中
        //ObjectMapper mapper = new ObjectMapper();
        //String provinces_json = mapper.writeValueAsString(provinces);
        response.getWriter().write(provinces_json);
    }

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

service

ProvinceService

package service;

import domain.Province;

import java.util.List;

public interface ProvinceService {
    public List<Province> findProvinces();
    public String findProvincesJson();
}

impl/ProvinceServiceImpl

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 {
    private ProvinceDao dao = new ProvinceDaoImpl();
    @Override
    public List<Province> findProvinces() {
        return dao.findProvinces();
    }

    /**
     * 使用redis缓存
     * @return
     */
    @Override
    public String findProvincesJson() {
        //先从redis中查询数据 先获取redis的客户端连接
        Jedis jedis = JedisPoolUtils.getJedis();
        String province_json = jedis.get("province");
        //判断province是否为空
        if (province_json == null || province_json.length() == 0) {
            //从数据库查询
            System.out.println("redis中没有数据,查询数据库");
            List<Province> provinces = dao.findProvinces();
            ObjectMapper mapper = new ObjectMapper();
            try {
                province_json = mapper.writeValueAsString(provinces);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            //将数据存入redis
            jedis.set("province", province_json);
        } else {
            System.out.println("redis中有数据,查询缓存");
        }
        jedis.close();
        return province_json;
    }
}

dao

ProvinceDao

package dao;

import domain.Province;

import java.util.List;

public interface ProvinceDao {
    public List<Province> findProvinces();
}

impl/ProvinceDaoImpl

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 {
    JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    @Override
    public List<Province> findProvinces() {
        //使用JDBC操作数据库
        //定义sql
        String sql = "select * from province";
        //执行sql
        List<Province> provinces = template.query(sql, new BeanPropertyRowMapper<Province>(Province.class));
        return provinces;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值