Design and Implementation of an Online Course Purchase System(25)

Complete course details page display related functions (apply Redis cache)

Posted by Chen Xingxu on June 3, 2020

易课寄在线购课系统开发笔记(二十五)–完成课程详情页面展示相关功能(应用Redis缓存)

课程详情页面展示

创建一个课程详情页面展示的工程,是一个表现层工程。

工程搭建

ecourses-item-web。打包方式 war。

可以参考

易课寄在线购课系统开发笔记(七)–后台管理系统工程搭建分析

ecourses-bms-web 工程的搭建过程。

pom 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>cn.ecourses</groupId>
    <artifactId>ecourses-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <groupId>cn.ecourses</groupId>
  <artifactId>ecourses-item-web</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <dependencies>
    <dependency>
      <groupId>cn.ecourses</groupId>
      <artifactId>ecourses-bms-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jms</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
    </dependency>
    <!-- JSP相关 -->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jsp-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- dubbo相关 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <exclusions>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.jboss.netty</groupId>
          <artifactId>netty</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
    </dependency>
    <dependency>
      <groupId>com.github.sgroschupf</groupId>
      <artifactId>zkclient</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </dependency>
    <dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-all</artifactId>
    </dependency>
  </dependencies>
  <!-- 配置tomcat插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <configuration>
          <path>/</path>
          <port>8086</port>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

功能分析

在搜索结果页面点击课程图片或者课程标题,展示课程详情页面。

请求的 url:/item/{itemId}

参数:课程 id

返回值:String 逻辑视图

业务逻辑:

1、从 url 中取参数,课程 id

2、根据课程 id 查询课程信息 (ecourses_item) 得到一个 EcoursesItem 对象,缺少 images 属性,可以创建一个pojo 继承 EcoursesItem,添加一个 getImages 方法。在 ecourses-item-web 工程中。

package cn.ecourses.item.pojo;
public class Item extends EcoursesItem {
	public Item(EcoursesItem ecoursesItem) {
		this.setId(ecoursesItem.getId());
		this.setTitle(ecoursesItem.getTitle());
		this.setSellPoint(ecoursesItem.getSellPoint());
		this.setPrice(ecoursesItem.getPrice());
		this.setNum(ecoursesItem.getNum());
		this.setBarcode(ecoursesItem.getBarcode());
		this.setImage(ecoursesItem.getImage());
		this.setCid(ecoursesItem.getCid());
		this.setStatus(ecoursesItem.getStatus());
		this.setCreated(ecoursesItem.getCreated());
		this.setUpdated(ecoursesItem.getUpdated());
	}

	public String[] getImages() {
		String image2 = this.getImage();
		if (image2 != null && !"".equals(image2)) {
			String[] strings = image2.split(",");
			return strings;
		}
		return null;
	}
}

3、根据课程 id 查询课程描述。

4、展示到页面。

Dao 层

查询 ecourses_item, ecourses_item_desc 两个表,都是单表查询,可以使用逆向工程。

1.2. Service层

1、根据课程 id 查询课程信息

参数:课程 id

返回值:EcoursesItem

2、根据课程 id 查询课程描述

参数:课程 id

返回值:EcoursesItemDesc

@Override
public EcoursesItemDesc getItemDescById(long itemId) {
    //查询缓存
    try {
        String json = jedisClient.get(REDIS_ITEM_PRE + ":" + itemId + ":DESC");
        if(StringUtils.isNotBlank(json)) {
            EcoursesItemDesc ecoursesItemDesc = JsonUtils.jsonToPojo(json, EcoursesItemDesc.class);
            return ecoursesItemDesc;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    EcoursesItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId);
    //把结果添加到缓存
    try {
        jedisClient.set(REDIS_ITEM_PRE + ":" + itemId + ":DESC", JsonUtils.objectToJson(itemDesc));
        //设置过期时间
        jedisClient.expire(REDIS_ITEM_PRE + ":" + itemId + ":DESC", ITEM_CACHE_EXPIRE);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return itemDesc;
}
@Override
public EcoursesItem getItemById(long itemId) {
    //查询缓存
    try {
        String json = jedisClient.get(REDIS_ITEM_PRE + ":" + itemId + ":BASE");
        if(StringUtils.isNotBlank(json)) {
            EcoursesItem ecoursesItem = JsonUtils.jsonToPojo(json, EcoursesItem.class);
            return ecoursesItem;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    //缓存中没有,查询数据库
    //根据主键查询
    //EcoursesItem ecoursesItem = itemMapper.selectByPrimaryKey(itemId);
    EcoursesItemExample example = new EcoursesItemExample();
    Criteria criteria = example.createCriteria();
    //设置查询条件
    criteria.andIdEqualTo(itemId);
    //执行查询
    List<EcoursesItem> list = itemMapper.selectByExample(example);
    if (list != null && list.size() > 0) {
        //把结果添加到缓存
        try {
            jedisClient.set(REDIS_ITEM_PRE + ":" + itemId + ":BASE", JsonUtils.objectToJson(list.get(0)));
            //设置过期时间
            jedisClient.expire(REDIS_ITEM_PRE + ":" + itemId + ":BASE", ITEM_CACHE_EXPIRE);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list.get(0);
    }
    return null;
}

表现层

Controller

请求的 url:/item/{itemId}

参数:课程 id

返回值:String 逻辑视图

package cn.ecourses.item.controller;
//详情页面展示Controller
@Controller
public class ItemController {

	@Autowired
	private ItemService itemService;
	
	@RequestMapping("/item/{itemId}")
	public String showItemInfo(@PathVariable Long itemId, Model model) {
		//调用服务取基本信息
		EcoursesItem ecoursesItem = itemService.getItemById(itemId);
		Item item = new Item(ecoursesItem);
		//取商品描述信息
		EcoursesItemDesc itemDesc = itemService.getItemDescById(itemId);
		//把信息传递给页面
		model.addAttribute("item", item);
		model.addAttribute("itemDesc", itemDesc);
		//返回逻辑视图
		return "item";
	}
}

向业务逻辑中添加缓存

缓存添加分析

使用 Redis 做缓存。

业务逻辑:

1、根据课程 id 到缓存中命中;

2、查到缓存,直接返回;

3、查不到,查询数据库;

4、把数据放到缓存中;

5、返回数据。

缓存中缓存热点数据,提供缓存的使用率。需要设置缓存的有效期。一般是一天的时间,可以根据实际情况调整。

需要使用 String 类型来保存课程数据。

可以加前缀方法对象 Redis 中的 key 进行归类。

ITEM_INFO:123456:BASE

ITEM_INFO:123456:DESC

如果把二维表保存到 Redis 中:

1、表名就是第一层;

2、主键是第二层;

3、字段名第三次;

三层使用 “:” 分隔作为 key,value 就是字段中的内容。

把 Redis 相关的 jar 包添加到工程

<!-- ecourses-common pom.xml -->
<!-- Redis客户端 -->
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
</dependency>

添加缓存

@Override
public EcoursesItem getItemById(long itemId) {
    //查询缓存
    try {
        String json = jedisClient.get(REDIS_ITEM_PRE + ":" + itemId + ":BASE");
        if(StringUtils.isNotBlank(json)) {
            EcoursesItem ecoursesItem = JsonUtils.jsonToPojo(json, EcoursesItem.class);
            return ecoursesItem;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    //缓存中没有,查询数据库
    //根据主键查询
    //EcoursesItem ecoursesItem = itemMapper.selectByPrimaryKey(itemId);
    EcoursesItemExample example = new EcoursesItemExample();
    Criteria criteria = example.createCriteria();
    //设置查询条件
    criteria.andIdEqualTo(itemId);
    //执行查询
    List<EcoursesItem> list = itemMapper.selectByExample(example);
    if (list != null && list.size() > 0) {
        //把结果添加到缓存
        try {
            jedisClient.set(REDIS_ITEM_PRE + ":" + itemId + ":BASE", JsonUtils.objectToJson(list.get(0)));
            //设置过期时间
            jedisClient.expire(REDIS_ITEM_PRE + ":" + itemId + ":BASE", ITEM_CACHE_EXPIRE);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list.get(0);
    }
    return null;
}

取课程描述添加缓存:

@Override
public EcoursesItemDesc getItemDescById(long itemId) {
    //查询缓存
    try {
        String json = jedisClient.get(REDIS_ITEM_PRE + ":" + itemId + ":DESC");
        if(StringUtils.isNotBlank(json)) {
            EcoursesItemDesc ecoursesItemDesc = JsonUtils.jsonToPojo(json, EcoursesItemDesc.class);
            return ecoursesItemDesc;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    EcoursesItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId);
    //把结果添加到缓存
    try {
        jedisClient.set(REDIS_ITEM_PRE + ":" + itemId + ":DESC", JsonUtils.objectToJson(itemDesc));
        //设置过期时间
        jedisClient.expire(REDIS_ITEM_PRE + ":" + itemId + ":DESC", ITEM_CACHE_EXPIRE);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return itemDesc;
}