我们可以使用 Redis 中的各种数据结构(List、Set、ZSet、Hash等)来存储数据。Jackson2JsonRedisSerializer 序列化器可以将 Java 对象转换为 JSON 格式的字符串。理想情况下,可以使用普通键(key)将 JSON 存储为该键的值。你还可以通过使用 Redis 的 Hash 映射来实现更复杂的结构化对象映射,Spring Data Redis 提供了将 Java 对象映射到 Hash 的各种策略,如下:
(1)通过使用 HashOperations 和序列化程序(如:Jackson2JsonRedisSerializer 将 Java 对象序列化成 JSON 字符串)进行直接映射
(2)使用 Redis 存储库,存储库可以无缝地转换和存储 Redis Hash 中的域对象,应用定制映射策略,并使用二级索引。
(3)使用 HashMapper 和 HashOperations 工具类
Hash映射器可以在 Java 对象与 Map<K,V> 之间进行转换,HashMapper 旨在与 Redis Hash 一起使用。有多种实现方案可供选择:
(1)使用 Spring 的 BeanUtils 的 BeanUtilsHashMapper。
(2)使用 Object 对象到 Hash 映射的工具类 ObjectHashMapper。
(3)使用 FasterXML Jackson 的 Jackson2HashMapper。
通过一个示例简单演示怎样通过 ObjectHashMapper 将一个 Java 对象映射成 Map<byte[],byte[]>,并且将它存储到 Redis。或者从 Redis 中获取该值,并将 Map<byte[],byte[]> 反映射成一个 Java 对象,
由于 ObjectHashMapper 映射成的 Map<byte[],byte[]> 是 byte[] 类型,如果我们要将 byte[] 保存到 Redis,需要自定义一个序列化器。下面自定义了一个 byte[] 序列化器,代码如下:
import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; /** * 自定义 byte[] 数组序列化器 * @author hxstrive.com 2022/10/9 */ public class ByteArrayRedisSerializer implements RedisSerializer<byte[]> { @Override public byte[] serialize(byte[] bytes) throws SerializationException { return bytes; } @Override public byte[] deserialize(byte[] bytes) throws SerializationException { return bytes; } }
使用上面自定义的 ByteArrayRedisSerializer 序列化器创建 RedisTemplate 对象,代码如下:
import com.hxstrive.redis.custom.ByteArrayRedisSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfigHashMapper { @Bean public RedisTemplate<String,byte[]> hashMapperRedisTemplate(RedisConnectionFactory factory) { RedisTemplate<String,byte[]> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); // 设置键序列化方式 redisTemplate.setKeySerializer(new StringRedisSerializer()); // 设置简单类型值的序列化方式 redisTemplate.setValueSerializer(new ByteArrayRedisSerializer()); // 设置 hash 类型键的序列化方式 redisTemplate.setHashKeySerializer(new ByteArrayRedisSerializer()); // 设置 hash 类型值的序列化方式 redisTemplate.setHashValueSerializer(new ByteArrayRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
// Order.java import java.util.ArrayList; import java.util.List; /** * 订单实体 * @author hxstrive.com 2022/10/9 */ public class Order { /** 订单编号 */ private String orderNumber; /** 订单总金额 */ private double amount; /** 订单所属用户 */ private User user; /** 订单商品列表 */ private List<Goods> goodsList; public Order() { this.orderNumber = ""; this.amount = 0.0D; this.user = null; this.goodsList = new ArrayList<>(); } /** * 添加商品到订单 * @param goods */ public void addGoods(Goods goods) { this.goodsList.add(goods); this.amount += goods.getPrice(); } //... } // User.java /** * 用户实体 * @author hxstrive.com 2022/7/5 */ public class User { /** 用户ID */ private int id; /** 用户名 */ private String name; public User() {} public User(int id, String name) { this.id = id; this.name = name; } //... } // Goods.java /** * 商品实体 * @author hxstrive.com 2022/10/9 */ public class Goods { /** 商品编号 */ private String number; /** 商品名称 */ private String name; /** 商品价格 */ private double price; /** 商品数量 */ private int quantity; //... }
使用 @RunWith(SpringRunner.class) 和 @SpringBootTest 注解创建一个测试类,代码如下:
import com.alibaba.fastjson.JSONObject; import com.hxstrive.redis.entity.Goods; import com.hxstrive.redis.entity.Order; import com.hxstrive.redis.entity.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.hash.HashMapper; import org.springframework.data.redis.hash.ObjectHashMapper; import org.springframework.test.context.junit4.SpringRunner; import java.util.Map; /** * 测试验证 HashMapper 的用法 * @author hxstrive.com 2022/10/9 */ @RunWith(SpringRunner.class) @SpringBootTest public class HashMapperTest { @Autowired @Qualifier("hashMapperRedisTemplate") private RedisTemplate<String,byte[]> redisTemplate; private HashMapper<Object, byte[], byte[]> hashMapper = new ObjectHashMapper(); private Order getOrder() { Order order = new Order(); order.setOrderNumber("BH20221009125434"); User user = new User(); user.setId(100); user.setName("张三"); order.setUser(user); Goods goodsJava = new Goods(); goodsJava.setNumber("9787111213826"); goodsJava.setName("《Java编程思想》"); goodsJava.setQuantity(2); goodsJava.setPrice(54.0D); order.addGoods(goodsJava); Goods goodsPhp = new Goods(); goodsPhp.setNumber("9787302596462"); goodsPhp.setName("《PHP从入门到精通》(第6版)"); goodsPhp.setQuantity(1); goodsPhp.setPrice(90.70D); order.addGoods(goodsPhp); return order; } @Test public void toHash2fromHash() { Order order = getOrder(); System.out.println("将对象转换成Hash:"); Map<byte[],byte[]> map = hashMapper.toHash(order); for(Map.Entry<byte[],byte[]> entry : map.entrySet()) { String key = new String(entry.getKey()); String value = new String(entry.getValue()); System.out.println(key + "=" + value); } System.out.println("\n将Hash转换成对象:"); Order newOrder = (Order)hashMapper.fromHash(map); System.out.println(JSONObject.toJSONString(newOrder, true)); } @Test public void save() { Order order = getOrder(); Map<byte[],byte[]> map = hashMapper.toHash(order); for(Map.Entry<byte[],byte[]> entry : map.entrySet()) { String key = new String(entry.getKey()); String value = new String(entry.getValue()); System.out.println(key + "=" + value); } HashOperations<String,byte[],byte[]> ops = redisTemplate.opsForHash(); ops.putAll("order:" + order.getOrderNumber(), map); } @Test public void find() { Order order = getOrder(); HashOperations<String,byte[],byte[]> ops = redisTemplate.opsForHash(); Map<byte[],byte[]> map = ops.entries("order:" + order.getOrderNumber()); Order obj = (Order)hashMapper.fromHash(map); if(null != obj) { System.out.println(JSONObject.toJSONString(obj, true)); } } }
运行上面的 toHash2fromHash 单元测试用例,输出如下:
将对象转换成Hash: _class=com.hxstrive.redis.entity.Order orderNumber=BH20221009125434 amount=144.7 user.id=100 user.name=张三 goodsList.[0].number=9787111213826 goodsList.[0].name=《Java编程思想》 goodsList.[0].price=54.0 goodsList.[0].quantity=2 goodsList.[1].number=9787302596462 goodsList.[1].name=《PHP从入门到精通》(第6版) goodsList.[1].price=90.7 goodsList.[1].quantity=1 将Hash转换成对象: { "amount":144.7, "goodsList":[ { "name":"《Java编程思想》", "number":"9787111213826", "price":54.0, "quantity":2 }, { "name":"《PHP从入门到精通》(第6版)", "number":"9787302596462", "price":90.7, "quantity":1 } ], "orderNumber":"BH20221009125434", "user":{ "id":100, "name":"张三" } }
其中,save() 和 find() 单元测试用例自行测试。