Spring Boot+Mybatis+Annotation+Redis+Web(六)

本文介绍缓存的基本概念及应用场景,并通过一个电商案例详细解析如何利用Redis实现数据的高效读取与更新,包括缓存的增删改查操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、缓存的应用

什么是缓存?
   在互联网场景下,尤其 2C 端大流量场景下,需要将一些经常展现和不会频繁变更的数据,存放在存取速率更快的地方。缓存就是一个存储器,在技术选型中,常用 Redis 作为缓存数据库。缓存主要是在获取资源方便性能优化的关键方面。

缓存的应用场景有哪些呢?
   比如常见的电商场景,根据商品ID获取商品信息时,店铺信息和商品详情信息就可以缓存在 Redis,直接从 Redis 获取。减少了去数据库查询的次数。但会出现新的问题,就是如何对缓存进行更新?

摘要: 原创出处 www.bysocket.com 

二、更新缓存的策略

参考《缓存更新的套路》 http://coolshell.cn/articles/17416.html,缓存更新的模式有四种:Cache aside, Read through, Write through, Write behind caching。

这里我们使用的是 Cache Aside 策略,从三个维度:(摘自 耗子叔叔博客)
失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
命中:应用程序从cache中取数据,取到后返回。
更新:先把数据存到数据库中,成功后,再让缓存失效。

大致的流程:
a. 从商品 Cache 中获取商品详情,如果存在,则返回获取 Cache 数据返回。
b. 如果不存在,则从商品 DB 中获取。获取成功后,将数据存到 Cache 中。则下次获取商品详情,就可以从 Cache 就可以得到商品详情数据。
c. 从商品 DB 中更新或者删除商品详情成功后,则从缓存中删除对应商品的详情缓存


三、案例的应用

 3.1)安装Redis忽略

 3.2)数据库忽略(同五)

 3.3)工程结构
 

3.4)pom依赖包redis,还有jsp,json 其他忽略
	<!-- spring-boot-starter-redis 依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-redis</artifactId>
			<version>1.3.1.RELEASE</version>
		</dependency>
		
		
        
        <!--JSP-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        
        
        
        <!-- apache common -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        
        <!-- json jar -->
        <dependency>  
            <groupId>com.jayway.jsonpath</groupId>  
            <artifactId>json-path</artifactId>  
            <version>0.8.1</version>  
            <scope>test</scope>  
        </dependency>  
        <dependency>  
            <groupId>org.codehaus.jackson</groupId>  
            <artifactId>jackson-core-lgpl</artifactId>  
            <version>1.8.5</version>  
        </dependency>  
        <dependency>  
            <groupId>org.codehaus.jackson</groupId>  
            <artifactId>jackson-mapper-lgpl</artifactId>  
            <version>1.8.5</version>  
        </dependency> 

3.5)  application.properties 应用配置文件,添加 Redis 相关配置
## 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
 
## Mybatis 配置
##mybatis.typeAliasesPackage=org.spring.springboot.domain
##mybatis.mapperLocations=classpath:mapper/*.xml
 
## Redis 配置
## Redis数据库索引(默认为0)
spring.redis.database=0
## Redis服务器地址
spring.redis.host=127.0.0.1
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=
## 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
## 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
## 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
## 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
## 连接超时时间(毫秒)
spring.redis.timeout=0
##jsp
spring.mvc.view.prefix=/ 
spring.mvc.view.suffix=.jsp 

3.6)实体类必须序列化 
public class City implements Serializable {...}

3.7)Dao接口
@Mapper
// 这个注解是Mybatis 的 Mapper(无需配置文件)
public interface CityDao {

	/**
	 * 根据城市名称,查询城市信息
	 * 
	 * @param cityName
	 *            城市名
	 */
	@Select("select * from city where id=#{id}")
	// 查询语句
	// 返回 Map 结果集
	@Results({ @Result(column = "id", property = "id"),
			@Result(column = "province_id", property = "provinceId"),
			@Result(column = "city_name", property = "cityName"),
			@Result(column = "description", property = "description")

	})
	public City findCity(@Param("id") Long id);

	/**
	 * 查询语句
	 * 
	 * 注意:属性与数据列名不一致就会显示NUll,那么就必须配置ResultMap一一对应
	 * 
	 * @return
	 */
	@Select("select * from city")
	// 返回 Map 结果集
	@Results({ @Result(column = "id", property = "id"),
			@Result(column = "province_id", property = "provinceId"),
			@Result(column = "city_name", property = "cityName"),
			@Result(column = "description", property = "description")

	})
	public List<City> all();

	/**
	 * 根据ID删除数据
	 * 
	 * @param id
	 * @return
	 */
	@Delete("delete from city where id=#{id}")
	Long delete(Long id);

	
	/**
	 * 添加数据
	 * 
	 * @param city
	 * @return
	 */
	@Insert("insert into city values(#{id},#{provinceId},#{cityName},#{description})")
	Long save(City city);

	
	/**
	 * 更新数据
	 * 
	 * @param city
	 * @return
	 */
	@Update("update city set province_id=#{provinceId},city_name=#{cityName},description=#{description} where id=#{id}")
	Long updates(City city);
}
3.8)业务实现类
@Service
public class CityServiceImpl implements CityService {

	// 日志对象 org.slf4j.Logger
	private Logger logger = LoggerFactory.getLogger(CityServiceImpl.class);

	// 注入dao
	@Autowired
	private CityDao cityDao;

	// 注入redisTemplate模板
	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 
	 * (1)如果缓存存在,从缓存中获取城市信息
	 * 
	 * (2)如果缓存不存在,从 DB 中获取城市信息,然后插入缓存
	 */
	@Override
	public City findCityName(Long id) {
		// 从缓存中获取
		String key = "city_" + id;
		
		System.out.println("findCityName===>"+key);
		
		// 操作视图接口类 String/Value 操作
		ValueOperations<String, City> operations = redisTemplate.opsForValue();
		// 缓存是否存在
		boolean flag = redisTemplate.hasKey(key);
		
		System.out.println("flag====>"+flag);
		// 判断
		if (flag) {
			// 获得value
			City city = operations.get(key);

			// 输出日志
			logger.info("CityServiceImpl.findCityName(),从缓存中获取数据:"
					+ city.toString());
			return city;

		}
		// 从 DB 中获取城市信息
		City citydb = cityDao.findCity(id);

		// 插入到缓存中
		operations.set(key, citydb, 10, TimeUnit.SECONDS);
		logger.info("CityServiceImpl.findCityName(),数据插入到缓存中:"
				+ citydb.toString());
		return citydb;
	}

	/**
	 * 查询所有的数据
	 */
	@Override
	public List<City> list() {
		// 从 DB 中获取城市信息
		List<City> list = cityDao.all();
		return list;
	}

	/**
	 * 添加数据
	 */
	@Override
	public Long add(City city) {
		// TODO Auto-generated method stub
		return cityDao.save(city);
	}

	/**
	 * 
	 * 删除
	 * (1)如果缓存存在,删除 (2)如果缓存不存在,不操作
	 */
	@Override
	public Long del(Long id) {
		// TODO Auto-generated method stub
		Long del = cityDao.delete(id);

		// 获得key
		String key = "city_" + id;

		// 判断是否存在缓存
		if (redisTemplate.hasKey(key)) {
			// 缓存存在,删除缓存
			redisTemplate.delete(key);

			// 日志跟踪
			logger.info("CityServiceImpl.del(),数据从缓存中删除>>>>" + id);
		}

		return del;
	}

	/**
	 * 
	 * 更新 (1)如果缓存存在,删除 (2)如果缓存不存在,不操作
	 */
	@Override
	public Long update(City city) {
		// TODO Auto-generated method stub
		Long my = cityDao.updates(city);
		
		System.out.println(my+"------>"+city);

		// 获得key
		String key = "city_" + city.getId();

		// 判断
		if (redisTemplate.hasKey(key)) {
			// 删除缓存
			redisTemplate.delete(key);

			// 日志跟踪
			logger.info("CityServiceImpl.update(),数据从缓存中删除>>>>" + my);
		}

		return my;
	}

}
3.9)控制类
@RestController
public class CityRestController {

	// 注入业务
	@Autowired
	private CityService cityService;

	/**
	 * 添加数据
	 * 
	 * @param cityName
	 * @return
	 */
	@RequestMapping(value = "/your/city", method = RequestMethod.POST)
	public Long addCity(@RequestBody City city) {
		return cityService.add(city);
	}

	/**
	 * 
	 * 更新数据
	 * 
	 * @param cityName
	 * @return
	 */
	@RequestMapping(value = "/your/city", method = RequestMethod.PUT)
	public void updateCity(@RequestBody City city) {
		cityService.update(city);
	}

	/**
	 * 
	 * 删除数据
	 * 
	 * @param cityName
	 * @return
	 */
	@RequestMapping(value = "/your/city/{id}", method = RequestMethod.DELETE)
	public Long updateCity(@PathVariable("id") Long id) {
		return cityService.del(id);
	}

	/**
	 * 
	 * 
	 * @param cityName
	 * @return
	 */
	// @RequestMapping("/your/city")
	// public List<City> allCity() {
	// return cityService.list();
	// }

	/**
	 * 查询一条数据
	 * 
	 * @param cityName
	 * @return
	 */
	@RequestMapping(value = "/your/city/{id}", method = RequestMethod.GET)
	public City findCity(@PathVariable("id") Long id) {
		return cityService.findCityName(id);
	}

}

3.10)springBoot启动类忽略
3.11)JSP页面
<body>
   
      
    <h3>查询界面</h3>  
    <input type="text" name="cityid" id="cityid"/>  
    <h3 id="c1"></h3>  
    <h3 id="c2"></h3>  
    <p><input type="button"  value="查询" id="ajaxFind"/> 
       <input type="button"  value="删除" id="ajaxDel"/>
    
    
    <fieldset>
    <legend>添加城市</legend>
      pid: <input type="text"  id="pid"/>  
      uname: <input type="text" id="uname"/>  
      desc: <input type="text"  id="desc"/> 
     <input type="button"  value="添加" id="ajaxAdd"/>
    </fieldset>
   
   <p/><p/><p/>
    
     <fieldset>
    <legend>修改城市</legend>
      id: <input type="text" name="cityid" id="cid"/>  
      pid: <input type="text"  id="ppid"/>  
      uname: <input type="text" id="puname"/>  
      desc: <input type="text"  id="pdesc"/> 
       <input type="button"  value="修改" id="ajaxPut"/>
    </fieldset>

    
    </p>  
      
</body> 

3.12)Web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- 配置 HiddenHttpMethodFilter 可以把POST请求转为 DELETE 和 PUT请求 -->
  <filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

 <filter>
    <filter-name>httpPutFormcontentFilter</filter-name>
    <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>httpPutFormcontentFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

3.13)Ajax无刷新
$(function() {

	// 单击事件
	$("#ajaxFind").click(function() {

		var id = $("#cityid").val();
		$.ajax({
			url : "your/city/" + id,
			data : {
				"id" : id
			},
			type : "get",
			dataType : "json",
			success : function(data) {
				$("#c1").html("名称:" + data.cityName);
				$("#c2").html("描述:" + data.description);
				$("#c2").show();

			},
			error : function() {
				$("#c1").html("查询失败");
				$("#c2").hide();
			}
		});

	});

	// 单击事件
	$("#ajaxDel").click(function() {

		var id = $("#cityid").val();

		if (id!="" && confirm("您确定要删除吗?")) {

			$.ajax({
				url : "your/city/" + id,
				data : {
					"id" : id
				},
				type : "delete",
				dataType : "json",
				success : function(data) {
					$("#c2").show();
				},
				error : function() {
					$("#c2").hide();
				}
			});
		}

	});
	// 单击事件
	$("#ajaxAdd").click(function() {

		// json数据
		var city = {
			"provinceId" : $("#pid").val(),
			"cityName" : $("#uname").val(),
			"description" : $("#desc").val()
		};
		$.ajax({
			url : "your/city",
			data : JSON.stringify(city),
			type : "post",
			contentType : 'application/json;charset=utf-8', // 指定类型
			dataType : JSON.stringify(city), //
			success : function(data) {
				$("#c2").show();
			},
			error : function() {
				$("#c2").hide();
			}
		});

	});
	// 单击事件
	$("#ajaxPut").click(function() {

		// json数据
		var city = {
			"id" : $("#cid").val(),
			"provinceId" : $("#ppid").val(),
			"cityName" : $("#puname").val(),
			"description" : $("#pdesc").val()
		};

		$.ajax({
			url : "your/city",
			data : JSON.stringify(city),
			type : "put",
			contentType : 'application/json;charset=utf-8', // 指定类型
			dataType : JSON.stringify(city), //
			success : function(data) {
				$("#c2").show();
			},
			error : function() {
				$("#c2").hide();
			}
		});

	});

});

运行效果:(注意不能写工程名哦! 必须启动Redis服务器)
数据库数据:


(1)查询


(2)添加



(3)删除





(4)修改


写入缓存中


https://github.com/yuanhangs/bearDay
 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值