Spring Data Redis 教程

Hash映射:ObjectHashMapper

我们可以使用 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映射器(Hash Mappers)

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;
    }

}

配置 RedisTemplate

使用上面自定义的 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() 单元测试用例自行测试。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号