初始化分类以及商品信息
@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<String, Double> phoneScoreMembers = new HashMap<String, Double>(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<String, Double> computerScoreMembers = new HashMap<String, Double>(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);
}
}
- 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
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
实现Spring接口ApplicationListener(项目启动后执行初始化方法)。使用Redis事务控制分类,商品列表,商品详情信息的数据一致性。结合数据的访问情景,使用不同的Redis缓存方式。
- 商品分类:数据唯一,且一次性全部拿取,使用Set(集合)进行缓存
- 商品列表:数据具备排序支持,使用SortedSet(有序集合)进行缓存
- 商品详情:准确定位获取数据信息,使用Hash(哈希表)进行缓存
控制器以及业务处理类
@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";
}
}
- 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
@Service
public class GoodsBiz {
@Autowired
private JedisPool jedisPool;
// 获取分类列表
public List<CategoryVO> category() {
Jedis jedis = jedisPool.getResource();
try {
Set<String> cacheCategory = jedis.sunion(RCRedisKey.GOODS_CATEGORY);
if (cacheCategory.isEmpty())
return null;
List<CategoryVO> result = new ArrayList<CategoryVO>(cacheCategory.size());
for (String item : cacheCategory)
result.add(JSONObject.parseObject(item, CategoryVO.class));
return result;
} finally {
jedis.close();
}
}
// 获取商品列表
public List<GoodsListVO> goodsList(int categoryId) {
Jedis jedis = jedisPool.getResource();
try {
Set<String> categoryGoods = jedis.zrevrange(String.format(RCRedisKey.GOODS_LIST, categoryId), 0, -1);
if (categoryGoods.isEmpty())
return null;
List<GoodsListVO> result = new ArrayList<GoodsListVO>(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();
}
}
}
- 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
- 55
- 56
- 57
注:使用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
列表页面
<!DOCTYPE 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>
- 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
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