练手项目1笔记 day08

广告管理与缓存解决方案

目标

  • 了解网站前台的页面以及广告相关表结构
  • 完成运营商后台广告类型管理与广告管理
  • 完成前台工程广告轮播图的展示
  • 使用SpringDataRedis操作字符串、set、list、hash等类型缓存
  • 使用SpringDataRedis实现广告数据的缓存

1. 网站前台分析

1. 网站前台有哪些页面

  1. 网站首页
  2. 商家(店铺)页面
  3. 商品详情页
  4. 商品搜索页
  5. 购物车列表页
  6. 购物选项选择页
  7. 支付页
  8. 用户注册页
  9. 用户登录页
  10. 用户中心页

2. 网站首页广告

  1. 首页海报(轮播图)
  2. 今日推荐
  3. 猜你喜欢
  4. 楼层广告

3. 数据库表结构分析

tb_content_category 广告分类表

tb_content 广告表

2. 运营商后台-广告类型及广告管理

1. 需求分析

实现广告类型表与广告表的增删改查

2. 准备工作

1. 构建工程
1. content-interface

引入依赖pinyougou-pojo

创建包com.pinyougou.content.service

2. pinyougou-content-service(WAR)

引入依赖同pinyougou-sellergoods-service

引入tomcat插件配置,指定运行端口为9002

为pinyougou-content-service工程提那家web.xml

创建包com.pinyougou.content.service.impl

添加spring相关配置文件 复制sellergoods-service的即可,将它的dobbo的port改为20882,包改为自己的包

3. manager-web工程引入依赖content-interface
2. 生成代码拷贝到工程

在这里插入图片描述

拷贝js代码到manager-web

包括service、controller和admin的广告相关代码

3. 测试运行

广告分类管理和广告管理页面

注意:需要将content和sellergoods两个service都开启。

3. 广告管理

1. 广告图片上传
1. 将shop-web的以下资源拷贝到manager-web
  1. UploadController.java
  2. uploadService.js
  3. application.properties
  4. fdfs_client.conf
2. 在manager-web的springmvc.xml添加配置多媒体解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <property name="defaultEncoding" value="UTF-8"/>
  <!--设置文件上传的最大值5MB-->
  <property name="maxUploadSize" value="5242880"/>
</bean>
3. 在contentController.js引入uploadService
app.controller('contentController' ,function($scope,$controller   ,contentService,uploadService,contentCategoryService){...}
4. 在content.html引入js
<script type="text/javascript" src="../js/service/uploadService.js">  </script>
5. 在contentController.js编写代码

注意上传出错,写到error

// 上传图片
$scope.uploadFile = function () {
  uploadService.uploadFile().success(
    function (response) {
      if(response.success){
        // 上传成功,取出url
        $scope.entity.pic = response.message;// 设置文件地址
      }else{
        alert(response.message);
      }
    }
  ).error(
    function () {
      alert("上传出错!");
    }
  );
}
6. 修改content.html实现上传功能
<td>图片</td>
<td>
  <input type="file" id="file">
  <button ng-click="uploadFile()">上传图片</button>
  <img alt="" src="{{entity.pic}}" height="100px" width="200px">
</td>
2. 广告类目选择

将contentCategoryService引入contentController,见之前代码

在content.html引入contentCategoryService.js

在contentController.js添加代码

// 加载广告分类的列表
$scope.findContentCategoryList = function () {
  contentCategoryService.findAll().success(
    function (response) {
      $scope.contentCategoryList = response;
    }
  )
}

在content.html初始化该调用方法

<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="contentController" ng-init="findContentCategoryList()" >

将广告分类改为下拉列表

<td>内容类目ID</td>
<td>
  <select class="form-control" ng-model="entity.categoryId"
          ng-options="item.id as item.name for item in contentCategoryList"></select>
</td>
3. 广告状态

修改content.html

<td>状态</td>
<td>
  <input type="checkbox"  ng-model="entity.status"
         ng-true-value="1" ng-false-value="0" placeholder="状态" >
</td>

修改contentController.js

$scope.status = ["无效","有效"];

修改content.html的列表

<td>
  <img src="{{entity.pic}}" alt="" height="50px" width="100px">
</td>
<td>{{status[entity.status]}}</td>

一个是状态的修改,一个是图片的展示

3. 网站首页- 广告展示

1. 需求分析

修改首页,当其轮播广告图根据后台设置的广告列表动态生成

2. 准备工作

1. 工程搭建

各种配置文件,不需要springsecurity框架

另外,pom.xml配置的tomcat穹顶端口为9103

2. 前端

拷贝资源,将静态资源里面的index.html和相关目录都拷过去

添加angularJS库

在js文件夹拷贝base.js和base_pagination.js,创建service和controller文件夹

3. 后端代码

1. 服务接口层

在content-interface工程ContentService接口增加方法

// 根据广告类型id查询列表
	public List<TbContent> findByCategoryId(Long categoryId);
2. 服务实现层

content-service的ContentServiceImpl类增加方法

@Override
public List<TbContent> findByCategoryId(Long categoryId) {
  TbContentExample example = new TbContentExample();
  Criteria criteria = example.createCriteria();
  criteria.andCategoryIdEqualTo(categoryId);
  criteria.andStatusEqualTo("1");//状态必须为1
  example.setOrderByClause("sort_order");// 排序
  return contentMapper.selectByExample(example);
}
3. 控制层

在portal-web创建控制器类ContentController

@RestController
@RequestMapping("/content")
public class ContentController {

    @Reference
    private ContentService contentService;

    @RequestMapping("/findByCategoryId")
    public List<TbContent> findByCategoryId(Long categoryId) {
        return contentService.findByCategoryId(categoryId);
    }
}

4. 前端代码

1. 服务层

在portal-web工程创建contentService.js

app.service("contentService",function ($http) {

    // 根据分类id查询广告列表
    this.findByCategoryId=function (categoryId) {
        return $http.get("content/findByCategoryId.do?categoryId="+categoryId);
    }
});
2. 控制层

在portal-web创建contentController.js

app.controller("contentController",function ($scope, contentService) {

  $scope.contentList = [];//广告集合
  // contentList[1] 是轮播图,contentList[2] 是今日推荐

  $scope.findByCategoryId = function (categoryId) {
    contentService.findByCategoryId(categoryId).success(
      function (response) {
        $scope.contentList[categoryId] = response;
      }
    );
  }
});
3. 页面

修改portal-web的index.html引入js

<script src="plugins/angularjs/angular.min.js"></script>
<script type="text/javascript" src="js/base.js"></script>
<script type="text/javascript" src="js/service/contentService.js"></script>
<script type="text/javascript" src="js/controller/contentController.js"></script>

在body上添加指令

<body ng-app="pinyougou" ng-controller="contentController" ng-init="findByCategoryId(1)">

修改轮播图

<!--banner轮播-->
<div id="myCarousel" data-ride="carousel" data-interval="4000" class="sui-carousel slide">
  <ol class="carousel-indicators">
    <li data-target="#myCarousel" data-slide-to="{{$index}}" class="{{$index==0?'active':''}}"
        ng-repeat="item in contentList[1]"></li>
  </ol>
  <div class="carousel-inner">
    <div class="{{$index==0?'active':''}} item" ng-repeat="item in contentList[1]">
      <a href="{{item.url}}">
        <img src="{{item.pic}}"  />
      </a>
    </div>

启动后再地址栏输入页面,看到首页效果

4. SpringDataRedis

1. 项目常见问题思考

项目实现了广告后台管理和广告前台展示,但是对于首页来说,每天大量用户访问,对数据库压力巨大,如何解决?一种是数据缓存,一种是网页静态化。今天看第一种方案

2. redis和Jedis

参见自己的java-web3的笔记

3. Spring Data Redis

是spring家族的一员,提供在spring应用中通过简单的配置访问redis服务,对redis底层开发包(Jedis,JRedis,RJC)进行高度封装,RedisTemplate提供redis的各种操作,异常处理以及序列化,支持发布订阅,对spring 3.1 cache进行实现。

针对jedis提供的功能:

  1. 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
  2. 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
    • ValueOperations:简单K-V操作
    • SetOperations:set类型数据操作
    • ZSetOperations:zset类型数据操作
    • HashOperations:针对map类型的数据操作
    • ListOperations:针对list类型的数据操作

4. 入门demo

1. 准备工作

构架maven项目,引入spring和junit的依赖

引入Jedis和SpringDataRedis依赖。

<!--缓存-->
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.1</version>
</dependency>
<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-redis</artifactId>
  <version>1.7.2.RELEASE</version>
</dependency>

在src/main/resources下创建properties文件将爱,建立redis-config.properties

# Redis settings 
# server IP 
redis.host=127.0.0.1 
# server port 
redis.port=6379 
# server pass 
redis.pass= 
# use dbIndex 
redis.database=0 
# \u63A7\u5236\u4E00\u4E2Apool\u6700\u591A\u6709\u591A\u5C11\u4E2A\u72B6\u6001\u4E3Aidle(\u7A7A\u95F2\u7684)\u7684jedis\u5B9E\u4F8B 
redis.maxIdle=300 
# \u8868\u793A\u5F53borrow(\u5F15\u5165)\u4E00\u4E2Ajedis\u5B9E\u4F8B\u65F6\uFF0C\u6700\u5927\u7684\u7B49\u5F85\u65F6\u95F4\uFF0C\u5982\u679C\u8D85\u8FC7\u7B49\u5F85\u65F6\u95F4(\u6BEB\u79D2)\uFF0C\u5219\u76F4\u63A5\u629B\u51FAJedisConnectionException\uFF1B  
redis.maxWait=3000 
# \u5728borrow\u4E00\u4E2Ajedis\u5B9E\u4F8B\u65F6\uFF0C\u662F\u5426\u63D0\u524D\u8FDB\u884Cvalidate\u64CD\u4F5C\uFF1B\u5982\u679C\u4E3Atrue\uFF0C\u5219\u5F97\u5230\u7684jedis\u5B9E\u4F8B\u5747\u662F\u53EF\u7528\u7684  
redis.testOnBorrow=true 

创建spring文件夹,创建applicationContext-redis.xml

<?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" 
       xmlns:mvc="http://www.springframework.org/schema/mvc" 
       xmlns:cache="http://www.springframework.org/schema/cache"
       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   
                           http://www.springframework.org/schema/mvc   
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd 
                           http://www.springframework.org/schema/cache  
                           http://www.springframework.org/schema/cache/spring-cache.xsd">  

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

  <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"/>  

  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">  
    <property name="connectionFactory" ref="jedisConnectionFactory" />
  </bean>  

</beans>  

其中maxIdle:最大空闲数;maxWaitMills:连接时的最大等待毫秒数;testOnBorrow:在提取一个jedis实例时,是否提前进行验证操作;如果true,得到的jedis实例是可用的。

2. 值类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext-redis.xml")
public class ValueTest {

  @Autowired
  private RedisTemplate template;

  @Test
  public void setValue(){
    template.boundValueOps("namea").set("itcast");
  }

  @Test
  public void getValue(){
    String str = (String) template.boundValueOps("name").get();
    System.out.println(str);
  }

  @Test
  public void deleteValue(){
    template.delete("name");
  }
}
3. set类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext-redis.xml")
public class SetTest {

    @Autowired
    private RedisTemplate template;

    @Test
    public void setValue(){
        template.boundSetOps("nameSet").add("曹操");
        template.boundSetOps("nameSet").add("刘备");
        template.boundSetOps("nameSet").add("孙权");
    }

    @Test
    public void getValue(){
        Set set = template.boundSetOps("nameSet").members();
        System.out.println(set);
    }

    @Test
    public void removeValue(){
        template.boundSetOps("nameSet").remove("孙权");

    }

    @Test
    public void delete(){
        template.delete("nameSet");
    }
}
4. List类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext-redis.xml")
public class ListTest {

    @Autowired
    private RedisTemplate template;

    // 右压栈:后添加的元素排在后边
    @Test
    public void testSetValue1(){
        template.boundListOps("nameList1").rightPush("刘备");
        template.boundListOps("nameList1").rightPush("关羽");
        template.boundListOps("nameList1").rightPush("张飞");
    }

    @Test
    public void testGetValue1(){
        List list = template.boundListOps("nameList1").range(0, 10);
        System.out.println(list);
    }

    // 左压栈
    @Test
    public void testSetValue2(){
        template.boundListOps("nameList2").leftPush("刘备");
        template.boundListOps("nameList2").leftPush("关羽");
        template.boundListOps("nameList2").leftPush("张飞");
    }

    @Test
    public void testGetValue2(){
        List list = template.boundListOps("nameList2").range(0, 10);
        System.out.println(list);
    }

    // 显示左压栈的值,根据索引查询元素
    @Test
    public void testSearchByIndex(){
        String str = (String) template.boundListOps("nameList1").index(1);
        System.out.println(str);
    }

    // 移除某个元素的值 参数1为移除的个数
    @Test
    public void testRemoveValue(){
        template.boundListOps("nameList1").remove(1,"刘备");
    }

}
5. hash类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext-redis.xml")
public class HashTest {
    @Autowired
    private RedisTemplate template;

    @Test
    public void testSetValue(){
        template.boundHashOps("nameHash").put("a","tom");
        template.boundHashOps("nameHash").put("b","jack");
        template.boundHashOps("nameHash").put("c","rose");
        template.boundHashOps("nameHash").put("d","pony");
    }

    // 获取所有的key
    @Test
    public void testGetKeys(){
        Set keys = template.boundHashOps("nameHash").keys();
        System.out.println(keys);
    }

    // 获取所有的值
    @Test
    public void testGetValues(){
        List list = template.boundHashOps("nameHash").values();
        System.out.println(list);
    }

    // 根据key取值
    @Test
    public void searchValueByKey(){
        String str = (String) template.boundHashOps("nameHash").get("b");
    }

    // 移除某个小key的值
    @Test
    public void removeValue(){
        template.boundHashOps("nameHash").delete("c");
    }
}

5. 网站首页-缓存广告数据

1. 需求分析

现在首页的广告每次都是从数据库读取,这样网站数据库压力很大,并且影响执行效率。需要将这部分数据缓存。

2. 读取缓存

1. 公共组件层

缓存对于整个的系统是通用功能。其他部分的数据可能也需要,放置到common里面。

1. 引入依赖
<!--缓存-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.7.2.RELEASE</version>
</dependency>
2. 创建配置文件

将redis-config.properties和applicationContext-redis.xml拷贝到common对应的部分

3. content-service 依赖common
2. 后端服务实现层

修改content-service的ContentServiceImpl

@Override
public List<TbContent> findByCategoryId(Long categoryId) {

  List<TbContent> list = (List<TbContent>) redisTemplate.boundHashOps("content").get(categoryId);

  if(list==null){
    System.out.println("get data from db");
    TbContentExample example = new TbContentExample();
    Criteria criteria = example.createCriteria();
    criteria.andCategoryIdEqualTo(categoryId);
    criteria.andStatusEqualTo("1");//状态必须为1
    example.setOrderByClause("sort_order");// 排序
    list= contentMapper.selectByExample(example);
    redisTemplate.boundHashOps("content").put(categoryId,list);
  }else{
    System.out.println("get data from redis");
  }
  return list;
}

3. 更新缓存

广告数据发生变更后,需要将缓存数据清除,再次查询才能获取最新数据

1. 新增广告后清除缓存

修改content-service的ContentServiceImpl.java

@Override
public void add(TbContent content) {
  contentMapper.insert(content);
  // 清除缓存
  redisTemplate.boundHashOps("content").delete(content.getCategoryId());
}
// 修改的话可能修改广告的分类,需要把原分类的缓存和新分类的缓存都删除
@Override
public void update(TbContent content){
  // 查询修改前的分类id
  Long oldCategoryId = contentMapper.selectByPrimaryKey(content.getId()).getCategoryId();

  redisTemplate.boundHashOps("content").delete(oldCategoryId);

  contentMapper.updateByPrimaryKey(content);
  // 如果分类id发生修改,清除修改后的分类id的缓存
  if(oldCategoryId.longValue()!=content.getCategoryId().longValue()){
    redisTemplate.boundHashOps("content").delete(content.getCategoryId());
  }
}	
@Override
public void delete(Long[] ids) {
  for(Long id:ids){
    // 先清除缓存
    // 广告分类的id
    Long categoryId = contentMapper.selectByPrimaryKey(id).getCategoryId();
    redisTemplate.boundHashOps("content").delete(categoryId);

    contentMapper.deleteByPrimaryKey(id);
  }		
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值