@Component
public class InitGoodsRedisData implements ApplicationListener<ContextRefreshedEvent>
{
@Autowired
private JedisPool jedisPool;
@Override
public void onApplicationEvent(ContextRefreshedEvent c)
{
Jedis jedis = jedisPool.getResource();
if (jedis.scard(RCRedisKey.GOODS_CATEGORY) > 0)
{
System.out.println("已经缓存商品初始化数据***************");
return;
}
Transaction transaction = jedis.multi();
initCategoryData(transaction);
initGoodsData(transaction);
transaction.exec();
jedis.close();
System.out.println("写入商品初始化数据***************OK");
}
// 初始化商品分类
private void initCategoryData(Transaction transaction)
{
transaction.sadd(RCRedisKey.GOODS_CATEGORY,
JSONObject.toJSONString(new CategoryVO(1, "手机")),
JSONObject.toJSONString(new CategoryVO(2, "平板")));
}
// 初始化商品信息
private void initGoodsData(Transaction transaction)
{
// 手机
Map phoneScoreMembers = new HashMap(5);
String goods11 = JSONObject.toJSONString(new GoodsVO(11, 1, "Iphone X", 8388.00,
"https://img11.360buyimg.com/n5/s75x75_jfs/t9907/361/46671134/65420/7d66b903/59c47a15N36a2fc0d.jpg"));
phoneScoreMembers.put(goods11, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 11 + "", goods11);
String goods12 = JSONObject.toJSONString(new GoodsVO(12, 1, "Iphone8 P", 6688.00,
"https://img10.360buyimg.com/n5/s54x54_jfs/t7339/80/2994230157/58546/a4b72d95/59b85837N990db7d3.jpg"));
phoneScoreMembers.put(goods12, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 12 + "", goods12);
String goods13 = JSONObject.toJSONString(new GoodsVO(13, 1, "三星S8", 5688.00,
"https://img14.360buyimg.com/n5/s54x54_jfs/t5095/313/1561100036/253747/b2dfce2f/59119ddcNc7305e8b.jpg"));
phoneScoreMembers.put(goods13, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 13 + "", goods13);
String goods14 = JSONObject.toJSONString(new GoodsVO(14, 1, "小米MIX2", 3599.00,
"https://img13.360buyimg.com/n5/s54x54_jfs/t9214/51/1590664916/300151/a59b7cd3/59bb8691Naca717e9.jpg"));
phoneScoreMembers.put(goods14, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 14 + "", goods14);
String goods15 = JSONObject.toJSONString(new GoodsVO(15, 1, "华为P10", 3488.00,
"https://img14.360buyimg.com/n5/s54x54_jfs/t4333/208/365266204/337840/e9e7bd9a/58b3f51aN10566aa1.jpg"));
phoneScoreMembers.put(goods15, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 15 + "", goods15);
transaction.zadd(String.format(RCRedisKey.GOODS_LIST, 1), phoneScoreMembers);
// 电脑
Map computerScoreMembers = new HashMap(5);
String goods21 = JSONObject.toJSONString(new GoodsVO(21, 2, "Apple iPad", 3258.00,
"https://img12.360buyimg.com/n5/s54x54_jfs/t3214/177/9256496777/621169/2a1735b5/58d16208N55e7302b.jpg"));
computerScoreMembers.put(goods21, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 21 + "", goods21);
String goods22 = JSONObject.toJSONString(new GoodsVO(22, 2, "华为M3", 2258.00,
"https://img10.360buyimg.com/n5/s54x54_jfs/t3235/143/2924985643/316383/370982db/57e8d527Ncb5ce343.jpg"));
computerScoreMembers.put(goods22, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 22 + "", goods22);
String goods23 = JSONObject.toJSONString(new GoodsVO(23, 2, "Apple iPad mini 4", 3098.00,
"https://img14.360buyimg.com/n5/s54x54_jfs/t9751/18/332152129/67998/3cef982f/59cc62ccN582dbd8a.jpg"));
computerScoreMembers.put(goods23, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 23 + "", goods23);
String goods24 = JSONObject.toJSONString(new GoodsVO(24, 2, "Apple iPad Pro", 5888.00,
"https://img10.360buyimg.com/n5/s54x54_jfs/t6679/178/76604977/301064/4e10cb22/5938ffa2Nb4d435f2.jpg"));
computerScoreMembers.put(goods24, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 24 + "", goods24);
String goods25 = JSONObject.toJSONString(new GoodsVO(25, 2, "Surface Pro", 9888.00,
"https://img10.360buyimg.com/n5/s54x54_jfs/t5617/321/1137763311/121847/b9326254/5923eb44Ndae8df59.jpg"));
computerScoreMembers.put(goods25, 0d);
transaction.hset(RCRedisKey.GOODS_INFO, 25 + "", goods25);
transaction.zadd(String.format(RCRedisKey.GOODS_LIST, 2), computerScoreMembers);
}
}
实现Spring接口ApplicationListener(项目启动后执行初始化方法)。使用Redis事务控制分类,商品列表,商品详情信息的数据一致性。结合数据的访问情景,使用不同的Redis缓存方式。
@Controller
@RequestMapping("goods")
public class GoodsController {
@Autowired
private GoodsBiz goodsBiz;
@Autowired
private HttpServletRequest request;
// 获取分类列表
@RequestMapping(value = "category")
public String category() {
request.setAttribute("pageData", goodsBiz.category());
return "goods/category";
}
// 获取商品列表
@RequestMapping(value = "goods_list")
public String goodsList(@RequestParam Integer cid) {
request.setAttribute("pageData", goodsBiz.goodsList(cid));
return "goods/goods_list";
}
// 获取商品详情
@RequestMapping(value = "goods_detail")
public String goodsDetail(@RequestParam Integer gid) {
request.setAttribute("info", goodsBiz.goodsDetail(gid));
return "goods/goods_detail";
}
}
@Service
public class GoodsBiz {
@Autowired
private JedisPool jedisPool;
// 获取分类列表
public List category() {
Jedis jedis = jedisPool.getResource();
try {
Set cacheCategory = jedis.sunion(RCRedisKey.GOODS_CATEGORY);
if (cacheCategory.isEmpty())
return null;
List result = new ArrayList(cacheCategory.size());
for (String item : cacheCategory)
result.add(JSONObject.parseObject(item, CategoryVO.class));
return result;
} finally {
jedis.close();
}
}
// 获取商品列表
public List goodsList(int categoryId) {
Jedis jedis = jedisPool.getResource();
try {
Set categoryGoods = jedis.zrevrange(String.format(RCRedisKey.GOODS_LIST, categoryId), 0, -1);
if (categoryGoods.isEmpty())
return null;
List result = new ArrayList(categoryGoods.size());
for (String item : categoryGoods) {
GoodsListVO pCache = JSONObject.parseObject(item, GoodsListVO.class);
pCache.setScore(jedis.zscore(String.format(RCRedisKey.GOODS_LIST, categoryId), item).intValue());
result.add(pCache);
}
return result;
} finally {
jedis.close();
}
}
// 获取商品详情
public GoodsVO goodsDetail(Integer gid) {
Jedis jedis = jedisPool.getResource();
try {
String cacheGoods = jedis.hget(RCRedisKey.GOODS_INFO, gid + "");
if (cacheGoods == null)
return null;
GoodsVO result = JSONObject.parseObject(cacheGoods, GoodsVO.class);
jedis.zincrby(String.format(RCRedisKey.GOODS_LIST, result.getCategoryId()), 1, cacheGoods);
return result;
} finally {
jedis.close();
}
}
}
注:使用jedis注意close
获取商品列表使用ZREVRANGE根据缓存的序号倒序拿取
ZREVRANGE
命令使用:ZREVRANGE key start stop [WITHSCORES]
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递减 (从大到小) 来排列
具有相同 score 值的成员按字典序的逆序 排列
获取商品详情时使用ZINCRBY对商品缓存列表序号追加1
ZINCRBY
命令使用:ZINCRBY key increment member
为有序集 key 的成员 member 的 score 值加上增量 increment
可以通过传递一个负数值 increment ,让 score 减去相应的值,比如 ZINCRBY key -5 member ,就是
让member 的 score 值减去 5 。
当 key 不存在,或 member 不是 key 的成员时,ZINCRBY key increment member 等同于 ZADD key
increment member 。
当 key 不是有序集类型时,返回一个错误。
score 值可以是整数值或双精度浮点数
界面使用thymeleaf模版进行编辑
thymeleaf介绍
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示
2.Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言
3.Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能
信息参考:http://www.importnew.com/25826.html
列表页面
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8" />
<title>商品列表title>
<link rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<link href="https://cdn.bootcss.com/Buttons/2.0.0/css/buttons.min.css"
rel="stylesheet" />
<style type="text/css">
.content_container {
width: 1366px;
}
th {
text-align: center;
}
style>
head>
<body class="container-fluid">
<div class="content_container center-block"
style="text-align: center; margin-top: 30px;">
<table class="table table-hover">
<thead>
<tr>
<th>编号th>
<th>名称th>
<th>图片th>
<th>价格th>
<th>浏览量th>
<th>操作th>
tr>
thead>
<tbody>
<tr th:each="item:${pageData}">
<td th:text="${item.id}">td>
<td th:text="${item.name}">td>
<td><img th:src="${item.img}" class="img-rounded" />td>
<td th:text="${item.price}">td>
<td th:text="${item.score}">td>
<td><a class="button button-primary"
th:href="@{goods_detail(gid=${item.id})}">查看a>td>
tr>
tbody>
table>
<a class="button" th:href="@{category}">返回分类a>
div>
body>
html>
HTML页面需引用thymeleaf标签声明,Eclipse对thymeleaf标签插件支持地址:http://www.thymeleaf.org/eclipse-plugin-update-site/
(Eclipse->Help->Install New Software)
此项目缓存思路不仅限于商城,适用于所有类似的业务缓存
项目源码下载(2分):http://download.csdn.net/download/qq_19260029/10022078
更多文章:
Java基于Redis实现“附近的人”
Eclipse新建Spring-boot项目,打包部署并输出HelloWord