最近打算研究一下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)