最近打算研究一下Redis缓存,于是在本地手动搭建了一个SSM+redis的demo实例,该实例采用的是spring AOP管理日志以及@Transactional注解的方式管理Spring事务和Redis事务
开发环境及工具:jdk1.8,Eclipse,Maven,Tomcat7
spring整合redis需要引入相关jar包,版本需要注意一下,不然会报错:
jedis-2.9.0.jar
spring-data-redis-1.8.4.RELEASE.jar
spring-data-commons-1.8.4.RELEASE.jar
commons-pool2-2.5.0.jar
项目结构如下,标红部分分别是Redis工具类和Redis的配置文件:
Maven依赖文件如下:
pom.xml
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.java</groupId> <artifactId>ssm</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>ssm Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <spring.version>4.3.3.RELEASE</spring.version> <mybatis.version>3.4.5</mybatis.version> <mybatis-spring.version>1.3.1</mybatis-spring.version> <log4j.version>1.2.17</log4j.version> <redis.version>2.9.0</redis.version> <spring.data.redis.version>1.8.4.RELEASE</spring.data.redis.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!-- <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.2</version> </dependency> --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc7</artifactId> <version>12.1.0.2</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> <type>pom.lastUpdated</type> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.8.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.10</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${redis.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>${spring.data.redis.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>1.8.4.RELEASE</version> </dependency> <!-- <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-keyvalue</artifactId> <version>2.0.0.RELEASE</version> <scope>test</scope> </dependency> --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0-b07</version> </dependency> </dependencies> <build> <finalName>ssm</finalName> </build> </project>
Spring的配置文件,主要配置数据库连接池,Spring事务管理,Mybatis的SqlSession工厂,Spring AOP切面声明,以及Redis的集成。
由于AOP管理Redis的事务无效,所以改用注解的方式实现事务管理, <tx:annotation-driven transaction-manager="txManager"/>
applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!-- 扫描包 --> <context:component-scan base-package="com.ssm.service"/> <context:component-scan base-package="com.ssm.dao"/> <context:component-scan base-package="com.ssm.redis"/> <!-- 数据库配置文件 --> <context:property-placeholder location="classpath:*.properties"/> <!-- 配置数据库连接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${dbcp.driverClassName}"/> <property name="url" value="${dbcp.url}"/> <property name="username" value="${dbcp.username}"/> <property name="password" value="${dbcp.password}"/> <property name="maxActive" value="${dbcp.maxActive}"/> <property name="maxIdle" value="${dbcp.maxIdle}"/> <property name="minIdle" value="${dbcp.minIdle}"/> <property name="initialSize" value="${dbcp.initialSize}"/> </bean> <!-- sqlsessionFactory配置 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/ssm/mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.ssm.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!-- 事务管理 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 事务通知 --> <!-- <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="get*" propagation="REQUIRED"/> <tx:method name="find*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> --> <!-- AOP事务管理 --> <!-- <aop:config> <aop:pointcut expression="execution(* com.ssm.service.*.*(..))" id="pointCut"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/> </aop:config> --> <!-- 注解的事务管理 --> <tx:annotation-driven transaction-manager="txManager"/> <!-- 注解的AOP管理 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- AOP切面 --> <bean id="LogAspect" class="com.ssm.aspect.LogAspect"/> <!-- Jedis连接池配置参数 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}"></property> <property name="maxTotal" value="${redis.maxTotal}"></property> <property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property> <property name="testOnBorrow" value="${redis.testOnBorrow}"></property> </bean> <!--redis连接工厂 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.hostName}"></property> <property name="port" value="${redis.port}"></property> <property name="timeout" value="${redis.timeout}"></property> <property name="poolConfig" ref="jedisPoolConfig"></property> </bean> <!--redis操作模版,使用该对象可以操作redis --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory"></property> <property name="keySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="hashKeySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="valueSerializer" > <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> <property name="hashValueSerializer" > <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> <!--开启事务 --> <property name="enableTransactionSupport" value="true"></property> </bean> </beans>
Mybatis配置文件很简单,只需要将实体类声明一个别名就可以了,工厂以及mapper文件扫描都交给spring管理。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="com.ssm.entity"/> </typeAliases> </configuration>
接下来是springmvc的配置,主要包括controller层的扫描,视图解析器前后缀,基于注解的处理器映射器和适配器<mvc:annotation-driven/>,拦截器Interceptor,以及自定义全局异常处理器ExceptionResolver
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns="http://www.springframework.org/schema/beans" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <context:component-scan base-package="com.ssm.controller"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> <mvc:annotation-driven/> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.ssm.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> <bean class="com.ssm.exception.ExceptionResolver"/> </beans>
db.properties,数据库配置可根据自己本机的环境添加
dbcp.driverClassName = dbcp.url = dbcp.username = dbcp.password = dbcp.maxActive = 20 dbcp.maxIdle = 20 dbcp.minIdle = 20 dbcp.initialSize = 0
redis.properties
redis.hostName = 127.0.0.1 redis.port = 6379 redis.timeout = 10000 redis.maxIdle = 400 redis.maxTotal = 400 redis.maxWaitMillis = 1000 redis.testOnBorrow = true
到这里,框架的搭建以及XML文件的配置基本已经完成,下一步开始实现业务代码
该业务主要实现用户的登录以及新增用户的功能,接下来看下是如何与Redis交互的。
UserLoginController.java
package com.ssm.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.ssm.entity.Users; import com.ssm.service.UserLoginService; @Controller public class UserLoginController { @Autowired private UserLoginService userLoginService; @RequestMapping("tologin") public String toLoginPage() { return "login/login"; } @RequestMapping("login") public String userLogin(Model model, Users user, HttpServletRequest req) { boolean bool = userLoginService.checkUser(user); if (bool == true) { req.getSession().setAttribute("user", user); return "login/loginSuccess"; } else { model.addAttribute("error", "用户名或密码错误!"); return "login/login"; } } @RequestMapping("addUser") public String addUser(Model model,Users user) throws Exception{ userLoginService.addUser(user); model.addAttribute("success", "添加成功"); return "login/loginSuccess"; } }
与redis交互,我们写在Service层,登录时,先去缓存中查找是否有数据,如果有就直接返回,没有再去数据库查找,如果数据库存在该用户,则将数据添加到缓存中,代码如下:
UserLoginServiceImpl.java
package com.ssm.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.ssm.dao.UserLoginDao; import com.ssm.entity.Users; import com.ssm.exception.UserException; import com.ssm.redis.RedisCacheUtil; import com.ssm.service.UserLoginService; @Service public class UserLoginServiceImpl implements UserLoginService{ @Autowired private UserLoginDao userLoginDao; @Autowired private RedisCacheUtil redisCacheUtil; public boolean checkUser(Users user) { //先去缓存中查找是否存在,没有再去数据库中查找 Users userRedis = (Users)redisCacheUtil.get(user.getUsername()); if(userRedis!=null) { if(userRedis.getPassword().equals(user.getPassword())) { System.out.println("用户名:"+userRedis.getUsername()+" 密码:"+userRedis.getPassword()); return true; } } int i = userLoginDao.checkUserDao(user); if(i>0) { //将数据放入缓存并设置过期时间600秒 boolean bool = redisCacheUtil.set(user.getUsername(), user,600); System.out.println(bool==false?"添加缓存失败":"添加缓存成功!"); return true; } return false; } /** * 用@Transactional实现spring事务管理和redis事务管理,为了验证事务的回滚,定义添加超过两笔记录即抛出自定义异常 * @param user */ @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void addUser(Users user) throws Exception{ for(int i=0;i<5;i++) { userLoginDao.addUser(user); redisCacheUtil.set(user.getUsername(), user); user.setUsername(user.getUsername()+i); if(i>=2) { //throw new RuntimeException("最多只能插入两笔记录"); throw new UserException("最多只能插入两笔记录"); } } } }
Redis工具类采用的是spring集成的redisTemplate操作缓存
RedisCacheUtil.java
package com.ssm.redis; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @Component public class RedisCacheUtil { @Autowired private RedisTemplate<String,Object> redisTemplate; /**指定缓存失效时间 * @param key * @param time * @return */ public boolean expire(String key,long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**根据key获取过期时间 * @param key * @return */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /**判断key是否存在 * @param key * @return */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /**删除缓存 * @param key 可以传一个值或多个 */ public void del(String ... key) { if(key!=null&&key.length>0) { if(key.length==1) { redisTemplate.delete(key[0]); }else { redisTemplate.delete(Arrays.asList(key)); } } } // ==========================String============================ /**普通缓存获取 * @param key * @return */ public Object get(String key) { try { Object object = redisTemplate.opsForValue().get(key); return object; } catch (Exception e) { e.printStackTrace(); return null; } } /**普通缓存放入 * @param key * @param value * @return */ public boolean set(String key,Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**普通缓存放入并设置时间 * @param key * @param value * @param time time如果小于等于0,将设置无限期 * @return */ public boolean set(String key,Object value,long time) { try { if(time>0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); }else { set(key,value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**递增或递减 * @param key * @param delta * @return */ public long incre(String key,long delta) { return redisTemplate.opsForValue().increment(key, delta); } //==============================Map================================== /**HashMap或HashSet缓存获取具体的value * @param key * @param item * @return */ public Object hget(String key,String item) { return redisTemplate.opsForHash().get(key, item); } /**获取所有键值对 * @param key * @return */ public Map<Object,Object> hmget(String key) { return redisTemplate.opsForHash().entries(key); } /**hashmap放入缓存 * @param key * @param map * @return */ public boolean hmset(String key,Map<Object,Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**hashmap放入缓存并设置时间 * @param key * @param map * @param time * @return */ public boolean hmset(String key,Map<Object,Object> map,long time) { try { redisTemplate.opsForHash().putAll(key, map); if(time>0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**向一张hash表中放入数据,如果不存在则创建 * @param key * @param item * @param value * @return */ public boolean hset(String key,String item,Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**向一张hash表中放入数据并设置时间,如果不存在则创建 * @param key * @param item * @param value * @param time * @return */ public boolean hset(String key,String item,Object value,long time) { try { redisTemplate.opsForHash().put(key, item, value); if(time>0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /**删除hash表中的值 * @param key * @param item */ public void hdel(String key,Object...item ) { redisTemplate.opsForHash().delete(key, item); } /**判断hash表中是否有该项的值 * @param key * @param item * @return */ public boolean hHasKey(String key,String item) { return redisTemplate.opsForHash().hasKey(key, item); } //==========================List================================= /** * 获取list缓存的内容 * @param key 键 * @param start 开始 * @param end 结束 0 到 -1代表所有值 * @return */ public List<Object> lGet(String key,long start, long end){ try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 获取list缓存的长度 * @param key 键 * @return */ public long lGetListSize(String key){ try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 通过索引 获取list中的值 * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 * @return */ public Object lGetIndex(String key,long index){ try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, List<Object> value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, List<Object> value, long time) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据索引修改list中的某条数据 * @param key 键 * @param index 索引 * @param value 值 * @return */ public boolean lUpdateIndex(String key, long index,Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 移除N个值为value * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ public long lRemove(String key,long count,Object value) { try { Long remove = redisTemplate.opsForList().remove(key, count, value); return remove; } catch (Exception e) { e.printStackTrace(); return 0; } } }
dao接口,没有实现类,dao层动态代理,注意接口方法要和mapper文件id名称一致:
UserLoginDao.java
package com.ssm.dao; import com.ssm.entity.Users; public interface UserLoginDao { public int checkUserDao(Users user); public void addUser(Users user); }
mapper文件:
UserLoginMapper.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 namespace="com.ssm.dao.UserLoginDao"> <select id="checkUserDao" parameterType="Users" resultType="int"> select count(*) from tblogin where loginname = #{username} and loginpassword= #{password} </select> <insert id="addUser" parameterType="Users"> insert into tblogin(loginid,loginname,loginpassword) values(seq_tbu_userrole.nextval,#{username},#{password}) </insert> </mapper>
到这一步,业务类已经基本编写完成,下一步到页面上测试:
这个时候缓存中是没有的,会去数据库查询,数据库如果存在该用户,会添加到缓存中,我们看到已经添加到缓存中了。
我们再登录一次,会发现,没有去数据库查询,通过AOP管理日志打印可看到执行效率提升很多
Opening RedisConnection
用户名:CarryStone 密码:123456
[INFO ] 2018-12-28 15:23:44,807 method:com.ssm.aspect.LogAspect.around(LogAspect.java:40)
Around:execution(boolean com.ssm.service.impl.UserLoginServiceImpl.checkUser(Users))耗时:1ms
[INFO ] 2018-12-28 15:23:44,807 method:com.ssm.aspect.LogAspect.around(LogAspect.java:41)
接下来看一下spring事务管理和redis事务管理,我们采用注解的方式
提交后发现数据库和redis都没有新增,提交的数据都已经回滚了。
控制台信息如下,抛出了异常信息并且事务已经回滚,说明事务已经生效:
如果不想回滚,可将@Transactional注解注释掉,再看下是否起作用,要注意的是抛出一般的异常spring不会回滚该事务,只有抛出Error及RuntimeException及其子类才会捕捉并回滚
可在注解中添加自定义的异常即可
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)