一、添加依赖
<dependency>
<groupId>com.redis.om</groupId>
<artifactId>redis-om-spring</artifactId>
<version>0.9.0</version>
</dependency>
说明:因为v0.9.1
之后集成了Spring AI相关的,所以在Spring AIv1.0.0
版后才处理,所以先用v0.9.0
二、配置 Jedis连接工厂
import cn.hutool.core.util.StrUtil;
import com.redis.om.spring.annotations.EnableRedisDocumentRepositories;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
@EnableRedisDocumentRepositories(basePackages = "com.lyq.edu.*")
@Configuration
public class JedisConfig {
@Resource
private Environment env;
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
String host = env.getProperty("spring.data.redis.host", "localhost");
int port = env.getProperty("spring.data.redis.port", Integer.class, 6379);
String password = env.getProperty("spring.data.redis.password");
RedisStandaloneConfiguration conf = new RedisStandaloneConfiguration(host, port);
if (StrUtil.isNotBlank(password)) {
conf.setPassword(password);
}
final JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setTestWhileIdle(false);
poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000));
poolConfig.setNumTestsPerEvictionRun(-1);
final int timeout = 10000;
final JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofMillis(timeout)).readTimeout(Duration.ofMillis(timeout)).usePooling()
.poolConfig(poolConfig).build();
return new JedisConnectionFactory(conf, jedisClientConfiguration);
}
}
说明:
1. 创建Jedis连接工厂
2. 开启RedisDocument注解扫描
三、创建实体对象
import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Searchable;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.index.Indexed;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
1. <p>
2. 课程信息
3. </p>
4. 5. @author LYQ
6. @since 2024-08-07
*/
@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@Accessors(chain = true)
@Document
public class RSCourse implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@Id
private Long id;
/**
* 讲师ID
*/
@NotNull
@Indexed
private Long lecturerId;
/**
* 分类ID
*/
private Long categoryId;
/**
* 课程名称
*/
@NonNull
@Searchable
@Indexed
private String courseName;
/**
* 课程封面
*/
private String courseLogo;
/**
* 课程简介
*/
@Searchable
private String introduce;
/**
* 划线价
*/
private BigDecimal linePrice;
/**
* 课程价格
*/
private BigDecimal price;
/**
* 课程排序(前端显示使用)
*/
private Integer courseSort;
/**
* 购买人数
*/
private Integer countBuy;
/**
* 学习人数
*/
private Integer countStudy;
/**
* 倍速播放(0关闭,1开启)
*/
private Integer speedDouble;
/**
* 拖拽播放(0关闭,1开启)
*/
private Integer speedDrag;
/**
* 发布状态(0:未发布,1:已发布)
*/
private Integer publishStatus;
/**
* 状态(1:正常,0:禁用)
*/
private Integer statusId;
/**
* 排序
*/
private Integer sort;
/**
* 创建时间
*/
private LocalDateTime gmtCreate;
/**
* 修改时间
*/
private LocalDateTime gmtModified;
}
说明:
@Document
:将该类标记为 JSON 文档@Id
:标识该字段为主键ID@Indexed
:标识对应字段为索引@Searchable
:表示用于搜索的字段
四、创建持久层
import com.redis.om.spring.repository.RedisDocumentRepository;
import com.lyq.edu.common.search.document.RSCourse;
public interface RSCourseRepository extends RedisDocumentRepository<RSCourse, Long> {
}
五、正删改查示例
import cn.hutool.json.JSONUtil;
import com.lyq.edu.common.core.util.BeanUtil;
import com.lyq.edu.common.search.document.RSCourse;
import com.lyq.edu.common.search.repository.RSCourseRepository;
import com.lyq.edu.resource.dao.CourseDao;
import com.lyq.edu.resource.dao.impl.mapper.entity.Course;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.*;
import java.util.List;
@SpringBootTest
public class CourseTest {
@Autowired
private RSCourseRepository repository;
@Autowired
private CourseDao courseDao;
@Test
public void list() {
List<RSCourse> all = repository.findAll();
System.out.println(JSONUtil.toJsonStr(all));
}
@Test
public void save() {
List<Course> courseList = courseDao.list();
List<RSCourse> rsCourses = BeanUtil.copyProperties(courseList, RSCourse.class);
List<RSCourse> rsList = repository.saveAll(rsCourses);
System.out.println(JSONUtil.toJsonStr(rsList));
}
@Test
public void edit() {
RSCourse rsCourse = repository.findById(1579349124530593794L).get();
rsCourse.setCourseName(rsCourse.getCourseName() + "1");
RSCourse update = repository.update(rsCourse);
System.out.println(JSONUtil.toJsonStr(update));
}
@Test
public void delete() {
repository.deleteById(1579349124530593794L);
}
@Test
public void query() {
// StringMatcher 的几种匹配方式及其含义:
// EXACT: 精确匹配,即完全相等。
// STARTING: 以指定的字符串开头。
// ENDING: 以指定的字符串结尾。
// CONTAINING: 包含指定的字符串,可以在任意位置。
// NOT_CONTAINING: 不包含指定的字符串。
RSCourse rsCourse = new RSCourse();
rsCourse.setCourseName("程序员");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase() // 忽略大小写
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) // 使用 CONTAINING 匹配方式
.withMatcher("courseName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()); // 指定 name 属性进行模糊匹配
Example<RSCourse> example = Example.of(rsCourse, matcher);
Iterable<RSCourse> resultList = repository.findAll(example);
System.out.println(JSONUtil.toJsonStr(resultList));
}
@Test
public void page() {
RSCourse rsCourse = new RSCourse();
rsCourse.setCourseName("程序员");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase() // 忽略大小写
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) // 使用 CONTAINING 匹配方式
.withMatcher("courseName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()); // 指定 name 属性进行模糊匹配
Pageable pageable = PageRequest.of(0, 1);
Example<RSCourse> example = Example.of(rsCourse, matcher);
Page<RSCourse> page = repository.findBy(example, (query) -> query
.sortBy(Sort.by("id").ascending())
.page(pageable)
);
System.out.println(JSONUtil.toJsonStr(page));
}
}
说明:
未有查询条件进行分页查询时,使用Page<T> findAll(Pageable pageable);
可以获取到对应结果。
但是如果带有查询条件时使用<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
方法查询时获取不到对应结果,只能使用<S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
进行查询
ps:看后面官方迭代升级会不会解决
六、遇到问题
和spring-boot-starter-test
内置json包冲突
错误问题:
解决方法:
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>