版权声明:作者: 阿顾, 转载请写明出处, 谢谢 https://blog.csdn.net/u010452388/article/details/82120664
文章中一些详细的说明,会在代码中用注释的方式解释,如果有疑问或者写的不好的地方可以留言交流
先上效果图(文末会附上源码):
目录
扫描二维码关注公众号,回复:
2941348 查看本文章
一 数据库表结构
数据库表的parentId为0的话,那么是1级分类;2级分类的parentId为1级分类的id;3级分类的parentId为2级分类的id,文末源码中会给出sql的结构和数据
二 实体类设计
从上图可以看出1级分类和2级分类是一对多的关系,2级分类和3级分类也是一对多的关系,所以整体的结构关系如下图
从上面分析的结构图可以看出,1级分类的对应着一个2级分类的集合,所以可以在设计实体类的时候,添加一个商品分类集合的属性,设计如下:
public class TbItemCat implements Serializable {
private Long id;
private Long parentId;
private String name;
private Long typeId;
//添加下级分类的集合作为属性
private List<TbItemCat> itemCatList;
public List<TbItemCat> getItemCatList() {
return itemCatList;
}
public void setItemCatList(List<TbItemCat> itemCatList) {
this.itemCatList = itemCatList;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public Long getTypeId() {
return typeId;
}
public void setTypeId(Long typeId) {
this.typeId = typeId;
}
}
三 后端代码
由于后端用的分布式开发的,对分布式框架搭建不清楚的可以访问下面的地址:
https://blog.csdn.net/u010452388/article/details/82014152
3.1 数据访问层
3.1.1 商品分类的mapper接口
public interface TbItemCatMapper {
List<TbItemCat> findItemCatListByParentId(@Param("parentId") Long parentId);
}
3.1.2 商品分类的mapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper接口的全限定类名-->
<mapper namespace="com.demo.mapper.TbItemCatMapper">
<!--结果映射,id为了给执行语句调用指定的映射Map,type为返回实体类的全限定类名-->
<resultMap id="BaseResultMap" type="com.demo.pojo.TbItemCat">
<!--column为查出来的列名,property为对应的实体属性名-->
<id column="id" property="id"/>
<result column="parent_id" property="parentId"/>
<result column="name" property="name"/>
<result column="type_id" property="typeId"/>
</resultMap>
<sql id="Base_Column_List">
id, parent_id, name, type_id
</sql>
<!--id为TbItemCatMapper接口的方法名-->
<select id="findItemCatListByParentId" resultMap="BaseResultMap">
select
/*这里查询直接用字段名,查询的性能会比用*高一点*/
<include refid="Base_Column_List"/>
from
tb_item_cat
where
/*parentId为findItemCatListByParentId方法传递的参数*/
parent_id=#{parentId}
</select>
</mapper>
3.2 redis属性文件
# Redis settings
# server IP
redis.host=127.0.0.1
# server port
redis.port=6379
# server pass
redis.pass=
# use dbIndex
redis.database=0
# 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例
redis.maxIdle=300
# 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间(毫秒),则直接抛出JedisConnectionException;
redis.maxWait=3000
# 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
redis.testOnBorrow=true
3.3 redis配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!--引入redis属性文件-->
<context:property-placeholder location="classpath*:properties/*.properties"/>
<!-- redis 相关配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWait}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!--配置jedis连接工厂-->
<bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<!--配置redisTemplate-->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="JedisConnectionFactory"/>
</bean>
</beans>
3.4 接口层
public interface IndexService {
public List<TbItemCat> findItemCatList();
}
3.5 服务层
这里主要实现思路:
先判断缓存中有没有数据,如果有,直接返回给前端,如果没有,则从数据库查询,并放入缓存,再返回给前端
因为首页访问的压力比较多,所以放到缓存中,主要目的:减少数据库访问压力
@Service
public class IndexServiceImpl implements IndexService {
@Autowired
private TbItemCatMapper itemCatMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public List<TbItemCat> findItemCatList() {
//从缓存中查询首页商品分类
List<TbItemCat> itemCatList = (List<TbItemCat>) redisTemplate.boundHashOps("itemCat").get("indexItemCat");
//如果缓存中没有数据,则从数据库查询再存入缓存
if(itemCatList==null){
//查询出1级商品分类的集合
List<TbItemCat> itemCatList1 = itemCatMapper.findItemCatListByParentId(0L);
//遍历1级商品分类的集合
for(TbItemCat itemCat1:itemCatList1){
//查询2级商品分类的集合(将1级商品分类的id作为条件)
List<TbItemCat> itemCatList2 = itemCatMapper.findItemCatListByParentId(itemCat1.getId());
//遍历2级商品分类的集合
for(TbItemCat itemCat2:itemCatList2){
//查询3级商品分类的集合(将2级商品分类的父id作为条件)
List<TbItemCat> itemCatList3 = itemCatMapper.findItemCatListByParentId(itemCat2.getId());
//将2级商品分类的集合封装到2级商品分类实体中
itemCat2.setItemCatList(itemCatList3);
}
/*到这一步的时候,3级商品分类已经封装到2级分类中*/
//将2级商品分类的集合封装到1级商品分类实体中
itemCat1.setItemCatList(itemCatList2);
}
//存入缓存
redisTemplate.boundHashOps("itemCat").put("indexItemCat",itemCatList1);
return itemCatList1;
}
//到这一步,说明缓存中有数据,直接返回
return itemCatList;
}
}
3.6 控制层
@RestController
@RequestMapping("/index")
public class IndexController {
@Reference
private IndexService indexService;
/**
* 查询商品分类信息
*
* @return
*/
@RequestMapping("/findItemCatList")
public List<TbItemCat> findItemCatList() {
return indexService.findItemCatList();
}
}
四 前端代码
这里把前端分成service、controller主要是为了便于后期维护
4.1 indexService.js文件
//服务层
app.service('indexService', function ($http) {
//查询商品分类信息
this.findItemCatList = function () {
return $http.get("index/findItemCatList.do");
}
});
4.2 indexController.js文件
app.controller('indexController',function ($scope,indexService) {
//查询商品分类信息
$scope.findItemCatList=function () {
indexService.findItemCatList().success(function (response) {
$scope.itemCatList=response;
})
}
})
4.3 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>
<title>demo</title>
<!--引入样式-->
<link rel="stylesheet" type="text/css" href="css/webbase.css"/>
<link rel="stylesheet" type="text/css" href="css/pages-JD-index.css"/>
<!--引入js文件-->
<script src="angularjs/angular.min.js"></script>
<script src="js/base.js"></script>
<script src="js/service/indexService.js"></script>
<script src="js/controller/indexController.js"></script>
</head>
<body ng-app="demo" ng-controller="indexController" ng-init="findItemCatList()">
<!--列表-->
<div class="sort">
<div class="py-container">
<div class="yui3-g SortList ">
<div class="yui3-u Left all-sort-list">
<div class="all-sort-list2">
<!--商品分类 开始-->
<!--这里鼠标移入某个分类,就会给flag变量赋值为true,然后通过三元表达式确定样式-->
<div class="item {{flag?'hover':''}}" ng-repeat="itemCat1 in itemCatList" ng-mouseenter="flag=true"
ng-mouseleave="flag=false">
<h3><a href="">{{itemCat1.name}}</a></h3>
<!--通过三元表达式确定是显示还是隐藏-->
<div class="item-list clearfix" style="display: {{flag?'block':'none'}};">
<div class="subitem">
<!--遍历2级分类-->
<dl class="fore1" ng-repeat="itemCat2 in itemCat1.itemCatList">
<dt>
<a href="">{{itemCat2.name}}</a>
</dt>
<dd>
<!--遍历3级分类-->
<em ng-repeat="itemCat3 in itemCat2.itemCatList">
<a href="">{{itemCat3.name}}</a>
</em>
</dd>
</dl>
</div>
</div>
</div>
<!--商品分类 结束-->
</div>
</div>
</div>
</div>
</div>
</body>
</html>
启动顺序:
1.先启动redis和zookeeper
2.启动service服务
3.启动web
4.浏览器输入指定地址即可
五 源码分享
链接:https://pan.baidu.com/s/1SwRSF7oGVNMWiMO2DQFzyw 密码:ntq9