学习指南:
我们在写一般的项目在使用增删改查,一般是直接操作数据库,但是我们可以换一个思路,如果当前时间段1万个人同时访问数据库,就会造成数据库的宕机。我们可不可以换个思路,把数据库复制一份“一模一样”的数据,只要查一次就完成了复制,如果下次再进行查询,就直接从“副本”里进行查询。这样就不会直接访问数据库了。这种副本就叫做缓存。这个技术就叫做缓存技术。
基础项目三层结构:
添加缓存后的层次结构:
RedisTemplate对象介绍
使用Spring提供的一个操作Redis数据库的模板对象,提供了对象redis数据库的增删查改的功能,封装了Jedis,同时自身实现了:
对象的序列化和反序列化功能(obj----byte[]),作为对象存储的默认方案。
如果需要将对象转化为json字符串存储,需要覆盖默认方案。
RedisTemplate操作对象我们已经在之前以及有提及,所以这次直接上手项目:
第一步:导入pom依赖(和之前ssm整合的依赖无异,需要额外添加的是:jackson包、Jedis包、spring-data-redis包(封装了Jedis))
<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>
<groupId>com.bjsxt.usercache</groupId>
<artifactId>maven-cache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<!--声明需要的资源坐标 -->
<dependencies>
<!--mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.3</version>
</dependency>
<!--德鲁伊数据连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- jsp -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!-- 因为RedisTemplate是将对象修改为jaskson数组进行读取,所以这里需要这个转环包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.0</version>
</dependency>
<!-- RedisTemplate底部是Jedis的封装,所以也是需要Jedis插件 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 使用RedisTemplate -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.6.RELEASE</version>
</dependency>
</dependencies>
</project>
第二步:书写一个正常的ssm项目:我们这里书写一个能够正常的查询,修改,删除的ssm简易项目:
xml文件一:application-context.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 实例化dataSource连接池 -->
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property>
<property name="url"
value="jdbc:mysql://localhost:3306/db_email"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 实例化SqlSessionFactory -->
<bean id="sqlSessionFactory"
class="org.apache.ibatis.session.SqlSessionFactory">
<!-- 输入数据库连接池 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 注入分页插件 -->
<property name="plugins">
<list>
<!-- 实例化 -->
<bean class="com.github.pagehelper.PageHelper">
<!-- 给properties属性注入值 -->
<property name="properties">
<props>
<!-- 方言 -->
<prop key="dialect">mysql</prop>
<!-- 分页合理化参数,不用自己判断最大页和最小页 -->
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</list>
</property>
</bean>
<!-- 扫描Mapper接口,产生代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bjsxt.mapper"></property>
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"></property>
</bean>
</beans>
文件二:applicationContext-service.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描service对象 -->
<context:component-scan base-package="com.bjsxt.service.impl"></context:component-scan>
<!--实例化事务管理对象 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 声明事务管理切面 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="load*" read-only="true"/>
<tx:method name="save*" read-only="false"/>
<tx:method name="insert*" read-only="false"/>
<tx:method name="delete*" read-only="false"/>
<tx:method name="update*" read-only="false"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.bjsxt.service.impl.*.*(..))" id="pc"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
</beans>
文件三:配置springmvc.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.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">
<context:component-scan
base-package="com.bjsxt.controller"></context:component-scan>
<mvc:annotation-driven></mvc:annotation-driven>
<!-- <mvc:resources location="" mapping=""></mvc:resources> -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
配置文件四:我们在这里还需要加入cache.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!-- 使用redisTemplate操作数据库对象 -->
<bean id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate">
<!-- 因为需要使用jedis连接数据库,所以我们需要一个工厂对数据库进行连接
进入该类>找到该类的父类,父类有构造方法,方法里有connectionFactory,底层还是需要这个工厂连接数据库
keySerializer实现键的序列化 点进去查看类 因为对key不要求选择String的
valueSerializer实现值的序列化,选择General
-->
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="keySerializer" ref="keySerializer"></property>
<property name="valueSerializer" ref="valueSerializer"></property>
</bean>
<!-- 通过工厂连接数据库 -->
<bean id="connectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="192.168.224.11"></property>
<property name="port" value="6379"></property>
</bean>
<!-- 键与值都是通过无参构造方法对其进行连接 -->
<bean id="keySerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
<bean id="valueSerializer"
class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"></bean>
</beans>
配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>car2-controller</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>charset</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>charset</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
关键部分:Service实现类
缓存是从service实现类时查询缓存:
package com.bjsxt.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.bjsxt.mapper.UserMapper;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
//加载redisTemplate对象
@Autowired
RedisTemplate<String, User>redisTemplate;
@Override
public User selectUser(Integer uid) {
//通过redisTemplate绑定键
BoundValueOperations<String,User> boundValueOps = redisTemplate.boundValueOps(String.valueOf(uid));
//通过这个键取得相应的值(对象)
User user = boundValueOps.get();
//第一次查为空,之后查就一直会从缓存中读取数据
if(user!=null) {
return user;
}
//第一次查的情况会先从数据库查
user=userMapper.selectUser(uid);
//将查到的数据添加到Redis数据库中
boundValueOps.set(user);
return null;
}
@Override
public void updateUser(User user) {
BoundValueOperations<String,User> boundValueOps = redisTemplate.boundValueOps(String.valueOf(user.getUid()));
//对相同的键进行值的覆盖,也就是对象的覆盖达到更新的效果
boundValueOps.set(user);
//更新数据库操作
userMapper.updateUser(user);
}
@Override
public void deleteUser(Integer uid) {
//直接删除键为uid的这个对象
redisTemplate.delete(String.valueOf(uid));
userMapper.deleteUser(uid);
}
}
这样就可以通过缓存来进行查询了
总结: 需要修改pom中的依赖 (Jedis spring-data-jedis jackson)
添加cache.xml注解。
修改service实现类修改代码,其余地方不动
二:使用注解来实现缓存机制:
与自己手写代码不同的是,
一:对cache.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: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/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
">
<!-- 实例化Redis的链接工厂 -->
<bean id="connectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="192.168.25.100"></property>
<property name="port" value="6379"></property>
</bean>
<!-- 实例化StringRedisSerializer,实现key的序列化
StringRedisSerializer keySerializer=new StringRedisSerializer();
-->
<bean id="keySerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
<!-- 实例化GenericJackson2JsonRedisSerializer,实现key的序列化 -->
<bean id="valueSerializer"
class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer">
</bean>
<!-- 实例化RedisTemplate对象 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<!-- 注入redis链接工厂对象 -->
<property name="connectionFactory" ref="connectionFactory"></property>
<!-- 给keySerializer注入值
redisTemplate.setKeySerializer(keySerializer);
-->
<property name="keySerializer" ref="keySerializer"></property>
<!-- 给valueSerializer注入值 -->
<property name="valueSerializer" ref="valueSerializer"></property>
</bean>
<!-- 实例化cacheManager对象 -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<!-- 注入RedisTemplate对象 -->
<constructor-arg name="redisOperations" ref="redisTemplate"></constructor-arg>
</bean>
<!-- 开启缓存注解驱动 -->
<cache:annotation-driven cache-manager="cacheManager"/>
</beans>
修改Service层的返回值(有注解是对方法的返回值进行缓存,所以更新不能再是无返回值的了。),在serviceImpl添加注解
这么做的优点是在不改动源代码的情况下就能够实现缓存机制。
实现类的代码:
package com.bjsxt.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
@Service
public class UsersServiceImpl implements UsersService {
//注入Mapper
@Autowired
private UsersMapper usersMapper;
/***
* @Cacheable() 作用,缓存方法的返回值,该方法在第一次查询的时候进入
* cacheNames:给缓存起的名字
* key:表示redis数据库的键
*/
@Cacheable(cacheNames= {"users_cahce"},key="#id+''")
@Override
public Users loadUsersService(Integer id) {
// TODO Auto-generated method stub
System.out.println("=======查询数据库========");
Users users= usersMapper.loadUsersMapper(id);
return users;
}
@CacheEvict(cacheNames= {"users_cahce"},key="#id+''")
@Override
public void deleteUsersService(Integer id) {
// TODO Auto-generated method stub
usersMapper.deleteUsersMapper(id);
}
/***
* @CachePut作用,缓存方法的返回值,每次都进入
* cacheNames:给缓存起的名字
* key:表示redis数据库的键
* 可以使用spring的el表达式,主要是获得方法的参数值
* 如果是简单的数据类型String,Integer
* 只有一个参数:#p0 或者 #argName
* 只有一个参数:#p0,#p1 或者 #argName1,#argName2
* 如果参数是javabean对象:
* #javabean.属性名
*
*/
@CachePut(cacheNames= {"users_cahce"},key="#users.id+''")
@Override
public Users updateUsersService(Users users) {
// TODO Auto-generated method stub
System.out.println("=======更新redis缓存======");
usersMapper.updateUsersMapper(users);
return users;
}
}
|