├── .gitignore ├── .settings ├── org.eclipse.wst.jsdt.ui.superType.name ├── org.eclipse.wst.validation.prefs ├── org.eclipse.wst.jsdt.ui.superType.container ├── org.eclipse.m2e.core.prefs ├── org.eclipse.wst.common.project.facet.core.xml ├── org.eclipse.jdt.core.prefs ├── .jsdtscope └── org.eclipse.wst.common.component ├── src ├── main │ ├── java │ │ └── com │ │ │ └── liuyu │ │ │ ├── redisperm │ │ │ ├── entity │ │ │ │ ├── Praise.java │ │ │ │ ├── Product.java │ │ │ │ └── Comment.java │ │ │ ├── util │ │ │ │ ├── GlobalSchema.java │ │ │ │ ├── StringUnicodeSerializer.java │ │ │ │ ├── JSONUtil.java │ │ │ │ └── RedisPagingFactory.java │ │ │ ├── messagepack │ │ │ │ └── MessagePackTest.java │ │ │ └── action │ │ │ │ ├── ProductAction.java │ │ │ │ └── ArticleRedisAction.java │ │ │ └── rediscache │ │ │ ├── dao │ │ │ ├── UserDao.java │ │ │ └── BaseDao.java │ │ │ ├── datasource │ │ │ ├── DynamicDataSource.java │ │ │ └── DataSourceSwitcher.java │ │ │ ├── entity │ │ │ └── User.java │ │ │ ├── aop │ │ │ └── DataSourceAdvice.java │ │ │ ├── service │ │ │ └── UserService.java │ │ │ └── controller │ │ │ └── UserController.java │ ├── resources │ │ ├── config.properties │ │ ├── app-web.xml │ │ ├── logback.xml │ │ ├── app-redis.xml │ │ └── app-context.xml │ └── webapp │ │ └── WEB-INF │ │ ├── views │ │ ├── edit.jsp │ │ └── list.jsp │ │ └── web.xml └── test │ └── java │ └── com │ └── liuyu │ └── redisperm │ └── action │ ├── ProductActionTest.java │ └── ArticleRedisActionTest.java ├── .classpath ├── .project └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.name: -------------------------------------------------------------------------------- 1 | Window -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.validation.prefs: -------------------------------------------------------------------------------- 1 | disabled=06target 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.container: -------------------------------------------------------------------------------- 1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/entity/Praise.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.entity; 2 | 3 | /** 4 | * 评论赞数 5 | * @author liuyu 6 | * 7 | */ 8 | public class Praise { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.dao; 2 | 3 | import org.springframework.stereotype.Repository; 4 | 5 | import com.liuyu.rediscache.entity.User; 6 | 7 | @Repository("userDao") 8 | public class UserDao extends BaseDao { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/java/com/liuyu/redisperm/action/ProductActionTest.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.action; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class ProductActionTest { 8 | 9 | @Test 10 | public void test() { 11 | fail("Not yet implemented"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/liuyu/redisperm/action/ArticleRedisActionTest.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.action; 2 | 3 | import org.junit.Test; 4 | import org.springframework.context.support.ClassPathXmlApplicationContext; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | 7 | public class ArticleRedisActionTest { 8 | 9 | 10 | @Test 11 | public void saveArticleCommentTest(){ 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 4 | org.eclipse.jdt.core.compiler.compliance=1.5 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 8 | org.eclipse.jdt.core.compiler.source=1.5 9 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/datasource/DynamicDataSource.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.datasource; 2 | 3 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 4 | 5 | /** 6 | * 动态数据源 7 | * 8 | * @author liuyu 9 | */ 10 | public class DynamicDataSource extends AbstractRoutingDataSource { 11 | 12 | @Override 13 | protected Object determineCurrentLookupKey() { 14 | return DataSourceSwitcher.getDataSource(); 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /.settings/.jsdtscope: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/config.properties: -------------------------------------------------------------------------------- 1 | # App Config 2 | jdbc.driver=com.mysql.jdbc.Driver 3 | jdbc.write.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull 4 | jdbc.read.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull 5 | jdbc.username=root 6 | jdbc.password= 7 | jdbc.maxIdle=10 8 | jdbc.maxActive=50 9 | 10 | 11 | 12 | # Redis Config 13 | redis.hostName=localhost 14 | redis.password= 15 | redis.port=6379 16 | redis.timeout=1000 17 | 18 | 19 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.component: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/datasource/DataSourceSwitcher.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.datasource; 2 | import org.springframework.util.Assert; 3 | 4 | /** 5 | * 数据源选择器 6 | * 7 | * @author liuyu 8 | */ 9 | public class DataSourceSwitcher { 10 | @SuppressWarnings("rawtypes") 11 | private static final ThreadLocal contextHolder = new ThreadLocal(); 12 | 13 | @SuppressWarnings("unchecked") 14 | public static void setDataSource(String dataSource) { 15 | Assert.notNull(dataSource, "dataSource cannot be null"); 16 | contextHolder.set(dataSource); 17 | } 18 | 19 | public static void setWrite(){ 20 | clearDataSource(); 21 | } 22 | 23 | public static void setRead() { 24 | setDataSource("read"); 25 | } 26 | 27 | public static String getDataSource() { 28 | return (String) contextHolder.get(); 29 | } 30 | 31 | public static void clearDataSource() { 32 | contextHolder.remove(); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/edit.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=UTF-8" 2 | pageEncoding="UTF-8"%> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 4 | 5 | 6 | 7 | 8 | edit 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 |
username
userpass
25 | 26 |
29 | 30 |
31 | 32 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/list.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=UTF-8" 2 | pageEncoding="UTF-8"%> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 | 5 | 6 | 7 | 8 | Insert title here 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 |
idusernameuserpassoperate
${user.id }${user.username }${user.userpass } 24 | details 25 | edit 26 | del 27 |
31 | add 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/entity/Product.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.entity; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | import org.msgpack.annotation.MessagePackBeans; 7 | 8 | /** 9 | * 商品 10 | * @author liuyu 11 | * 12 | * @since 2015年4月22日 13 | */ 14 | @MessagePackBeans 15 | public class Product implements Serializable{ 16 | 17 | private static final long serialVersionUID = -5818433884770798578L; 18 | private int id; 19 | private String content; 20 | private List comments; 21 | 22 | public Product(){} 23 | 24 | public Product(int id, String content){ 25 | this.id = id; 26 | this.content = content; 27 | } 28 | 29 | public int getId() { 30 | return id; 31 | } 32 | 33 | public void setId(int id) { 34 | this.id = id; 35 | } 36 | 37 | public String getContent() { 38 | return content; 39 | } 40 | 41 | public void setContent(String content) { 42 | this.content = content; 43 | } 44 | 45 | public List getComments() { 46 | return comments; 47 | } 48 | 49 | public void setComments(List comments) { 50 | this.comments = comments; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | redis-cache 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.jsdt.core.javascriptValidator 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.common.project.facet.core.builder 20 | 21 | 22 | 23 | 24 | org.eclipse.wst.validation.validationbuilder 25 | 26 | 27 | 28 | 29 | org.eclipse.m2e.core.maven2Builder 30 | 31 | 32 | 33 | 34 | 35 | org.eclipse.jem.workbench.JavaEMFNature 36 | org.eclipse.wst.common.modulecore.ModuleCoreNature 37 | org.eclipse.jdt.core.javanature 38 | org.eclipse.m2e.core.maven2Nature 39 | org.eclipse.wst.common.project.facet.core.nature 40 | org.eclipse.wst.jsdt.core.jsNature 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/util/GlobalSchema.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.util; 2 | 3 | public class GlobalSchema { 4 | 5 | /** 6 | * 获取文章浏览数键 7 | * @param articleId 8 | * @return 9 | */ 10 | public static final String getArticleBrowseNumKey(String articleId){ 11 | return String.format("article:%s:browsenum", articleId); 12 | } 13 | 14 | /** 15 | * 获取文章评论键 16 | * @param articleId 17 | * @param commentId 18 | * @param username 19 | * @return 20 | */ 21 | public static final String getArticleCommentKey(String articleId, String commentId){ 22 | return String.format("article:%s:comment:%s", articleId, commentId); 23 | } 24 | 25 | /** 26 | * 获取通过时间集的文章评论键 27 | * @param articleId 28 | * @return 29 | */ 30 | public static final String getArticleCommentByTimeKey(String articleId){ 31 | return String.format("article:%s:comment:time", articleId); 32 | } 33 | 34 | /** 35 | * 获取通过赞集的文章评论键 36 | * @param articleId 37 | * @return 38 | */ 39 | public static final String getArticleCommentByPraiseKey(String articleId){ 40 | return String.format("article:%s:comment:praise", articleId); 41 | } 42 | 43 | /** 44 | * 获取用户的所有评论键 45 | * @param username 46 | * @return 47 | */ 48 | public static final String getCommentByUsernameKey(String username){ 49 | return String.format("comment:username:%s", username); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | import javax.persistence.Table; 10 | 11 | import org.hibernate.annotations.GenericGenerator; 12 | 13 | 14 | @Entity 15 | @Table(name = "s_user") 16 | public class User implements Serializable { 17 | 18 | private static final long serialVersionUID = -3809577546127859031L; 19 | 20 | private String id; 21 | private String username;// 用户名 22 | private String userpass;// 密码 23 | 24 | @Id 25 | @GenericGenerator(name = "UUID", strategy = "uuid") 26 | @GeneratedValue(generator = "UUID") 27 | @Column(length = 32, name = "id") 28 | public String getId() { 29 | return id; 30 | } 31 | 32 | public void setId(String id) { 33 | this.id = id; 34 | } 35 | 36 | @Column(length = 25, nullable = false) 37 | public String getUsername() { 38 | return username; 39 | } 40 | 41 | public void setUsername(String username) { 42 | this.username = username; 43 | } 44 | 45 | @Column(length = 32, nullable = false) 46 | public String getUserpass() { 47 | return userpass; 48 | } 49 | 50 | public void setUserpass(String userpass) { 51 | this.userpass = userpass; 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/messagepack/MessagePackTest.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.messagepack; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.msgpack.MessagePack; 8 | import org.msgpack.template.Templates; 9 | import org.msgpack.type.Value; 10 | import org.msgpack.unpacker.Converter; 11 | 12 | 13 | /** 14 | * 序列化msgPack 15 | * @author liuyu 16 | * 17 | */ 18 | public class MessagePackTest { 19 | 20 | public static void main(String[] args) throws IOException { 21 | // Create serialize objects. 22 | List src = new ArrayList(); 23 | src.add("msgpack"); 24 | src.add("kumofs"); 25 | src.add("viver"); 26 | 27 | MessagePack msgpack = new MessagePack(); 28 | // Serialize 29 | byte[] raw = msgpack.write(src); 30 | System.out.println(raw.length); 31 | 32 | // Deserialize directly using a template 33 | List dst1 = msgpack.read(raw, Templates.tList(Templates.TString)); 34 | System.out.println(dst1.get(0)); 35 | System.out.println(dst1.get(1)); 36 | System.out.println(dst1.get(2)); 37 | 38 | // Or, Deserialze to Value then convert type. 39 | Value dynamic = msgpack.read(raw); 40 | List dst2 = new Converter(dynamic) 41 | .read(Templates.tList(Templates.TString)); 42 | System.out.println(dst2.get(0)); 43 | System.out.println(dst2.get(1)); 44 | System.out.println(dst2.get(2)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/resources/app-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/aop/DataSourceAdvice.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.aop; 2 | import java.lang.reflect.Method; 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.aop.AfterReturningAdvice; 7 | import org.springframework.aop.MethodBeforeAdvice; 8 | import org.springframework.aop.ThrowsAdvice; 9 | 10 | import com.liuyu.rediscache.datasource.DataSourceSwitcher; 11 | 12 | /** 13 | * 数据操作切面 14 | * @author liuyu 15 | */ 16 | public class DataSourceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice { 17 | 18 | private Logger logger = LoggerFactory.getLogger(DataSourceAdvice.class); 19 | 20 | // service方法执行之前被调用 21 | public void before(Method method, Object[] args, Object target) throws Throwable { 22 | logger.debug("切入点: " + target.getClass().getName() + "类中" + method.getName() + "方法"); 23 | if(method.getName().startsWith("add") 24 | || method.getName().startsWith("create") 25 | || method.getName().startsWith("save") 26 | || method.getName().startsWith("edit") 27 | || method.getName().startsWith("update") 28 | || method.getName().startsWith("delete") 29 | || method.getName().startsWith("remove")){ 30 | logger.debug("切换到: writerDataSource"); 31 | DataSourceSwitcher.setWrite(); 32 | } 33 | else { 34 | logger.debug("切换到: readDataSource"); 35 | DataSourceSwitcher.setRead(); 36 | } 37 | } 38 | 39 | // service方法执行完之后被调用 40 | public void afterReturning(Object arg0, Method method, Object[] args, Object target) throws Throwable { 41 | } 42 | 43 | // 抛出Exception之后被调用 44 | public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable { 45 | DataSourceSwitcher.setRead(); 46 | logger.debug("出现异常,切换到: readDataSource"); 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/entity/Comment.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | import org.msgpack.annotation.MessagePackBeans; 6 | 7 | 8 | /** 9 | * 评论 10 | * @author liuyu 11 | * 12 | * @since 2015年4月22日 13 | */ 14 | //@MessagePackBeans 15 | public class Comment implements Serializable{ 16 | 17 | private static final long serialVersionUID = 6568310863644187637L; 18 | /** 评论ID */ 19 | private String id; 20 | /** 评论内容 */ 21 | private String content; 22 | /** 商品Id */ 23 | private String productId; 24 | /** 用户ID */ 25 | private String username; 26 | /** 赞数 */ 27 | private int praise; 28 | 29 | public Comment(){} 30 | 31 | /** 32 | * 构造方法 33 | * @param id 评论ID 34 | * @param content 评论内容 35 | * @param username 用户名 36 | * @param productId 商品ID 37 | * @param praise 赞数 38 | */ 39 | public Comment(String id, String content, String username, String productId, int praise){ 40 | this.id = id; 41 | this.content = content; 42 | this.productId = productId; 43 | this.username = username; 44 | this.praise = praise; 45 | } 46 | 47 | public String getId() { 48 | return id; 49 | } 50 | public void setId(String id) { 51 | this.id = id; 52 | } 53 | public String getContent() { 54 | return content; 55 | } 56 | public void setContent(String content) { 57 | this.content = content; 58 | } 59 | public String getProductId() { 60 | return productId; 61 | } 62 | public void setProductId(String productId) { 63 | this.productId = productId; 64 | } 65 | public String getUsername() { 66 | return username; 67 | } 68 | public void setUsername(String username) { 69 | this.username = username; 70 | } 71 | public int getPraise() { 72 | return praise; 73 | } 74 | public void setPraise(int praise) { 75 | this.praise = praise; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Archetype Created Web Application 7 | 8 | 9 | 10 | contextConfigLocation 11 | classpath*:app-context.xml 12 | 13 | 14 | 15 | spring.profiles.active 16 | ${profiles.active} 17 | 18 | 19 | 20 | org.springframework.web.context.ContextLoaderListener 21 | 22 | 23 | 24 | org.springframework.web.util.IntrospectorCleanupListener 25 | 26 | 27 | 28 | encodingFilter 29 | org.springframework.web.filter.CharacterEncodingFilter 30 | 31 | encoding 32 | UTF-8 33 | 34 | 35 | forceEncoding 36 | true 37 | 38 | 39 | 40 | 41 | encodingFilter 42 | /* 43 | 44 | 45 | 46 | springMVC 47 | org.springframework.web.servlet.DispatcherServlet 48 | 49 | contextConfigLocation 50 | classpath*:app-web.xml 51 | 52 | 1 53 | 54 | 55 | springMVC 56 | / 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/dao/BaseDao.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.dao; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.orm.hibernate3.HibernateTemplate; 7 | import org.springframework.stereotype.Repository; 8 | 9 | @Repository("baseDao") 10 | public class BaseDao { 11 | 12 | @Autowired 13 | private HibernateTemplate hibernateTemplate; 14 | 15 | public int getTotalCount(String hql) { 16 | return hibernateTemplate.find(hql).size(); 17 | } 18 | 19 | public void save(T t) { 20 | hibernateTemplate.save(t); 21 | } 22 | public void update(T t) { 23 | hibernateTemplate.update(t); 24 | } 25 | 26 | public void saveOrUpdate(T t) { 27 | hibernateTemplate.saveOrUpdate(t); 28 | } 29 | 30 | public void delete(T t) { 31 | hibernateTemplate.delete(t); 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | public List list(String hql) { 36 | return hibernateTemplate.find(hql); 37 | } 38 | 39 | @SuppressWarnings({ "rawtypes", "unchecked" }) 40 | public T findById(String id, Class clazz) { 41 | if (id == null || "".equals(id.trim())) 42 | return null; 43 | if (clazz == null) 44 | throw new RuntimeException("查询主体不能为空"); 45 | String hql = "from " + clazz.getSimpleName() + " t where t.id=? "; 46 | List list = hibernateTemplate.find(hql, id); 47 | if (list != null && list.size() > 0) 48 | return list.get(0); 49 | else 50 | return null; 51 | } 52 | 53 | @SuppressWarnings({ "unchecked", "rawtypes" }) 54 | public T lockByid(String id, Class clazz) { 55 | if (id == null || "".equals(id.trim())) 56 | return null; 57 | if (clazz == null) 58 | throw new RuntimeException("查询主体不能为空"); 59 | String hql = "from " + clazz.getSimpleName() 60 | + " t where t.id=? for update"; 61 | List list = hibernateTemplate.find(hql, id); 62 | if (list != null && list.size() > 0) 63 | return list.get(0); 64 | else 65 | return null; 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | %date [%thread] %-5level %logger{80} - %msg%n 11 | 12 | 13 | 14 | 15 | 17 | UTF-8 18 | ${log.base}.log 19 | 20 | 21 | ${log.base}.%d{yyyy-MM-dd}.log.zip 22 | 23 | 24 | 25 | 26 | %date [%thread] %-5level %logger{80} - %msg%n 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/resources/app-redis.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.cache.annotation.CacheEvict; 7 | import org.springframework.cache.annotation.CachePut; 8 | import org.springframework.cache.annotation.Cacheable; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import com.liuyu.rediscache.dao.UserDao; 13 | import com.liuyu.rediscache.entity.User; 14 | 15 | /** 16 | * 参数选项: 17 | * @Cacheable:根据方法的请求参数对其结果进行缓存,即第一次从数据库查询真实的数据 18 | * @CachePut:根据方法的请求参数对其结果进行缓存;和Cacheable不同的是,每次都会触发真实的方法调用 19 | * @CachEvict:根据方法的请求参数对缓存进行清空 20 | * @author liuyu 21 | * 22 | */ 23 | @Service("userService") 24 | @Transactional 25 | public class UserService { 26 | 27 | @Autowired 28 | private UserDao userDao; 29 | 30 | /** 31 | * 添加用户时,直接将key对应的数据存入到缓存 32 | * @param user 33 | */ 34 | // @CacheEvict(value = "user", key = "'all'") 35 | // @CachePut(value = "user", key = "#user.id") 36 | public void addUser(User user) { 37 | userDao.save(user); 38 | } 39 | 40 | /** 41 | * 如果缓存中有key对应的数据,则从缓存中取;如果没有则查询数据库 42 | * @param id 43 | * @return 44 | */ 45 | @Cacheable(value = "user", key = "#id") 46 | public User findById(String id) { 47 | return userDao.findById(id, User.class); 48 | } 49 | 50 | // @Cacheable(value = "user", key = "'all'") 51 | public List list() { 52 | return userDao.list("from User"); 53 | } 54 | 55 | /** 56 | * 修改用户时,直接将key对应的数据更新到缓存 57 | * @param user 58 | */ 59 | // @CacheEvict(value = "user", key="'all'") 60 | @CacheEvict(value = "user", key = "#user.id") 61 | public void updateUser(User user) { 62 | userDao.update(user); 63 | } 64 | 65 | /** 66 | * 删除用户时,清空key对应的缓存 67 | * @param id 68 | * @return 69 | */ 70 | // @CacheEvict(value = "user", key = "'all'") 71 | @CacheEvict(value = "user", key = "#id") 72 | public String deleteUser(String id) { 73 | User user = userDao.findById(id, User.class); 74 | userDao.delete(user); 75 | return "success"; 76 | } 77 | 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/rediscache/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.rediscache.controller; 2 | 3 | import java.util.List; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.ui.Model; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | 15 | import com.liuyu.rediscache.entity.User; 16 | import com.liuyu.rediscache.service.UserService; 17 | 18 | @Controller 19 | public class UserController { 20 | 21 | @Autowired 22 | private UserService userService; 23 | 24 | @RequestMapping(value = "/list") 25 | public String list(HttpServletRequest request, Model model) { 26 | List users = userService.list(); 27 | model.addAttribute("users", users); 28 | return "list"; 29 | } 30 | 31 | @RequestMapping(value = "/json") 32 | @ResponseBody 33 | public String json(HttpServletRequest request, Model model) { 34 | List users = userService.list(); 35 | return users.toString(); 36 | } 37 | 38 | @RequestMapping("/details") 39 | public String details(HttpServletRequest request, @RequestParam String id) { 40 | User user = userService.findById(id); 41 | return "redirect:/list"; 42 | } 43 | 44 | @RequestMapping(value = "/edit", method = RequestMethod.GET) 45 | public String edit(Model model, String id) { 46 | if(id != null && !"".equals(id)){ 47 | User user = userService.findById(id); 48 | model.addAttribute("user", user); 49 | } 50 | return "edit"; 51 | } 52 | 53 | @RequestMapping(value = "/save", method = RequestMethod.POST) 54 | public String save(HttpServletRequest request, User user) { 55 | if(user != null && !"".equals(user.getId())){ 56 | userService.updateUser(user); 57 | } else { 58 | userService.addUser(user); 59 | } 60 | return "redirect:/list"; 61 | } 62 | 63 | @RequestMapping(value = "/del", method = RequestMethod.GET) 64 | public String del(HttpServletRequest request, @RequestParam String id) { 65 | userService.deleteUser(id); 66 | return "redirect:/list"; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/util/StringUnicodeSerializer.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.util; 2 | 3 | import java.io.IOException; 4 | 5 | import com.fasterxml.jackson.core.JsonGenerationException; 6 | import com.fasterxml.jackson.core.JsonGenerator; 7 | import com.fasterxml.jackson.core.JsonProcessingException; 8 | import com.fasterxml.jackson.core.io.CharTypes; 9 | import com.fasterxml.jackson.core.json.JsonWriteContext; 10 | import com.fasterxml.jackson.databind.JsonSerializer; 11 | import com.fasterxml.jackson.databind.SerializerProvider; 12 | 13 | public class StringUnicodeSerializer extends JsonSerializer { 14 | 15 | private final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); 16 | private final int[] ESCAPE_CODES = CharTypes.get7BitOutputEscapes(); 17 | 18 | private void writeUnicodeEscape(JsonGenerator gen, char c) throws IOException { 19 | gen.writeRaw('\\'); 20 | gen.writeRaw('u'); 21 | gen.writeRaw(HEX_CHARS[(c >> 12) & 0xF]); 22 | gen.writeRaw(HEX_CHARS[(c >> 8) & 0xF]); 23 | gen.writeRaw(HEX_CHARS[(c >> 4) & 0xF]); 24 | gen.writeRaw(HEX_CHARS[c & 0xF]); 25 | } 26 | 27 | private void writeShortEscape(JsonGenerator gen, char c) throws IOException { 28 | gen.writeRaw('\\'); 29 | gen.writeRaw(c); 30 | } 31 | 32 | @Override 33 | public void serialize(String str, JsonGenerator gen, 34 | SerializerProvider provider) throws IOException, 35 | JsonProcessingException { 36 | int status = ((JsonWriteContext) gen.getOutputContext()).writeValue(); 37 | switch (status) { 38 | case JsonWriteContext.STATUS_OK_AFTER_COLON: 39 | gen.writeRaw(':'); 40 | break; 41 | case JsonWriteContext.STATUS_OK_AFTER_COMMA: 42 | gen.writeRaw(','); 43 | break; 44 | case JsonWriteContext.STATUS_EXPECT_NAME: 45 | throw new JsonGenerationException("Can not write string value here"); 46 | } 47 | gen.writeRaw('"');//写入JSON中字符串的开头引号 48 | for (char c : str.toCharArray()) { 49 | if (c >= 0x80){ 50 | writeUnicodeEscape(gen, c); // 为所有非ASCII字符生成转义的unicode字符 51 | }else { 52 | // 为ASCII字符中前128个字符使用转义的unicode字符 53 | int code = (c < ESCAPE_CODES.length ? ESCAPE_CODES[c] : 0); 54 | if (code == 0){ 55 | gen.writeRaw(c); // 此处不用转义 56 | }else if (code < 0){ 57 | writeUnicodeEscape(gen, (char) (-code - 1)); // 通用转义字符 58 | }else { 59 | writeShortEscape(gen, (char) code); // 短转义字符 (\n \t ...) 60 | } 61 | } 62 | } 63 | gen.writeRaw('"');//写入JSON中字符串的结束引号 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/util/JSONUtil.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.util; 2 | 3 | import java.io.IOException; 4 | import java.io.StringWriter; 5 | 6 | import org.apache.commons.io.IOUtils; 7 | 8 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 9 | import com.fasterxml.jackson.core.JsonGenerator; 10 | import com.fasterxml.jackson.core.JsonParser; 11 | import com.fasterxml.jackson.databind.ObjectMapper; 12 | import com.fasterxml.jackson.databind.SerializationFeature; 13 | import com.fasterxml.jackson.databind.module.SimpleModule; 14 | 15 | public enum JSONUtil { 16 | /** 17 | * 单例实例 18 | */ 19 | instance; 20 | 21 | private ObjectMapper objectMapper; 22 | 23 | /** 24 | * 懒惰单例模式得到ObjectMapper实例 此对象为Jackson的核心 25 | */ 26 | private JSONUtil() { 27 | this.objectMapper = new ObjectMapper(); 28 | // 当找不到对应的序列化器时 忽略此字段 29 | this.objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, 30 | false); 31 | this.objectMapper.configure( 32 | JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); 33 | // 使Jackson JSON支持Unicode编码非ASCII字符 34 | SimpleModule module = new SimpleModule(); 35 | module.addSerializer(String.class, new StringUnicodeSerializer()); 36 | this.objectMapper.registerModule(module); 37 | // 设置null值不参与序列化(字段不被显示) 38 | this.objectMapper.setSerializationInclusion(Include.NON_NULL); 39 | // 支持结束 40 | } 41 | 42 | /** 43 | * 创建JSON处理器的静态方法 44 | * 45 | * @param content 46 | * JSON字符串 47 | * @return 48 | */ 49 | private JsonParser getParser(String content) { 50 | try { 51 | return this.objectMapper.getFactory().createParser(content); 52 | } catch (IOException ioe) { 53 | return null; 54 | } 55 | } 56 | 57 | /** 58 | * 创建JSON生成器的静态方法, 使用标准输出 59 | * 60 | * @return 61 | */ 62 | private JsonGenerator getGenerator(StringWriter sw) { 63 | try { 64 | return this.objectMapper.getFactory().createGenerator(sw); 65 | } catch (IOException e) { 66 | return null; 67 | } 68 | } 69 | 70 | /** 71 | * JSON对象序列化 72 | * @throws IOException 73 | * 74 | * @throws JSONSerException 75 | */ 76 | public static String toJSON(Object obj) throws IOException { 77 | StringWriter sw = new StringWriter(); 78 | JsonGenerator jsonGen = JSONUtil.instance.getGenerator(sw); 79 | if (jsonGen == null) { 80 | IOUtils.closeQuietly(sw); 81 | return null; 82 | } 83 | // 由于在getGenerator方法中指定了OutputStream为sw 84 | // 因此调用writeObject会将数据输出到sw 85 | jsonGen.writeObject(obj); 86 | // 由于采用流式输出 在输出完毕后务必清空缓冲区并关闭输出流 87 | jsonGen.flush(); 88 | jsonGen.close(); 89 | return sw.toString(); 90 | } 91 | 92 | /** 93 | * JSON对象反序列化 94 | * 95 | * @throws IOException 96 | * 97 | * @throws JSONDeserException 98 | */ 99 | public static T fromJSON(String json, Class clazz) throws IOException { 100 | JsonParser jp = JSONUtil.instance.getParser(json); 101 | return jp.readValueAs(clazz); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/action/ProductAction.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.action; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import org.springframework.context.support.ClassPathXmlApplicationContext; 7 | import org.springframework.dao.DataAccessException; 8 | import org.springframework.data.redis.core.BoundValueOperations; 9 | import org.springframework.data.redis.core.ListOperations; 10 | import org.springframework.data.redis.core.RedisOperations; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | import org.springframework.data.redis.core.SessionCallback; 13 | import org.springframework.data.redis.core.SetOperations; 14 | import org.springframework.data.redis.core.ValueOperations; 15 | import org.springframework.data.redis.core.ZSetOperations; 16 | 17 | import com.liuyu.redisperm.entity.Comment; 18 | import com.liuyu.redisperm.entity.Product; 19 | 20 | public class ProductAction { 21 | 22 | private static RedisTemplate redisTemplate; 23 | 24 | public void valueOperationSample() { 25 | ValueOperations valueOper = redisTemplate.opsForValue(); 26 | Product product = new Product(1, "iphone6"); 27 | valueOper.set("product:" + product.getId(), product); 28 | } 29 | 30 | public static void listOperationSample() throws IOException { 31 | Product product = new Product(2, "iphone5"); 32 | ListOperations listOper = redisTemplate.opsForList(); 33 | // redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Product.class)); 34 | //messagePack序列化 35 | // MessagePack mspack = new MessagePack(); 36 | // byte[] data = mspack.write("abc"); 37 | // String data = JSONUtil.toJSON(product); 38 | // System.out.println(data); 39 | listOper.leftPush("product:list", product); 40 | // listOper.rightPush("product:list", data);// rpush,tail 41 | } 42 | 43 | public void boundValueOperationSample() { 44 | Product product = new Product(3, "iphone4"); 45 | BoundValueOperations bvo = redisTemplate 46 | .boundValueOps("product:" + product.getId()); 47 | bvo.set(product); 48 | bvo.expire(60, TimeUnit.MINUTES); 49 | } 50 | 51 | /** 52 | * 非连接池环境下,事务操作;每次操作(例如,get,set)都有会从pool中获取connection; 因此在连接池环境下,使用事务需要注意。 53 | */ 54 | public void txUnusedPoolSample() { 55 | Product product = new Product(1, "iphone6"); 56 | redisTemplate.watch("product:" + product.getId()); 57 | redisTemplate.multi(); 58 | ValueOperations tvo = redisTemplate.opsForValue(); 59 | tvo.set("product:" + product.getId(), product); 60 | redisTemplate.exec(); 61 | } 62 | 63 | /** 64 | * 在连接池环境中,需要借助sessionCallback来绑定connection 65 | */ 66 | public static void txProductPoolSample() { 67 | SessionCallback sessionCallBack = new SessionCallback() { 68 | 69 | public Product execute(RedisOperations operations) 70 | throws DataAccessException { 71 | operations.multi(); 72 | Product product = new Product(1, "iphone6"); 73 | String productKey = String.format("product:%d", product.getId()); 74 | String productScoresKey = "product:scores"; 75 | @SuppressWarnings("unchecked") 76 | SetOperations setOper = (SetOperations) operations.opsForSet(); 77 | setOper.add(productKey, product); 78 | @SuppressWarnings("unchecked") 79 | ZSetOperations zsetOper = (ZSetOperations) operations.opsForZSet(); 80 | zsetOper.add(productScoresKey, product.getId(), System.currentTimeMillis()); 81 | operations.exec(); 82 | return product; 83 | } 84 | }; 85 | redisTemplate.execute(sessionCallBack); 86 | } 87 | 88 | public static void txCommentPoolSample() { 89 | SessionCallback sessionCallBack = new SessionCallback() { 90 | 91 | public Comment execute(RedisOperations operations) 92 | throws DataAccessException { 93 | operations.multi(); 94 | Comment comment = new Comment("1", "good", "ly", "1", 32); 95 | String productCommentKey = String.format("product:%d:comment:%d", 1, comment.getId()); 96 | String productCommentScoreKey = String.format("product:%d:comment:scores", 1); 97 | @SuppressWarnings("unchecked") 98 | SetOperations setOper = (SetOperations) operations.opsForSet(); 99 | setOper.add(productCommentKey, comment); 100 | @SuppressWarnings("unchecked") 101 | ZSetOperations zsetOper = (ZSetOperations) operations.opsForZSet(); 102 | zsetOper.add(productCommentScoreKey, 1, 103 | System.currentTimeMillis()); 104 | operations.exec(); 105 | return comment; 106 | } 107 | }; 108 | redisTemplate.execute(sessionCallBack); 109 | } 110 | 111 | public static void main(String[] args) throws IOException { 112 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( 113 | "app-redis.xml"); 114 | redisTemplate = context.getBean(RedisTemplate.class); 115 | // txProductPoolSample(); 116 | // txCommentPoolSample(); 117 | listOperationSample(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/resources/app-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 16 | 17 | 19 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | org.hibernate.dialect.MySQL5InnoDBDialect 63 | 64 | true 65 | false 66 | update 67 | true 68 | utf-8 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.liuyu.rediscache 5 | redis-cache 6 | war 7 | 0.0.1-SNAPSHOT 8 | redis-cache Maven Webapp 9 | http://maven.apache.org 10 | 11 | 12 | 3.2.10.RELEASE 13 | 14 | 15 | 16 | 17 | org.springframework 18 | spring-webmvc 19 | ${spring.version} 20 | 21 | 22 | org.springframework 23 | spring-context-support 24 | ${spring.version} 25 | 26 | 27 | org.springframework 28 | spring-jdbc 29 | ${spring.version} 30 | 31 | 32 | org.springframework 33 | spring-orm 34 | ${spring.version} 35 | 36 | 37 | 38 | mysql 39 | mysql-connector-java 40 | 5.1.31 41 | 42 | 43 | org.apache.tomcat 44 | tomcat-jdbc 45 | 7.0.54 46 | 47 | 48 | ch.qos.logback 49 | logback-classic 50 | 1.1.2 51 | 52 | 53 | com.fasterxml.jackson.core 54 | jackson-databind 55 | 2.3.3 56 | 57 | 58 | org.aspectj 59 | aspectjweaver 60 | 1.8.1 61 | 62 | 63 | jstl 64 | jstl 65 | 1.2 66 | 67 | 68 | javax.servlet 69 | servlet-api 70 | 2.5 71 | provided 72 | 73 | 74 | javax.servlet.jsp 75 | jsp-api 76 | 2.2 77 | provided 78 | 79 | 80 | 81 | org.springframework.data 82 | spring-data-redis 83 | 1.3.1.RELEASE 84 | 85 | 86 | org.springframework 87 | spring-tx 88 | 89 | 90 | org.springframework 91 | spring-aop 92 | 93 | 94 | org.springframework 95 | spring-context-support 96 | 97 | 98 | 99 | 100 | redis.clients 101 | jedis 102 | 2.4.2 103 | 104 | 105 | commons-fileupload 106 | commons-fileupload 107 | 1.2.2 108 | 109 | 110 | commons-io 111 | commons-io 112 | 2.4 113 | 114 | 115 | 116 | 117 | org.hibernate 118 | hibernate-validator 119 | 5.1.3.Final 120 | 121 | 122 | org.hibernate 123 | hibernate-core 124 | 3.6.10.Final 125 | 126 | 127 | slf4j-api 128 | org.slf4j 129 | 130 | 131 | 132 | 133 | org.javassist 134 | javassist 135 | 3.15.0-GA 136 | 137 | 138 | 139 | 140 | org.msgpack 141 | msgpack 142 | 0.6.11 143 | 144 | 145 | 146 | com.fasterxml.jackson.core 147 | jackson-databind 148 | 2.4.3 149 | 150 | 151 | 152 | 153 | 154 | 155 | junit 156 | junit 157 | 4.10 158 | test 159 | 160 | 161 | org.springframework 162 | spring-test 163 | ${spring.version} 164 | test 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | org.apache.maven.plugins 176 | maven-war-plugin 177 | 178 | ${project.artifactId} 179 | 180 | 181 | true 182 | src/main/webapp 183 | 184 | **/web.xml 185 | 186 | 187 | 188 | ${basedir}/src/main/webapp 189 | ${basedir}/src/main/webapp/WEB-INF/web.xml 190 | 191 | 192 | 193 | org.mortbay.jetty 194 | maven-jetty-plugin 195 | 6.1.10 196 | 197 | 198 | 199 | profiles.active 200 | dev 201 | 202 | 203 | 204 | / 205 | 206 | 207 | 208 | 209 | org.apache.tomcat.maven 210 | tomcat7-maven-plugin 211 | 2.2 212 | 213 | UTF-8 214 | 215 | dev 216 | 217 | / 218 | 8080 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | dev 227 | 228 | dev 229 | 230 | 231 | 232 | test 233 | 234 | test 235 | 236 | 237 | 238 | production 239 | 240 | production 241 | 242 | 243 | true 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/action/ArticleRedisAction.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.action; 2 | 3 | import java.io.IOException; 4 | import java.util.Iterator; 5 | import java.util.Set; 6 | import java.util.UUID; 7 | 8 | import org.springframework.context.support.ClassPathXmlApplicationContext; 9 | import org.springframework.dao.DataAccessException; 10 | import org.springframework.data.redis.core.ListOperations; 11 | import org.springframework.data.redis.core.RedisOperations; 12 | import org.springframework.data.redis.core.RedisTemplate; 13 | import org.springframework.data.redis.core.SessionCallback; 14 | import org.springframework.data.redis.core.SetOperations; 15 | import org.springframework.data.redis.core.ValueOperations; 16 | import org.springframework.data.redis.core.ZSetOperations; 17 | import org.springframework.data.redis.serializer.GenericToStringSerializer; 18 | import org.springframework.data.redis.support.atomic.RedisAtomicLong; 19 | 20 | import com.liuyu.redisperm.entity.Comment; 21 | import com.liuyu.redisperm.util.GlobalSchema; 22 | import com.liuyu.redisperm.util.RedisPagingFactory; 23 | import com.liuyu.redisperm.util.RedisPagingFactory.RedisPageHelper; 24 | 25 | /** 26 | * 文章的评论的操作 27 | * @author liuyu 28 | * 29 | */ 30 | public class ArticleRedisAction { 31 | 32 | private static RedisTemplate redisTemplate1; 33 | private static RedisTemplate redisTemplate; 34 | private static RedisPagingFactory redisPag = new RedisPagingFactory(); 35 | private static RedisAtomicLong redisAtomiclong; 36 | 37 | /** 38 | * 保存文章评论 39 | * @param comment 40 | */ 41 | public static void saveArticleComment(final Comment comment){ 42 | redisTemplate.execute(new SessionCallback() { 43 | 44 | public Comment execute(RedisOperations operations) 45 | throws DataAccessException { 46 | operations.multi(); 47 | 48 | //文章评论key 49 | String articleCommentKey = GlobalSchema.getArticleCommentKey(comment.getProductId(), comment.getId()); 50 | //文章评论时间key集 51 | String articleCommentTimeKey = GlobalSchema.getArticleCommentByTimeKey(comment.getProductId()); 52 | //文章评论点赞key集 53 | String articleCommentPraiseKey = GlobalSchema.getArticleCommentByPraiseKey(comment.getProductId()); 54 | //用户评论key集 55 | String commentUsernameKey = GlobalSchema.getCommentByUsernameKey(comment.getUsername()); 56 | 57 | 58 | //文章的评论使用set 59 | @SuppressWarnings("unchecked") 60 | SetOperations setOper = (SetOperations) operations.opsForSet(); 61 | //单条评论 62 | setOper.add(articleCommentKey, comment); 63 | 64 | //文章的评论的展示排序使用zset 65 | @SuppressWarnings("unchecked") 66 | ZSetOperations zsetOper = (ZSetOperations) operations.opsForZSet(); 67 | //按时间排序存储 68 | zsetOper.add(articleCommentTimeKey, comment.getId(), System.currentTimeMillis()); 69 | //按赞数存储 70 | zsetOper.add(articleCommentPraiseKey, comment.getId(), comment.getPraise()); 71 | 72 | //用户评论使用list 73 | @SuppressWarnings("unchecked") 74 | ListOperations listOper = (ListOperations) operations.opsForList(); 75 | //存储用户所有的评论键,value为具体文章评论的key 76 | listOper.leftPush(commentUsernameKey, articleCommentKey); 77 | 78 | operations.exec(); 79 | return null; 80 | } 81 | }); 82 | } 83 | 84 | /** 85 | * 初始化文章浏览数 86 | * @param articleId 87 | */ 88 | public static void saveArticBrowseNum(final String articleId){ 89 | redisTemplate1.setValueSerializer(new GenericToStringSerializer(Long.class)); 90 | 91 | //文章浏览数Key 92 | String articleBrowseNumKey = GlobalSchema.getArticleBrowseNumKey(articleId); 93 | //文章的浏览数使用String 94 | ValueOperations valueOper = redisTemplate1.opsForValue(); 95 | valueOper.set(articleBrowseNumKey, 0l); 96 | } 97 | 98 | /** 99 | * 按赞数排序分页获取文章的评论Id集 100 | * @param articleId 文章Id 101 | * @param pageNo 当前页 102 | * @return 103 | */ 104 | public static RedisPageHelper getPageForArticleCommentIdsByPraise(String articleId, long pageNo){ 105 | return redisPag.getPageForZSet(redisTemplate, GlobalSchema.getArticleCommentByPraiseKey(articleId), String.class, pageNo, false); 106 | } 107 | 108 | /** 109 | * 按时间排序分页获取文章的评论Id集 110 | * @param articleId 文章Id 111 | * @param pageNo 当前页 112 | * @return 113 | */ 114 | public static RedisPageHelper getPageForArticleCommentIdsByTime(String articleId, long pageNo){ 115 | return redisPag.getPageForZSet(redisTemplate, GlobalSchema.getArticleCommentByTimeKey(articleId), String.class, pageNo, false); 116 | } 117 | 118 | /** 119 | * 通过用户名获取评论Key集 120 | * @param username 121 | * @return 122 | */ 123 | public static RedisPageHelper getPageForCommentByUsername(String username){ 124 | return redisPag.getPageForList(redisTemplate, GlobalSchema.getCommentByUsernameKey(username), String.class, 1l); 125 | } 126 | 127 | /** 128 | * 获取文章评论 129 | * @param articleId 文章ID 130 | * @param commentId 评论ID 131 | * @return 132 | * @throws IOException 133 | */ 134 | public static Comment getArticleComment(String articleId, String commentId) throws IOException{ 135 | SetOperations setOper = redisTemplate.opsForSet(); 136 | Set comments = setOper.members(GlobalSchema.getArticleCommentKey(articleId, commentId)); 137 | Iterator iter = comments.iterator(); 138 | Comment comment = null; 139 | 140 | while(iter.hasNext()){ 141 | comment = iter.next(); 142 | } 143 | return comment; 144 | } 145 | 146 | /** 147 | * 通过key获取评论 148 | * @param key 149 | * @return 150 | * @throws IOException 151 | */ 152 | public static Comment getCommentByKey(String key) throws IOException{ 153 | SetOperations setOper = redisTemplate.opsForSet(); 154 | Set comments = setOper.members(key); 155 | Iterator iter = comments.iterator(); 156 | Comment comment = null; 157 | 158 | while(iter.hasNext()){ 159 | comment = iter.next(); 160 | } 161 | return comment; 162 | } 163 | 164 | /** 165 | * 获取文章浏览数 166 | * @param key 167 | * @return 168 | */ 169 | public static Long getArticleBrowesNumByKey(String key){ 170 | ValueOperations valueOper = redisTemplate1.opsForValue(); 171 | Long browesNum = valueOper.increment(key, 1l); 172 | return browesNum; 173 | } 174 | 175 | public static void main(String[] args) throws IOException { 176 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( 177 | "app-redis.xml"); 178 | redisTemplate1 = context.getBean(RedisTemplate.class); 179 | redisTemplate = context.getBean(RedisTemplate.class); 180 | 181 | //添加评论 182 | for(int i=0;i<10;i++){ 183 | Comment comment = new Comment(); 184 | comment.setId(UUID.randomUUID().toString()); 185 | comment.setContent("I have a dream_" + i); 186 | comment.setProductId("1"); 187 | comment.setUsername("17090020140"); 188 | comment.setPraise(32-i); 189 | saveArticleComment(comment); 190 | } 191 | 192 | // String articleId = "1"; 193 | // //通过赞排序分页获取文章评论 194 | // RedisPageHelper redisPageHelper = getPageForArticleCommentIdsByPraise("1", 1l); 195 | // //通过时间排序分页获取文章评论 196 | //// RedisPageHelper redisPageHelper = getPageForArticleCommentIdsByTime(articleId, 1l); 197 | // 198 | // List commentIds = redisPageHelper.getData(); 199 | // List comments = new LinkedList(); 200 | // for(int i = 0; i < commentIds.size(); i ++){ 201 | // String commentId = commentIds.get(i); 202 | // Comment comment = getArticleComment("1", commentId); 203 | // comments.add(comment); 204 | // } 205 | // System.out.println(comments); 206 | 207 | //通过用户名分页获取评论 208 | // RedisPageHelper redisPageHelperByUsername = getPageForCommentByUsername("17090020140"); 209 | // List keys = redisPageHelperByUsername.getData(); 210 | // List comments = new LinkedList(); 211 | // for(int i = 0; i < keys.size(); i ++){ 212 | // String key = keys.get(i); 213 | // Comment comment = getCommentByKey(key); 214 | // comments.add(comment); 215 | // } 216 | // System.out.println(comments.get(0)); 217 | 218 | //初始化文章浏览数 219 | // saveArticBrowseNum("1"); 220 | //浏览数,每次加一 221 | // System.out.println(getArticleBrowesNumByKey("article:1:browsenum")); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/com/liuyu/redisperm/util/RedisPagingFactory.java: -------------------------------------------------------------------------------- 1 | package com.liuyu.redisperm.util; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.data.redis.core.ListOperations; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.data.redis.core.ZSetOperations; 12 | 13 | /** 14 | * Redis分页 15 | */ 16 | public class RedisPagingFactory { 17 | 18 | private static final Logger log = LoggerFactory.getLogger(RedisPagingFactory.class); 19 | 20 | /** 21 | * 内部分页助手,分页大小由工厂设置 22 | */ 23 | public final class RedisPageHelper{ 24 | 25 | /** 分页大小 */ 26 | private int pageSize; 27 | /** 当前页码 */ 28 | private long pageNo; 29 | /** 总页数 */ 30 | private long totalPages; 31 | /** 最小分数 */ 32 | private long minScore; 33 | /** 最大分数 */ 34 | private long maxScore; 35 | /** 元素总数 */ 36 | private long totalElements; 37 | /** 当前页数据 */ 38 | private List data; 39 | 40 | /** 41 | * 构造分页助手 42 | * @param pageSize 分页大小 43 | */ 44 | private RedisPageHelper(int pageSize){ 45 | this.pageSize = pageSize; 46 | } 47 | 48 | /** 49 | * 获取分页大小 50 | * @return 分页大小 51 | */ 52 | public int getPageSize() { 53 | return pageSize; 54 | } 55 | 56 | /** 57 | * 设置分页大小 58 | * @param pageSize 分页大小 59 | */ 60 | protected void setPageSize(int pageSize) { 61 | this.pageSize = pageSize; 62 | } 63 | 64 | /** 65 | * 获取当前页码 66 | * @return 当前页码 67 | */ 68 | public long getPageNo() { 69 | return pageNo; 70 | } 71 | 72 | /** 73 | * 设置当前页码 74 | * @param pageNo 当前页码 75 | */ 76 | public void setPageNo(long pageNo) { 77 | this.pageNo = pageNo; 78 | } 79 | 80 | /** 81 | * 获取总页数 82 | * @return 总页数 83 | */ 84 | public long getTotalPages() { 85 | return totalPages; 86 | } 87 | 88 | /** 89 | * 设置总页数 90 | * @param totalPages 总页数 91 | */ 92 | public void setTotalPages(long totalPages) { 93 | this.totalPages = totalPages; 94 | } 95 | 96 | /** 97 | * 获取元素总数 98 | * @return 元素总数 99 | */ 100 | public long getTotalElements() { 101 | return totalElements; 102 | } 103 | 104 | /** 105 | * 设置元素总数 106 | * @param totalElements 元素总数 107 | */ 108 | public void setTotalElements(long totalElements) { 109 | this.totalElements = totalElements; 110 | } 111 | 112 | /** 113 | * 获取当前页数据 114 | * @return 当前页数据 115 | */ 116 | public List getData() { 117 | return data; 118 | } 119 | 120 | /** 121 | * 设置当前页数据 122 | * @param data 当前页数据 123 | */ 124 | public void setData(List data) { 125 | this.data = data; 126 | } 127 | 128 | /** 129 | * 获取最小分数 130 | * @return 131 | */ 132 | public long getMinScore() { 133 | return minScore; 134 | } 135 | 136 | /** 137 | * 设置最小分数 138 | * @param minScore 139 | */ 140 | public void setMinScore(long minScore) { 141 | this.minScore = minScore; 142 | } 143 | 144 | /** 145 | * 获取最大分数 146 | * @return 147 | */ 148 | public long getMaxScore() { 149 | return maxScore; 150 | } 151 | 152 | /** 153 | * 获取最小分数 154 | * @param maxScore 155 | */ 156 | public void setMaxScore(long maxScore) { 157 | this.maxScore = maxScore; 158 | } 159 | } 160 | 161 | /** 默认分页大小 */ 162 | private static final int DEFAULT_PAGE_SIZE = 5; 163 | 164 | /** 分页大小 */ 165 | private int pageSize; 166 | 167 | /** 168 | * 安全地获取分页大小 169 | * @return 分页大小 170 | */ 171 | public int getPageSize() { 172 | return pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE; 173 | } 174 | 175 | /** 176 | * 安全地分页大小 177 | * @param pageSize 178 | */ 179 | public void setPageSize(int pageSize) { 180 | if (pageSize<=0){ 181 | log.warn("pageSize must be larger than 0. this property will be restored to default:{}", DEFAULT_PAGE_SIZE); 182 | this.pageSize = DEFAULT_PAGE_SIZE; 183 | }else{ 184 | this.pageSize = pageSize; 185 | } 186 | } 187 | 188 | /** 189 | * 生成空的分页助手 190 | * @param clazz 分页数据类型 191 | * @return 分页助手 192 | */ 193 | public RedisPageHelper getPageHelper(Class clazz){ 194 | return new RedisPageHelper(this.getPageSize()); 195 | } 196 | 197 | /** 198 | * 准备分页助手 199 | * @param totalElements 元素总数 200 | * @param pageNo 需要的页码 201 | * @param clazz 分页数据类型 202 | * @return 当元素总数=0或者需要的页码超出总页数时返回空,如果返回不为空再设置一次分页数据即可 203 | */ 204 | private RedisPageHelper preparePageHelper(long totalElements, long pageNo, Class clazz, long minScore, long maxScore){ 205 | if (totalElements <= 0){ 206 | return null; 207 | } 208 | //计算总分页数 209 | long totalPages = (long)Math.ceil((double)totalElements/this.getPageSize()); 210 | if (pageNo<1 || pageNo>totalPages){ 211 | return null; 212 | } 213 | RedisPageHelper helper = this.getPageHelper(clazz); 214 | helper.setPageNo(pageNo); 215 | helper.setTotalPages(totalPages); 216 | helper.setTotalElements(totalElements); 217 | helper.setMinScore(minScore); 218 | helper.setMaxScore(maxScore); 219 | return helper; 220 | } 221 | 222 | /** 223 | * 获取指定页码的分页数据开始下标 224 | * @param pageNo 页码 225 | * @return 分页数据开始下标 226 | */ 227 | private long getStartIdxForPage(long pageNo){ 228 | return Math.max((Math.max(pageNo, 1) - 1) * this.getPageSize(), 0); 229 | } 230 | 231 | /** 232 | * 获取指定页码的分页数据结束下标 233 | * @param pageNo 页码 234 | * @param totalElements 元素总数 235 | * @return 分页数据结束下标 236 | */ 237 | private long getEndIdxForPage(long pageNo, long totalElements){ 238 | return Math.min((Math.max(pageNo, 1) * this.getPageSize()) - 1, totalElements); 239 | } 240 | 241 | /** 242 | * 分页获取List内的数据 243 | * @param template Redis操作模板 244 | * @param k 数据键 245 | * @param clazz 分页数据类型 246 | * @param pageNo 页码 247 | * @return 分页助手 248 | */ 249 | public RedisPageHelper getPageForList(RedisTemplate template, K k, Class clazz, long pageNo){ 250 | ListOperations opsForList = template.opsForList(); 251 | long totalElements = opsForList.size(k); 252 | RedisPageHelper helper = this.preparePageHelper(totalElements, pageNo, clazz, 0l, 0l); 253 | if (helper == null){ 254 | return null; 255 | } 256 | List data = opsForList.range(k, this.getStartIdxForPage(pageNo), this.getEndIdxForPage(pageNo, totalElements)); 257 | helper.setData(data); 258 | return helper; 259 | } 260 | 261 | /** 262 | * 分页获取ZSet内的数据 263 | * @param template Redis操作模板 264 | * @param k 数据键 265 | * @param clazz 分页数据类型 266 | * @param pageNo 页码 267 | * @param isRevers true:正序,false:反序 268 | * @return 分页助手 269 | */ 270 | public RedisPageHelper getPageForZSet(RedisTemplate template, K k, Class clazz, long pageNo, boolean isRevers){ 271 | ZSetOperations opsForZSet = template.opsForZSet(); 272 | long totalElements = opsForZSet.size(k); 273 | RedisPageHelper helper = this.preparePageHelper(totalElements, pageNo, clazz, 0l, 0l); 274 | if (helper == null){ 275 | return null; 276 | } 277 | Set retVal = null; 278 | if(isRevers){ 279 | retVal = opsForZSet.range(k, this.getStartIdxForPage(pageNo), this.getEndIdxForPage(pageNo, totalElements)); 280 | } else { 281 | retVal = opsForZSet.reverseRange(k, this.getStartIdxForPage(pageNo), this.getEndIdxForPage(pageNo, totalElements)); 282 | } 283 | List data = new LinkedList(); 284 | data.addAll(retVal); 285 | helper.setData(data); 286 | return helper; 287 | } 288 | 289 | /** 290 | * 分页获取按ZSet的Score排序好的内部数据 291 | * @param template Redis操作模板 292 | * @param k 数据键 293 | * @param clazz 分页数据类型 294 | * @param pageNo 页码 295 | * @return 分页助手 296 | */ 297 | public RedisPageHelper getPageForZSetByScore(RedisTemplate template, K k, Class clazz, long pageNo, long minScore, long maxScore){ 298 | ZSetOperations opsForZSet = template.opsForZSet(); 299 | long totalElements = opsForZSet.size(k); 300 | RedisPageHelper helper = this.preparePageHelper(totalElements, pageNo, clazz, minScore, maxScore); 301 | if (helper == null){ 302 | return null; 303 | } 304 | Set retVal = opsForZSet.rangeByScore(k, helper.getMinScore(), helper.getMaxScore()); 305 | List data = new LinkedList(); 306 | data.addAll(retVal); 307 | helper.setData(data); 308 | return helper; 309 | } 310 | 311 | } 312 | --------------------------------------------------------------------------------