我们可以使用 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() 单元测试用例自行测试。