JAVA WEB DAY 17_ Redis

这篇博客介绍了NOSQL数据库的优势,特别是高并发访问和海量数据存储,并深入讲解了Redis的安装启动、数据类型(如string、hash、list、set、zset)及其操作命令。此外,详细阐述了Redis的持久化机制,包括RDB和AOF持久化,以及AOF重写原理和实践。最后,探讨了Jedis的基本使用、连接池创建和工具类实现,以及一个异步加载联系人的案例分析与实现。

Redis

01_NOSQL的概述-[★★]

NOSQL:泛指非关系型数据库

02_为什么要使用NOSQL-[★★]

NOSQL的好处:快
1. 解决高并发数据访问问题
2. 解决高海亮数据存储问题

具体表现为对如下问题的解决:

High Performance - 数据库高并发访问

​ 在同一个时间点,同时有海量的用户并发访问。往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。

  • 如天猫的双11,从凌晨0点到2点这段时间,每秒达到上千万次的访问量。
  • 12306春运期间,过年回家买火车抢票的时间,用户不断查询有没有剩余票。

Huge Storage - 高海量数据的存储

​ 数据库中数据量特别大,数据库表中每天产生海量的数据。

​ 类似QQ,微信,微博,每天用户产生海量的用户动态,每天产生几千万条记录。对于关系数据库来说,在一张几亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。

03_Redis的安装和启动-[★★]

  1. redis存储数据的格式:键值对形式存储
  2. 启动redis客户端的可执行程序是:redis-cli.exe
  3. 启动redis服务器的可执行程序是:redis-server.exe
  4. redis服务器使用的端口号是:6379
  • Redis 安装步骤:

解压即完成安装
注意事项

  1. 安装目录不要包含中文
  2. 目录不要太深
  3. 硬盘需要至少有20G内存空间
  • Redis 目录结构
目录或文件作用
redis-benchmark.exe用于 Reids 的性能测试工具
redis-check-aof.exeAOF 日志文件修复工具
redis-check-dump.exeRDB 文件修改工具
redis-cli.execlient 命令行的客户端工具
redis-server.exeserver 服务器端的启动程序
redis.windows.confredis 在 window 下的配置文件
  • Redis启动和停止步骤
  1. 启动redis
    进入redis解压目录:双击redis-server.exe文件
  2. 停止redis
    关闭服务器窗口即可停止

04_Redis五种数据类型及结构概述-[★★★★★]

  1. redis五种数据类型分别是:
    string
    hash
    list
    set
    zset
  2. 实际开发中主要使用哪种数据类型:string

在这里插入图片描述

关于key的定义,注意如下几点:

  1. 不建议key名字太长,通常不超过1024,如果太长会影响查询的速度。
  2. 不建议太短,太短会降低可读性。
  3. 一般在公司,都有统一命名规范

05_string类型操作命令-[★★★★★]

  1. 往redis存储键值对字符串的命令是:set 键 值
  2. 从redis中根据键获得值的命令是:get 键
  3. 从redis中根据键删除键值对的命令是:del 键
  • 命令语法
命令说明
set 键 值添加或修改键值对
get 键根据键获得值
del 键根据键删除键值对
  • 命令演示
  1. 添加一个键为company,值为qingruan
  2. 再设置一个键为company,值为baidun
  3. 得到company的元素
  4. 删除company元素
  5. 再次删除company看返回值是否相同
  6. 得到company看返回值是多少
  • 执行效果

在这里插入图片描述

06_hash类型操作命令-[★★★]

hash类型底层结构对应java的哪种类型?HashMap

  • 命令语法
命令说明
hset 键 字段 值向指定的中添加一对hash类型的字段名和值
hget 键 字段取出指定键的指定字段的值
hmset 键 字段 值 字段 值mulitple,一次向某个键中设置多个字段名和值
hmget 键 字段 字段一次从指定的键中得到多个字段的值
hdel 键 字段 字段删除一个键中的一个或多个字段
hgetall 键得到某个键所有的字段值
  • 命令演示
  1. 创建hash类型的键为user,并且添加一个字段为username,值为pkxing
  2. 向user中添加字段为password,值为12345
  3. 向user中添加字段为age,值为18
  4. 分别得到user中的username、password和age的字段值
  5. 向user中同时添加多个字段和值,birthday 2018-01-01 sex male
  6. 同时取得多个字段:age 和 sex
  7. 得到user中所有的字段和值
  8. 删除user中的生日和密码字段
  • 执行效果

在这里插入图片描述

07_list类型操作命令-[★★★]

1.list数据类型的特点:有序可重复
2.list类型底层结构对应java的哪种类型:ArrayList

  • 命令语法
命令行为
lpush 键 元素 元素left push在列表的左边向指定的键中添加列表元素,如果该键并不存在,Redis将为该键创建一个新的链表,如果这个键已经存在,则是向list添加元素。
rpush 键 元素 元素right push在列表的右边向指定的键中添加列表元素
lpop 键left pop从指定键中的左边弹出一个元素,列表中的元素就删除了。
rpop 键right pop从指定键的右边弹出一个元素,列表中的元素就删除了。
lrange 键 开始 结束从指定键的列表中取出指定范围的元素列表,从左边数起从0开始,从右边数起从-1开始。如果要取整个列表,开始是0,结束是-1
llen 键得到指定列表的长度
  • 命令演示
  1. 向mylist键的列表中,从左边添加a b c三个元素
  2. 从右边添加one two three三个元素
  3. 查询所有的元素
  4. 从右边添加一个重复的元素three
  5. 删除最右边的元素three
  6. 删除最左边的元素c
  7. 获取列表中元素的个数
  • 执行效果

在这里插入图片描述

08_set类型操作命令-[★★★]

  1. set数据类型的特点:无序且不可重复
  2. list类型底层结构对应java的哪种类型:HashSet
  • 命令语法
命令行为
sadd 键 元素 元素向set集合中添加1个或多个元素
smembers 键查询指定的集合中所有的元素
sismember 键 元素判断指定的元素是否在某个集合中,如果存在返回1,否则返回0
srem 键 元素 元素remove删除指定的一个或多个元素
  • 命令演示
  1. 向myset集合中添加A B C 1 2 3 六个元素
  2. 再向myset中添加B元素,看能否添加成功
  3. 显示所有的成员,发现与添加的元素顺序不同,元素是无序的
  4. 删除其中的C这个元素,再查看结果
  5. 判断A是否在myset集合中
  6. 判断D是否在myset集合中
  • 执行效果

在这里插入图片描述

09_zset类型操作命令-[★★]

  • 命令语法
命令描述
zadd 键 分数 值 分数 值添加1个或多个元素,每个元素都有一个分数
zrange 键 开始索引 结束索引获取指定范围的元素,得到所有的元素,索引是0到-1
zrem 键 值 值删除一个或多个值
zcard 键得到元素个数
zrank 键 值得到元素的索引号
zscore 键 值得到元素的分数
  • 命令演示
  1. 添加键country,分数是10,值是Japan
  2. 添加键country,分数是5,值是USA,添加键country,分数是50,值是Russian
  3. 添加键country,分数是1,值是China,分数是120,值是Korea
  4. 查询country中所有的元素
  5. 查询Japan的索引号(从0开始)
  6. 删除值为USA的元素
  7. 查询country中还有多少个元素
  8. 显示Russian的分数值

在这里插入图片描述

10_客户端工具使用和redis通用命令-[★★★★★]

  • 通用命令语法
命令功能
keys 匹配字符查询当前数据库中由哪些键
* 匹配多个字符
? 匹配1个字符
del 键 1 键 2可以删除任意键,可以一次删除多个键
exits 键是否存在指定的键
type 键判断指定的键它的值是什么类型,如:string,hash,list,set,none
select 数据库编号选择指定的数据库,0~15
move 键 数据库编号将某个键移动到另一个数据库中,如果另一个数据库中有同名的键,则移动失败。
  • 命令演示
  1. 添加字符串name的值为zhangsan
  2. 显示所有的键
  3. 显示所有以my开头的键
  4. 显示所有my后面有三个字符的键
  5. 添加一个字符串:name2 lisi
  6. 添加一个list:name3 a b c d
  7. 显示所有的键
  8. 一次删除name2和name3这两个键,其中name2和name3是不同的类型,显示所有键
  9. 分别判断name和name2是否存在
  10. 分别判断name user myset mylist分别是什么类型
  11. 切换数据库到15,向15中添加一个name2 wangwu,得到name2的值显示。
  12. 将15中的name2移到0中
  13. 切换到数据库0,显示所有的键
  • 执行结果

在这里插入图片描述

在这里插入图片描述

11_Redis的持久化-RDB持久化机制-[★★]

问:把客户端和服务端都关闭了,再重新开启服务器和客户端,数据会不会丢失?
答:可能会部分丢失,可能会全部丢失

  • RDB持久化机制的配置

在redis.windows.conf配置文件中的SNAPSHOTTING快照中有如下说明:

语法说明
save<时间间隔><修改键数>在指定的时间间隔内,修改了多少个键,则进行持久化的操作

如下面配置的是RDB方式数据持久化时机,必须两个条件都要满足

关键字时间(秒)修改键数解释
save9001在15分钟内如果修改了1个键,则进行持久化操作
save60010在5分钟内如果修改了10个键,则进行持久化操作
save6010000在1分钟内如果修改了1万个键,则进行持久化操作
  • RDB持久化内存的数据到dump.rdb文件,文件中会存储键值对数据。
  • RDB持久化的时候是将当时内存中所有的键值对一次性的持久化到dump.rdb。

示例演示-RDB持久化数据

  • 需求:修改rdb持久化策略方案,设置20秒内修改3个键进行持久化数据到dump.rdb文件中。

  • 实现步骤:

    1. 修改配置文件:redis.windows.conf 的101行,增加如下配置
    save 20 3
    
    1. 重启redis服务器端:要求启动的时候指明配置文件启动
    3. 打开DOS命令行窗口,切换到redis安装目录:cd d:/redis
    4. 启动服务器指定配置文件启动,格式:redis-server.exe redis.windows.conf
    
    1. 启动客户端,测试在20秒内写入3个键
    2. 关闭服务器,再次启动看是否有持久化
  1. RDB执行持久化的机制:在指定时间段内修改了指定数量的键时才执行持久化操作。
  2. RDB持久化机制的优点
  • 因为不是实时持久化,所以效率高。
  • 持久化文件中只记录内存中键值对的结果,不会记录对键值对修改的过程。
  1. RDB持久化机制的缺点
  • 因为不是实时持久化,所以数据容易丢失。
  • 当一次持久化数据很大的时候,会导致服务器暂停。

## 12_Redis的持久化-AOF持久化机制-[★★]

  • AOF持久化机制的配置

开启AOF持久化
AOF默认是关闭的,首先需要开启AOF模式.

参数配置说明
appendonly no/yesyes表示开启持久化,no表示关闭,默认是关闭
如果开启会在硬盘上生成一个文件appendonly.aof

AOF 持久化时机

关键字持久化时机解释
appendfsyncalways每次修改都持久化,效率最低
appendfsynceverysec每秒持久化一次
appendfsyncnoi不持久化,效率最高

示例演示-AOF持久化数据

  • 需求:开启AOF机制进行持久化数据测试。

  • 实现步骤:

    1. 开启AOF机制:修改配置文件:redis.windows.conf,将393行修改如下
    appendonly yes
    
    1. 重启redis服务器端:启动的时候指明配置文件启动
    3. 打开DOS命令行窗口,切换到redis安装目录:cd d:/redis
    4. 启动服务器指定配置文件启动,格式:redis-server.exe redis.windows.conf
    	* 此时在服务器目录下出现appendonly.aof文件。大小是0个字节。
    
    1. 启动客户端,添加3个键
      • 打开appendonly.aof文件,查看文件的变化,会发现文件记录了所有操作的过程。
    2. 关闭服务器,再次启动看是否有持久化
  1. AOF执行持久化的机制:每秒执行一次持久化操作。
  2. AOF持久化机制的优点:
  • 因为持久化的频率更高了,所以数据更加安全,更不容易丢失。
  1. AOF持久化机制的缺点:
  • 因为持久化的频率更高了,导致性能低了。
  • 因为日志文件中记录的所有修改的操作,在运行恢复数据过程中效率低。

13_AOF重写机制介绍-[★]

为什么需要AOF重写

​ 为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令,通常体积会较旧AOF文件小很多。

set name jack
set name rose
set name lucy
set name ptg
set name gcw

set name gcw

AOF 文件重写的原理

​ AOF重写并不需要对原有AOF文件进行任何的读取,写入,分析等操作,这个功能是通过读取服务器当前的数据库状态来实现的。

# 假设服务器对键list执行了以下命令
127.0.0.1:6379> RPUSH list "A" "B"
(integer) 2
127.0.0.1:6379> RPUSH list "C"
(integer) 3
127.0.0.1:6379> RPUSH list "D" "E"
(integer) 5
127.0.0.1:6379> LPOP list
"A"
127.0.0.1:6379> LPOP list
"B"
127.0.0.1:6379> RPUSH list "F" "G"
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"
127.0.0.1:6379>
结果分析

​ 当前列表键list在数据库中的值就为[“C”,“D”, “E”, “F”, “G”]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list “C” “D” “E” “F” "G"代替前面的6条命令。

结论

​ 因为AOF如果记录每一步操作,文件会越来越大,通过AOF的重写,可以缩小AOF文件的尺寸。同样可以达到数据还原效果。

  • AOF重写触发的方式
触发方式描述
手动触发通过调用bgrewriteaof手动触发
自动触发同时满足以下条件就触发自动的AOF重写操作:
1. 没有RDB持久化/AOF持久化在执行,没有bgrewriteaof在进行
2. 当前AOF文件大小要大于redis.conf配置的auto-aof-rewrite-min-size大小
3. 当前AOF文件大小和最后一次重写后的大小之间的比率大于或者等于
指定的增长百分比。
(在配置文件设置了auto-aof-rewrite-percentage参数,不设置默认为100%)

演示-AOF手动重写

  1. 关闭服务器,删除生成的aof和rdb文件

  2. 执行以下命令

1. 从右边添加一个键为list,值为A B
2. 从右边添加 C
3. 从右边添加 D E
4. 从左边弹出一个元素
5. 从左边弹出一个元素
6. 从右边添加元素 F G
7. 显示列表中的所有元素 
  1. 输入命令:bgrewriteaof,则aof被重写

  2. 观察目录下会产生旧的的aof文件

  3. 观察服务器上出现提示

1.5 演示-AOF自动重写

  1. 关闭服务器删除生成的aof和rdb文件

  2. 修改配置文件如下:

    # 大于原来的50%就自动重写
    auto-aof-rewrite-percentage 50
    # 自动重写的最小尺寸
    auto-aof-rewrite-min-size 100b
    
  3. 带配置文件启动服务器: redis-server redis.windows.conf

  4. 执行如下命令:

    1. 从右边添加一个键为list,值为A B
    2. 从右边添加 C
    3. 从右边添加 D E
    4. 从左边弹出一个元素
    5. 从左边弹出一个元素
    6. 从右边添加元素 F G
    7. 显示列表中的所有元素 
    
  5. 观察目录下会产生旧的的aof文件

  6. 观察服务器上出现提示

AOF重写原理:从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录该键值对的多个命令。

14_AOF和RDB常见问题-[★★]

1.1 实际开发中到底选择哪种持久化机制?

下面是来自官方的建议:
通常,如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。
如果你可以接受灾难带来的几分钟的数据丢失,那么你可以仅使用RDB。
很多用户仅使用了AOF,但是我们建议,既然RDB可以时不时的给数据做个完整的快照,并且提供更快的重启,所以最好还是也使用RDB。
因此,我们希望可以在未来(长远计划)统一AOF和RDB成一种持久化模式。

1.2 AOF和RDB是否可以同时使用?

可以同时使用,而且也是官方推荐的方式。

1.3 恢复数据时AOF和RDB哪个块?

RDB更快:因为RDB持久化的是内存键值对的最终结果,直接载入内存即可。而AOF日志文件中保存的是操作过的所有命令信息,恢复数据时需要逐条执行命令。

15_Jedis的基本使用-[★★★★★]

  • Jedis使用步骤
  1. 导入jedis相关jar包
  2. 创建连接对象:Jedis对象
    2.1 Redis数据库地址
    2.2 Redis数据库端口号
  3. 调用Jedis对象的方法对Redis数据库执行增删改查参数
    方法名就是命令名字,方法参数就是命令参数
    set name jack
    set(“name”,“jack”)
  4. 关闭连接释放资源:close
  • 示例代码
  • 需求:使用Jedis向redis中添加string和list,读取它们的值
/**
 * 需求:使用Jedis向redis中添加string和list,读取它们的值

   实现步骤:
        1. 导入jedis相关jar包
        2. 创建连接对象:Jedis对象
            2.1 Redis数据库地址
            2.2 Redis数据库端口号
        3. 调用Jedis对象的方法对Redis数据库执行增删改查参数
            方法名就是命令名字,方法参数就是命令参数
            set name jack
            set("name","jack")
        4. 关闭连接释放资源:close
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建连接对象:Jedis对象
        // 参数1:Redis数据库地址
        // 参数2:Redis数据库端口号
        Jedis jedis = new Jedis("localhost", 6379);

        // 存储string类型数据
        jedis.set("username", "小波");

        // 存储list类型的数据
        // lpush 键 元素
        // rpush 键 元素
        jedis.lpush("list","a","b","c");
        jedis.rpush("list","one","two","three");


        // 获得string类型的数据
        String username = jedis.get("username"); // 小波
        System.out.println(username);

        // 获得list类型的数据
        List<String> list = jedis.lrange("list", 0, -1);
        System.out.println("list = " + list);
        System.out.println("list = " + list.getClass());// ArrayList


        // 关闭连接释放资源:close
        jedis.close();
    }
}
  1. 如何获得Jedis连接对象
    Jedis jedis = new Jedis("地址",端口号);
  2. Jedis对象与增删改查字符串相关的方法
    jedis.set(key,value);
    jedis.get(key);
    jedis.del(key)

16_Jedis连接池创建和使用-[★★★★★]

  • Jedis连接池相关API
JedisPoolConfig 配置类功能说明
JedisPoolConfig()创建一个配置对象,使用无参构造方法就可以了
void setMaxTotal()设置连接池最大的连接数
void setMaxWaitMillis()设置得到连接对象Jedis最长等待时间
JedisPool 连接池类说明
JedisPool(配置对象,服务器名,端口号)创建连接池
参数1:上面的配置对象,参数2:服务器名,参数3:6379
Jedis getResource()从连接池中得到一个Jedis连接对象
void close()连接池关闭方法,通常不关闭连接池
  • 示例代码
/**
 *  需求:创建Jedis连接池对象并获得连接操作redis数据库

    实现步骤:
        1. 创建连接池配置对象
            1.1 最大连接数
            1.2 最大等待时间
        2. 创建连接池对象
            2.1 配置对象
            2.2 数据库地址
            2.3 数据库端口号
        3. 从连接池中获取连接对象(Jedis对象)
        4. 执行操作数据库操作
        5. 关闭连接:不是真正关闭而是放回连接池中等待复用

 */
public class Demo02 {
    public static void main(String[] args) {
        // 1. 创建连接池配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        // 1.1 最大连接数
        config.setMaxTotal(10);
        // 1.2 最大等待时间
        config.setMaxWaitMillis(2000);

        // 2. 创建连接池对象
        // 2.1 配置对象
        // 2.2 数据库地址
        // 2.3 数据库端口号
        JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
        // 3. 从连接池中获取连接对象(Jedis对象)
        Jedis jedis = jedisPool.getResource();
        // 4. 执行操作数据库操作
        jedis.sadd("set", "1","2","3","a","a");
        Set<String> set = jedis.smembers("set");
        System.out.println(set);
        System.out.println(set.getClass()); // HashSet

        // 5. 关闭连接:不是真正关闭而是放回连接池中等待复用
        jedis.close();
    }
}
  1. 连接池类叫什么:JedisPool
  2. 常用的连接池参数有哪些?
    最大连接数:maxTotal
    最大等待时间:maxWaitMillis

17_Jedis连接池工具类实现-[★★★★★]

  1. 实现连接池工具类,通过工具类得到Jedis连接对象,配置参数写在属性文件中。
  2. 调用工具类,对Redis数据库进行操作。
  • JedisUtils工具类

  • 实现步骤

  1. 在src目录下创建连接池的工具类: jedis.properties
  2. 创建静态成员变量JedisPool对象
  3. 在静态代码块中,读取src下的配置文件,得到ResourceBundle对象
  4. 得到上面的四个参数,其中host是字符串类型,其它参数要转成整数类型
  5. 实例化配置对象,实例化连接池对象
  6. 编写静态方法getJedis()返回Jedis对象
  • jedis.properties配置文件
# 主机名
host=localhost
# 端口号
port=6379
# 最大连接数
maxTotal=20
# 最长等待时间
maxWaitMillis=3000
  • ResourceBundle类概述

java.util.ResourceBundle类是专门用于:读取类路径下Properties配置文件的类。

java.util.ResourceBundle 类功能
static ResourceBundle getBundle(“配置基名”)通过静态方法创建 ResourceBundle对象
参数:放在src 下.properties文件。参数中不用写扩展名,只要有主名就可以了
String getString(“键名”)通过键得到值
  • JedisUtils类代码
/**
    Jedis连接池工具类
 */
public class JedisUtil {

    // 连接池对象
    private static JedisPool jedisPool;


    // 加载配置获取信息
    static {
        // ResourceBundle类是专门用于:读取类路径(src)下Properties配置文件的类。
        // 只需要文件名,不需要后缀名
        ResourceBundle bundle = ResourceBundle.getBundle("jedis");
        // 获取地址
        String host = bundle.getString("host");
        // 获取端口号
        int port = Integer.parseInt(bundle.getString("port"));
        // 获取最大连接数
        String maxTotal = bundle.getString("maxTotal");
        // 获取最大等待时间
        String maxWaitMillis = bundle.getString("maxWaitMillis");


        // 创建连接池配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置最大连接数
        config.setMaxTotal(Integer.parseInt(maxTotal));
        // 设置最大等待时间
        config.setMaxWaitMillis(Long.parseLong(maxWaitMillis));

        // 根据配置对象创建连接池对象
        jedisPool = new JedisPool(config, host, port);
    }


    /**
     * 返回连接对象
     * @return
     */
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }

}
  • 测试类代码
/**
 * 测试Jedis连接池工具类
 */
public class Demo03 {
    public static void main(String[] args) {
        // 获取连接对象
        Jedis jedis = JedisUtil.getJedis();

        // 创建Map集合
        Map<String,String> map = new HashMap<>();
        map.put("username", "小泽");
        map.put("password", "1234");
        map.put("age", "20");

        // 保存hash类型的数据
        jedis.hmset("user1",map);

        // 获取hash类型的数据
        Map<String, String> user1 = jedis.hgetAll("user1");
        System.out.println(user1);
        System.out.println(user1.getClass()); //

        // 关闭连接释放资源
        jedis.close();
    }
}

18_案例-异步加载联系人-分析和环境准备-[★★★★★]

  • 案例需求

    访问index.html页面,点击页面上的加载所有联系人按钮,才使用ajax请求异步加载所有联系人列表。用户第一次访问从mysql数据库中获取数据,以后都从redis缓存里面获取。

  • 数据准备

  • 创建数据表

-- 联系人
create table contact (
   id int primary key auto_increment,
   name varchar(20) not null,  -- 姓名
   phone varchar(20),  -- 电话
   email varchar(50),  -- 邮箱
   birthday date  -- 生日
);

insert into contact (name,phone,email,birthday) values
('孙悟空','13423431234','wukong@163.com', '1993-11-23'),
('猪八戒','13525678909','bajie@163.com', '1953-05-02'),
('白骨精','18642343123','xiaobai@163.com', '1943-03-12');

select * from contact;
  • 实体类:Contact
/**
 * 联系人实体类
 */
public class Contact {

    private int id;  //编号
    private String name;  //姓名
    private String phone;  //电话
    private String email;  //邮箱
    private Date birthday;  //生日

    @Override
    public String toString() {
        return "Contact{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                ", birthday=" + birthday +
                '}';
    }

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

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}
  • 主配置文件:sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--给数据类型取别名-->
    <typeAliases>
        <!-- 包扫描给类取别名,默认别名就是类名 -->
        <package name="com.pkx.entity"/>
    </typeAliases>

    <!--配置数据库连接参数-->
    <environments default="mybatis">
        <environment id="mybatis">
            <!--事务管理器-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源-->
            <dataSource type="pooled">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///day33"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 加载接口映射文件 -->
    <mappers>
        <!-- 加载指定包(包括子包下)的所有接口映射映射文件 -->
        <package name="com.pkx.dao"/>
    </mappers>
</configuration>
  • SqlSessionUtils工具类
/**
 * sqlSession工具类
 */
public class SqlSessionUtils {

    // 1. sqlSessionFactory对象
    private static SqlSessionFactory sqlSessionFactory;

    // 2. 加载主配置文件并获得SqlSessionFactory对象
    static {
        try{
            // 2.1 获得字节输入流关联sqlMapConfig.xml文件
            InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
            // 2.2 获得SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 2.3 创建sqlSessionFactory对象
            sqlSessionFactory = builder.build(in);
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    // 3. 返回一个sqlSession对象
    public static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }

    // 4. 返回Dao接口代理对象
    public static <T> T getMapper(Class<T> interfaceClass){
        // 创建代理对象并返回
        return (T) Proxy.newProxyInstance(
                SqlSessionUtils.class.getClassLoader(),
                new Class[]{interfaceClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 1. 获得连接对象
                        SqlSession sqlSession = sqlSessionFactory.openSession();
                        // 2. 获得接口代理对象 ==》 (真实对象)
                        T dao = sqlSession.getMapper(interfaceClass);
                        // 4. 调用真实对象的方法
                        Object result = method.invoke(dao, args);
                        sqlSession.commit();
                        // 5. 关闭连接
                        sqlSession.close();
                        return result;
                    }
                });
    }
}
  • redis配置文件:redis.properties
# 连接池参数
# 最大连接数
maxTotal=10
# 最大等待时间
maxWaitMillis=3000

# 数据库参数
# redis数据库地址
host=localhost
# redis数据库端口号
port=6379
  • Redis工具类:RedisUtils
/**
 *  Redis连接池工具类

    实现步骤:
    1. 定义连接池对象静态成员变量
    2. 创建连接池对象:在静态代码块中完成
        2.1 读取配置文件信息
        2.2 创建连接池配置对象
        2.3 根据配置对象创建连接池对象
    3. 提供公共的方法返回连接对象:Jedis
 */
public class JedisUtils {
    //  1. 定义连接池对象静态成员变量
    private static JedisPool jedisPool = null;

    //  2. 创建连接池对象:在静态代码块中完成
    static {
        // 2.1 读取配置文件信息
        // ResourceBundle类专门加载src目录下的属性文件:只需要传递文件名,不需要后缀名
        ResourceBundle bundle = ResourceBundle.getBundle("jedis");
        // 获得最大连接数
        int maxTotal = Integer.parseInt(bundle.getString("maxTotal"));
        // 获得最大等待时间
        long maxWaitMillis = Long.parseLong( bundle.getString("maxWaitMillis"));
        // 获得服务器地址
        String host = bundle.getString("host");
        // 获得服务器端口号
        int port = Integer.parseInt(bundle.getString("port"));

        // 2.2 创建连接池配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMaxWaitMillis(maxWaitMillis);

        // 2.3 根据配置对象创建连接池对象
        jedisPool = new JedisPool(config, host, port);
    }

    // 3. 提供公共的方法返回连接对象:Jedis
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

19_案例-异步加载联系人-服务器端实现-[★★★★★]

  • 数据访问层:ContactDao
/**
 * 数据访问层接口
 */
public interface ContactDao {
    // 1. 定义方法:查询所有联系人信息
    // 参数:无参数
    // 返回值:List<Contact>
    @Select("select * from contact")
    List<Contact> findAll();
}
  • 业务层:ContactSerivce
/**
 * 业务逻辑层
 */
public class ContactService {

    // 获取接口实现类
    private ContactDao contactDao = SqlSessionUtils.getMapper(ContactDao.class);

    /**
     * 查询所有联系人
     */
    public String findAll(){
       try{
           // 1. 查询redis数据库获取联系人数据
           Jedis jedis = JedisUtil.getJedis();
           String jsonStr = jedis.get("contactList");
           if (jsonStr == null) {
               System.out.println("从MySQL查询数据...");
               // 2. 从MySQL数据库查询联系人数据
               List<Contact> contactList = contactDao.findAll();
               // 2.1 将集合转换为json字符串
               jsonStr = new ObjectMapper().writeValueAsString(contactList);
               // 2.2 将联系人字符串并存储到Redis中
               jedis.set("contactList", jsonStr);
           } else {
               System.out.println("从redis查询数据....");
           }
           jedis.close();
           // 3. 返回查询到联系人数据
           return jsonStr;
       } catch(Exception e){
           e.printStackTrace();
       }
       return null;
    }
}
  • 控制器:ContactServlet
/**
 * 查询联系人
 */
public class ContactServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         // 设置内容类型和编码
         response.setContentType("text/html;charset=utf-8");
         // 获得字符打印流
         PrintWriter out = response.getWriter();
        // 1. 创建业务层对象
        ContactService cs = new ContactService();
        // 2. 调用方法获取联系人数据
        String jsonStr = cs.findAll();
        // 3. 返回联系人数据
        out.println(jsonStr);
    }
}

20_案例-异步加载联系人-前端页面实现-[★★★★★]

  1. 发送异步请求到服务器请求联系人数据
  2. 将联系人数据显示在表格中
  • index.html实现代码
<!DOCTYPE html>
<html lang="zh-CN">

	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>通讯录管理</title>
		<link href="css/bootstrap.min.css" rel="stylesheet">
		<script src="js/jquery-3.3.1.min.js"></script>
		<script src="js/bootstrap.min.js"></script>
	</head>

	<body>
		<div class="container">
            <br>
            <input id="btnLoad" type="button" class="btn btn-primary" value="加载联系人列表">
            <input id="btnClear" type="button" class="btn btn-danger" value="清空联系人列表">
            <hr>
			<table class="table table-bordered table-hover table-striped">
				<thead>
				<tr class="success">
					<th>编号</th>
					<th>姓名</th>
					<th>电话</th>
					<th>邮箱</th>
					<th>生日</th>
				</tr>
				</thead>
				<tbody id="contacts">

				</tbody>
			</table>
		</div>
	</body>

<script>
	// 监听 加载联系人列表 点击
	$("#btnLoad").click(function () {
		// 发送异步请求获取联系人数据
		$.get({
			url:"list", // 请求地址
			dataType:"json", // 响应数据类型
			success:function (jsonArray) { // 成功回调
				// 定义变量:用来拼接tr元素
				var trs = "";
				// 遍历联系人数组
				$.each(jsonArray,function (index, contact) {
					// 每一个联系人对应一行数据:一个tr标签
					trs += "<tr>" +
							"<td>"+contact.id+"</td>"+
							"<td>"+contact.name+"</td>"+
							"<td>"+contact.phone+"</td>"+
							"<td>"+contact.email+"</td>"+
							"<td>"+contact.birthday+"</td>"+
							"</tr>";
				})
				// 设置tbody元素内容
				$("#contacts").html(trs);
			},
			error:function () { // 失败回调
				alert("服务器忙...")
			}
		});
	});

	$("#btnClear").click(function () {
		$("#contacts").empty();
	})
</script>
</html>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值