自定义查询接口----CustomBookDao
实现类:CustomBookDaoImpl
1、自定义添加hash对象的方法
测试:自定义查询价格高于某个点的Book对象
结果:
数据:价格有100, 200 ,300这三个
结果正确,因为自定义的查询,是价格 > , 不是 >= 。
如图:因为bookDao有继承这个 QueryByExampleExecutor 接口,所以可以进行样本查询
package cn.ljh.app.domain;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;
import java.util.concurrent.TimeUnit;
//通过@RedisHash注解存储实体Book到redis,就是该Book对象将存储为books(key)对应的hash对象(value) ---> key(books) field value
//相当于 @Entity 实体类映射到数据库,这里的RedisHash就是把这个book存到redis中
@RedisHash("books")
@Data
public class Book
{
@Id
private Integer id; //即使这里类型是Integer,可是存到redis后,也会变成String类型
//Indexed 指定对普通类型的属性建立索引,索引化后的属性才可用于查询。
@Indexed
private String name;
@Indexed
private String author;
private double price;
//该注解修饰一个数值类型的属性,用于指定该对象的过期时长。
@TimeToLive(unit = TimeUnit.MINUTES)
private long ttl;
public Book()
{
}
public Book(Integer id, String name, double price, String author)
{
this.id = id;
this.name = name;
this.price = price;
this.author = author;
}
public Book(Integer id, String name, double price, String author, long ttl)
{
this.id = id;
this.name = name;
this.price = price;
this.author = author;
this.ttl = ttl;
}
}
package cn.ljh.app.dao;
import cn.ljh.app.domain.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import java.util.List;
//DAO接口只需继承CrudRepository,Spring Data Redis 就能为DAO组件提供实现类。
//参数1:要操作的实体的类型 参数2:Id类型
//继承这个 QueryByExampleExecutor 接口,才可以进行样本查询
public interface BookDao extends CrudRepository<Book, Integer>, QueryByExampleExecutor,CustomBookDao
{
//方法名关键字查询
//根据 name 查询Book对象
//Booke实体类中的 name 属性有添加注解 @Indexed,所以可以对这个属性作为key进行查询
List<Book> findByName(String name);
//根据 author 查询Book对象
//这个author在实体类中也有添加 @Indexed 注解
List<Book> findByAuthor(String author);
}
package cn.ljh.app.dao;
import cn.ljh.app.domain.Book;
import java.util.List;
import java.util.Map;
//自定义查询
public interface CustomBookDao
{
//自定义添加hash对象的方法
void hmset(String key, Map<String,String> value);
//自定义查询价格高于某个点的Book对象
List<Book> findByPriceGt(double startPrice);
}
package cn.ljh.app.dao.impl;
import cn.ljh.app.dao.CustomBookDao;
import cn.ljh.app.domain.Book;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
//自定义查询接口实现类
public class CustomBookDaoImpl implements CustomBookDao
{
//借助于自动配置的 StringRedisTemplate 来实现数据库的访问
private final StringRedisTemplate stringRedisTemplate;
//有参构造器进行依赖注入
public CustomBookDaoImpl(StringRedisTemplate stringRedisTemplate)
{
this.stringRedisTemplate = stringRedisTemplate;
}
//自定义添加hash对象的方法
@Override
public void hmset(String key, Map<String, String> value)
{
//调用 HashOperations 的方法来向数据库中存入 Hash 对象
//要操作的value的类型是hash对象,就用.opsForHash() 方法
stringRedisTemplate.opsForHash().putAll(key, value);
}
//自定义查询价格高于某个点的Book对象
@Override
public List<Book> findByPriceGt(double startPrice)
{
//由于 price 没有用注解@Indexed 建立索引,因此不能直接根据price来查询
//首先获取全部的Book对象,redis会自动把我们添加的所有Book对象中的id,都存在一个set集合里面,因此先把id从set集合中都拿出来
//要操作的value的类型是 set 集合,就用.opsForSet() 方法,此处的 books 就是 Book对象上 @RedisHash("books") 所指定的key
Set<String> idSet = this.stringRedisTemplate.opsForSet().members("books");
//把这个 HashOperations 先提取出来
HashOperations<String, String, String> hashOps = this.stringRedisTemplate.opsForHash();
//返回接收Book对象的集合
List<Book> bookList = new ArrayList<>();
//2、遍历id集合,根据id获取所有的实体对象Book
for (String id : idSet)
{
//key 为 "books:" 对应的hash对象,就保存着一个个的持久化对象的全部信息。
String objkey = "books:" + id;
//3、判断key是否存在,因为key即使过期了,也还记录者,该objkey对应的value还存在,那么说明该实体还存在
if (this.stringRedisTemplate.hasKey(objkey))
{
//获取 "books:"+id 的key所对应的 Hash 对象中的price属性--就是Book对象的price属性
double price = Double.parseDouble(hashOps.get(objkey, "price"));
//4、判断价格,符合条件的再添加到list集合中
if (price > startPrice)
{
bookList.add(new Book(
Integer.parseInt(hashOps.get(objkey, "id")),
hashOps.get(objkey, "name"),
price,
hashOps.get(objkey, "author")));
}
}
}
return bookList;
}
}
//=========================================样本查询=========================================
@Autowired
private BookDao bookDao;
//==========================自定义查询================================
//自定义添加hash对象的方法
@Test
public void testHmset()
{
bookDao.hmset("test", Map.of("k1", "value1", "k2", "value2"));
}
//自定义查询价格高于某个点的Book对象
@ParameterizedTest
@ValueSource(doubles = {100, 200, 300})
public void testFindByPriceGt(double startPrice)
{
List<Book> books = bookDao.findByPriceGt(startPrice);
books.forEach(System.out::println);
}
//=========================================样本查询=========================================
//样本中只有 name 作为参数来查询
@ParameterizedTest
@ValueSource(strings = {"七龙珠", "火影忍者"})
public void testFindByExampleOnlyName(String name)
{
//创建一个样本对象
Example ex = Example.of(
new Book(null, name, 0, null),
ExampleMatcher.matching()
//忽略样本对象中的所有null值,即null值不作为样本的比较属性
.withIgnoreNullValues()
//因为查询的参数只有name,所以price不参与比较,用这个方法把这个price忽略掉
//因为price的默认值是0,不是null,需要额外用这个方法
.withIgnorePaths("price"));
//查询所有对象,把样本作为条件参数传进去比较查询
Iterable books = bookDao.findAll(ex);
books.forEach(System.err::println);
}
//样本中只有 author 作为参数来查询
@ParameterizedTest
@ValueSource(strings = "鸟山明")
public void testFindByExampleOnlyAuthor(String author)
{
//先创建一个样本
Example<Book> bookExample = Example.of(new Book(null, null, 0, author),
ExampleMatcher.matching().withIgnoreNullValues().withIgnorePaths("price")
);
//查询所有对象,把样本作为条件参数传进去比较查询
bookDao.findAll(bookExample).forEach(System.err::println);
}
# 配置连接redis服务器的相关信息
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
# 选择连接redis默认16个数据库中的第11个数据库
spring.redis.database=11
# 连接redis超时的时间--30秒
spring.redis.connect-timeout=30s
# 配置连接池的相关信息
# 配置这个连接池最大的连接数量
spring.redis.lettuce.pool.max-active=10
# 配置最大的能有多少个活动的、空闲的连接数量 idle-空闲
spring.redis.jedis.pool.max-idle=10
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<groupId>cn.ljh</groupId>
<artifactId>redis_boot</artifactId>
<version>1.0.0</version>
<name>redis_boot</name>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 配置连接池需要的依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>