简介

Spring Data Solr就是为了方便Solr的开发所研制的一个 框架,其底层是对SolrJ(官方 API)的封装。SolrJ的使用是通过它提供的若干接口,而Spring Data Solr 是Spring调用Solr接口的 进一步封装,简化了Solr的使用,可以通过Spring Data Solr提供的对象HttpSolrServer对文档索引的创建、搜索、分组、排序、分页等等进行操控。功能还是较为完善的。
Spring Data Solr使得 SolrJ用起来更简单, 可以把JavaBean通过注解的方式直接从Solr查询出来和写入.就和ORM框架做的工作很相似。这两个包有个版本对应关系,使用的时候要注意:

spring-data-solr | solr-solrj ————————-+—————- 2.1.15 | 5.5.0 3.0.10 | 6.5.0 4.0.0 | 7.4.0 4.2.3 | 8.5.2

Spring Data Solr就是把Solr的应用更好的集成到Spring框架中,它的使用方法就是使用SolrTemplate这个类来对索引库进行CRUD的操作,实际上是由SolrOperations这个接口里面的方法来操作的。

工程应用

方式1:SpringBoot + Spring Data Solr

1. 添加pom.xml依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-solr</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-web</artifactId>
  8. </dependency>

2. Solr配置

■ 通过Solr URL连接

  • application.yml

    1. spring:
    2. application:
    3. name: solr-springdata-example01
    4. data:
    5. solr:
    6. host: http://bigdata-node1:8983/solr/db_sync
    7. server:
    8. port: 8081
  • application.properties

    1. spring.application.name=solr-springdata-example01
    2. server.port=8081
    3. # solr Configuration
    4. spring.data.solr.host=http://bigdata-node1:8983/solr/db_sync

    ■ 通过ZooKeeper连接

  • application.yml

    1. spring:
    2. application:
    3. name: solr-springdata-example01
    4. data:
    5. solr:
    6. zk-host: 192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181
    7. server:
    8. port: 8081
  • application.properties

    1. spring.application.name=solr-springdata-example01
    2. server.port=8083
    3. # solr Configuration
    4. spring.data.solr.zk-host=192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181
    5. spring.data.solr.core=db_sync

    3. 编写关键类

    ■ 配置类(SolrConfig.java) ```java import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.common.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.solr.core.SolrTemplate; import java.util.Arrays; import java.util.Optional;

/**

  • @Description 自定义配置类(SolrTemplate) */ @Configuration public class SolrConfig {

    @Value(“${spring.data.solr.zk-host}”) private String zk_host;

    @Value(“${spring.data.solr.core}”) private String solrCore;

    /**

    • 配置SolrTemplate */ @Bean public SolrTemplate solrTemplate() { if (StringUtils.isEmpty(zk_host)) {
      1. return null;
      } CloudSolrClient client = new CloudSolrClient.Builder(
      1. Arrays.asList(zk_host.split(",")),
      2. Optional.empty()
      ).build(); // 设置默认的操作实例 client.setDefaultCollection(solrCore); client.setZkClientTimeout(5000); client.setZkConnectTimeout(5000); SolrTemplate template = new SolrTemplate(client); return template; }

}

  1. **■ 实体类(Article.java)**
  2. ```java
  3. import lombok.AllArgsConstructor;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. import org.apache.solr.client.solrj.beans.Field;
  7. import org.springframework.data.solr.core.mapping.SolrDocument;
  8. import java.io.Serializable;
  9. /**
  10. * Description VO-文章类
  11. */
  12. @Data
  13. @AllArgsConstructor
  14. @NoArgsConstructor
  15. @SolrDocument(solrCoreName = "db_sync")
  16. public class Article implements Serializable {
  17. @Field("id")
  18. private Integer id;
  19. @Field("content_id")
  20. private Long contentId;
  21. @Field("title")
  22. private String title;
  23. @Field("content")
  24. private String content;
  25. @Field("type")
  26. private Integer type;
  27. @Field("create_at")
  28. private Long createAt;
  29. @Field("publish_at")
  30. private Long publishAt;
  31. @Override
  32. public String toString() {
  33. return "Article{" +
  34. "id=" + id +
  35. ", contentId=" + contentId +
  36. ", title='" + title + '\'' +
  37. ", content='" + content + '\'' +
  38. ", type=" + type +
  39. ", createAt=" + createAt +
  40. ", publishAt=" + publishAt +
  41. '}';
  42. }
  43. }

注解说明:spring.application.name=solr-springdata-example01server.port=8083# solr Configurationspring.data.solr.zk-host=192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181spring.data.solr.core=db_sync

  • @Field:注解标识 。

如果属性与配置文件定义的域名称不一致,需要在注解中指定域名称。

  • @Dynamic:声明这是一个动态域。

在代码层,将数据库中某一字段中文字解析成map对象,将给对象封装到实体类中即可完成动态域的配置。

  1. // 示例
  2. // 将spec字段中的json字符串转换为map,字段内容{"内存":"64G","网络":"4G"}
  3. Map specMap = JSON.parseObject(item.getSpec());
  4. // 给带注解的字段赋值
  5. item.setSpecMap(specMap);
  • @SolrDocument:指定集合的名称。

image.png
■ 服务接口(SolrTemplateService.java)

  1. import org.springframework.data.solr.core.query.Criteria;
  2. import org.springframework.data.solr.core.query.SimpleFilterQuery;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.util.List;
  5. import java.util.Map;
  6. public interface SolrTemplateService {
  7. /**
  8. * 将JavaBean对象添加(新增或修改)到Solr中
  9. *
  10. * @param bean VO/DTO/Pojo/Domain对象
  11. * @param <T> 泛型(对象类型)
  12. * @return
  13. */
  14. <T> boolean saveOrUpdate(T bean);
  15. /**
  16. * 将Map对象添加(新增或修改)到Solr中
  17. *
  18. * @param map Map对象
  19. * @param clz JavaBean的Class对象
  20. * @param <T> 泛型(对象类型)
  21. * @return
  22. */
  23. <T> boolean saveOrUpdate(Map map, Class<T> clz);
  24. /**
  25. * 通过文档ID删除Solr中的文档
  26. *
  27. * @param id 索引ID
  28. * @return
  29. */
  30. boolean deleteById(String id);
  31. /**
  32. * 通过查询删除Solr中对应的数据集合
  33. *
  34. * @param query 查询条件
  35. */
  36. boolean deleteByQuery(String query);
  37. /**
  38. * 批量新增或更新记录
  39. *
  40. * @param entities 新增或更新对象列表
  41. * @param <T> 泛型
  42. * @return
  43. */
  44. <T> boolean batchSaveOrUpdate(List<T> entities);
  45. /**
  46. * 分页查询
  47. *
  48. * @param clz 泛型类对应java.lang.Class
  49. * @param offset 开始索引(默认:0)
  50. * @param rows 每页显示记录数(默认:10)
  51. * @return
  52. */
  53. <T> SolrResultInfo<T> queryForPage(Class<T> clz, Long offset, Integer rows);
  54. /**
  55. * 高亮查询
  56. *
  57. * @param clz 泛型类对应java.lang.Class
  58. * @param keywords 关键字
  59. * @param hlFieldsList 高亮字段列表
  60. * @param <T> 泛型
  61. * @return
  62. */
  63. <T> SolrResultInfo<T> queryHeightLight(Class<T> clz, String keywords, List<String> hlFieldsList) throws InvocationTargetException, IllegalAccessException;
  64. /**
  65. * 通过泛型获取Solr中的对象集合
  66. *
  67. * @param clz 泛型类对应java.lang.Class
  68. * @param criteria 查询条件
  69. * @param hlFieldsList 高亮显示数据域名称,是List<String>集合
  70. * @param fqList 过滤条件
  71. * @param offset 开始索引(默认:0)
  72. * @param rows 每页显示记录数(默认:10)
  73. * @return
  74. */
  75. <T> SolrResultInfo<T> query(Class<T> clz, Criteria criteria, List<String> hlFieldsList, List<SimpleFilterQuery> fqList, Long offset, Integer rows) throws InvocationTargetException, IllegalAccessException;
  76. /**
  77. * 根据ID获取记录
  78. *
  79. * @param id 索引ID
  80. * @param clz Class对象
  81. * @param <T> 泛型
  82. * @return
  83. */
  84. <T> T getById(String id, Class<T> clz);
  85. }

■ 服务接口实现类(SolrTemplateServiceImpl.java)

  1. import com.lonton.bigdata.service.SolrResultInfo;
  2. import com.lonton.bigdata.service.SolrTemplateService;
  3. import com.lonton.bigdata.util.CommonUtils;
  4. import org.apache.commons.beanutils.BeanUtils;
  5. import org.apache.solr.client.solrj.response.UpdateResponse;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.data.domain.Page;
  11. import org.springframework.data.solr.core.SolrTemplate;
  12. import org.springframework.data.solr.core.query.*;
  13. import org.springframework.data.solr.core.query.result.HighlightEntry;
  14. import org.springframework.data.solr.core.query.result.HighlightPage;
  15. import org.springframework.data.solr.core.query.result.ScoredPage;
  16. import org.springframework.stereotype.Service;
  17. import org.springframework.transaction.annotation.Transactional;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Optional;
  22. @Service
  23. public class SolrTemplateServiceImpl implements SolrTemplateService {
  24. private static final Logger log = LoggerFactory.getLogger(SolrTemplateServiceImpl.class);
  25. @Value("${spring.data.solr.core}")
  26. private String solrCore;
  27. @Autowired
  28. private SolrTemplate solrTemplate;
  29. /**
  30. * 将JavaBean对象添加(新增或修改)到Solr中
  31. *
  32. * @param bean VO/DTO/Pojo/Domain对象
  33. * @param <T> 泛型(对象类型)
  34. * @return
  35. */
  36. @Override
  37. @Transactional(rollbackFor = Exception.class)
  38. public <T> boolean saveOrUpdate(T bean) {
  39. try {
  40. // 将对象存储到索引库中
  41. UpdateResponse resp = solrTemplate.saveBean(solrCore, bean);
  42. // 提交
  43. solrTemplate.commit(solrCore);
  44. if (resp.getStatus() == 0) {
  45. log.debug("saveOrUpdate successfully!");
  46. return true;
  47. }
  48. } catch (Exception e) {
  49. log.error(e.getMessage());
  50. e.printStackTrace();
  51. }
  52. log.debug("saveOrUpdate failure!");
  53. return false;
  54. }
  55. /**
  56. * 将Map对象添加(新增或修改)到Solr中
  57. *
  58. * @param map Map对象
  59. * @param clz JavaBean的Class对象
  60. * @param <T> 泛型(对象类型)
  61. * @return
  62. */
  63. @Override
  64. @Transactional(rollbackFor = Exception.class)
  65. public <T> boolean saveOrUpdate(Map map, Class<T> clz) {
  66. try {
  67. T bean = CommonUtils.mapToBean((Map<String, ?>) map, clz);
  68. // 将对象存储到索引库中
  69. UpdateResponse resp = solrTemplate.saveBean(solrCore, bean);
  70. // 提交
  71. solrTemplate.commit(solrCore);
  72. if (resp.getStatus() == 0) {
  73. log.debug("saveOrUpdate successfully!");
  74. return true;
  75. }
  76. } catch (Exception e) {
  77. log.error(e.getMessage());
  78. e.printStackTrace();
  79. }
  80. log.debug("saveOrUpdate failure!");
  81. return false;
  82. }
  83. /**
  84. * 通过文档ID删除Solr中的文档
  85. *
  86. * @param id 索引ID
  87. * @return
  88. */
  89. @Override
  90. @Transactional(rollbackFor = Exception.class)
  91. public boolean deleteById(String id) {
  92. try {
  93. UpdateResponse resp = solrTemplate.deleteByIds(solrCore, id);
  94. solrTemplate.commit(solrCore);
  95. if (resp.getStatus() == 0) {
  96. log.debug("delete successfully!");
  97. return true;
  98. }
  99. } catch (Exception e) {
  100. log.error(e.getMessage());
  101. e.printStackTrace();
  102. }
  103. log.debug("delete failure!");
  104. return false;
  105. }
  106. /**
  107. * 通过查询删除Solr中对应的数据集合
  108. *
  109. * @param query 查询条件
  110. */
  111. @Override
  112. @Transactional(rollbackFor = Exception.class)
  113. public boolean deleteByQuery(String query) {
  114. try {
  115. UpdateResponse resp = solrTemplate.delete(solrCore, new SimpleQuery(query));
  116. solrTemplate.commit(solrCore);
  117. if (resp.getStatus() == 0) {
  118. log.debug("delete successfully!");
  119. return true;
  120. }
  121. } catch (Exception e) {
  122. log.error(e.getMessage());
  123. e.printStackTrace();
  124. }
  125. log.debug("delete failure!");
  126. return false;
  127. }
  128. /**
  129. * 批量新增或更新记录
  130. *
  131. * @param entities 新增或更新对象列表
  132. * @param <T> 泛型
  133. * @return
  134. */
  135. @Override
  136. public <T> boolean batchSaveOrUpdate(List<T> entities) {
  137. try {
  138. // 将对象存储到索引库中
  139. UpdateResponse resp = solrTemplate.saveBeans(solrCore, entities);
  140. // 提交
  141. solrTemplate.commit(solrCore);
  142. if (resp.getStatus() == 0) {
  143. log.debug("batchSaveOrUpdate successfully!");
  144. return true;
  145. }
  146. } catch (Exception e) {
  147. log.error(e.getMessage());
  148. e.printStackTrace();
  149. }
  150. log.debug("batchSaveOrUpdate failure!");
  151. return false;
  152. }
  153. /**
  154. * 分页查询
  155. *
  156. * @param clz 泛型类对应java.lang.Class
  157. * @param offset 开始索引(默认:0)
  158. * @param rows 每页显示记录数(默认:10)
  159. * @return
  160. */
  161. @Override
  162. public <T> SolrResultInfo<T> queryForPage(Class<T> clz, Long offset, Integer rows) {
  163. // 定义返回自定义数据结构对象
  164. SolrResultInfo<T> resultInfo = new SolrResultInfo<T>();
  165. Query query = new SimpleQuery("*:*");
  166. query.setOffset(offset); // 开始索引(默认 0)
  167. query.setRows(rows); // 每页记录数(默认 10)
  168. ScoredPage<T> page = solrTemplate.queryForPage(solrCore, query, clz);
  169. resultInfo.setTotal(page.getTotalElements());
  170. resultInfo.setList(page.getContent());
  171. return resultInfo;
  172. }
  173. /**
  174. * 高亮查询
  175. *
  176. * @param clz 泛型类对应java.lang.Class
  177. * @param keywords 关键字
  178. * @param hlFieldsList 高亮字段列表
  179. * @param <T> 泛型
  180. * @return
  181. */
  182. @Override
  183. public <T> SolrResultInfo<T> queryHeightLight(Class<T> clz, String keywords, List<String> hlFieldsList) throws InvocationTargetException, IllegalAccessException {
  184. // 定义返回自定义数据结构对象
  185. SolrResultInfo<T> resultInfo = new SolrResultInfo<T>();
  186. // 封装高亮条件
  187. HighlightQuery q = new SimpleHighlightQuery();
  188. HighlightOptions highlightOptions = new HighlightOptions();
  189. // 高亮显示字段
  190. for (String s : hlFieldsList) {
  191. highlightOptions.addField(s);
  192. }
  193. // 高亮样式
  194. highlightOptions.setSimplePrefix("<em style='color: red'>");
  195. highlightOptions.setSimplePostfix("</em>");
  196. q.setHighlightOptions(highlightOptions);
  197. Criteria criteria = new Criteria("keywords").is(keywords);
  198. q.addCriteria(criteria);
  199. HighlightPage<T> pages = solrTemplate.queryForHighlightPage(solrCore, q, clz);
  200. // *********************** 设置高亮结果 ***********************
  201. for (HighlightEntry<T> h : pages.getHighlighted()) { // 循环高亮入口集合
  202. T t = h.getEntity(); // 获取原实体类
  203. if (h.getHighlights().size() > 0 && h.getHighlights().get(0).getSnipplets().size() > 0) {
  204. for (HighlightEntry.Highlight highlight : h.getHighlights()) {
  205. for (String value : highlight.getSnipplets()) {
  206. // 替换高亮部分的内容
  207. BeanUtils.setProperty(t, highlight.getField().getName(), value);
  208. }
  209. }
  210. }
  211. }
  212. resultInfo.setTotal(pages.getTotalElements());
  213. resultInfo.setList(pages.getContent());
  214. return resultInfo;
  215. }
  216. /**
  217. * 通过泛型获取Solr中的对象集合
  218. *
  219. * @param clz 泛型类对应java.lang.Class
  220. * @param criteria 查询条件
  221. * @param hlFieldsList 高亮显示数据域名称,是List<String>集合
  222. * @param fqList 过滤条件
  223. * @param offset 开始索引(默认:0)
  224. * @param rows 每页显示记录数(默认:10)
  225. * @return
  226. */
  227. @Override
  228. public <T> SolrResultInfo<T> query(Class<T> clz, Criteria criteria, List<String> hlFieldsList, List<SimpleFilterQuery> fqList, Long offset, Integer rows) throws InvocationTargetException, IllegalAccessException {
  229. // 定义返回自定义数据结构对象
  230. SolrResultInfo<T> resultInfo = new SolrResultInfo<>();
  231. // 封装高亮条件
  232. HighlightQuery q = new SimpleHighlightQuery();
  233. // 过滤条件
  234. for (SimpleFilterQuery simpleFilterQuery : fqList) {
  235. q.addFilterQuery(simpleFilterQuery);
  236. }
  237. // 分页查询
  238. q.setOffset(offset);
  239. q.setRows(rows);
  240. // 高亮显示字段
  241. HighlightOptions highlightOptions = new HighlightOptions();
  242. for (String s : hlFieldsList) {
  243. highlightOptions.addField(s);
  244. }
  245. // 高亮样式
  246. highlightOptions.setSimplePrefix("<em style='color: red'>");
  247. highlightOptions.setSimplePostfix("</em>");
  248. q.setHighlightOptions(highlightOptions);
  249. q.addCriteria(criteria);
  250. HighlightPage<T> pages = solrTemplate.queryForHighlightPage(solrCore, q, clz);
  251. // *********************** 设置高亮结果 ***********************
  252. for (HighlightEntry<T> h : pages.getHighlighted()) { // 循环高亮入口集合
  253. T t = h.getEntity(); // 获取原实体类
  254. if (h.getHighlights().size() > 0 && h.getHighlights().get(0).getSnipplets().size() > 0) {
  255. for (HighlightEntry.Highlight highlight : h.getHighlights()) {
  256. for (String value : highlight.getSnipplets()) {
  257. // 替换高亮部分的内容
  258. BeanUtils.setProperty(t, highlight.getField().getName(), value);
  259. }
  260. }
  261. }
  262. }
  263. resultInfo.setTotal(pages.getTotalElements());
  264. resultInfo.setList(pages.getContent());
  265. return resultInfo;
  266. }
  267. private <T> SolrResultInfo<T> queryNoHeightLight(Class<T> clz, String query, Criteria criteria, List<String> hlFieldsList, Long offset, Integer rows) {
  268. // 定义返回自定义数据结构对象
  269. SolrResultInfo<T> resultInfo = new SolrResultInfo<>();
  270. // 查询条件构造
  271. Query q = new SimpleQuery(query);
  272. // 分页查询
  273. q.setOffset(offset); // 开始索引(默认 0)
  274. q.setRows(rows); // 每页记录数(默认 10)
  275. // 条件查询
  276. q.addCriteria(criteria);
  277. Page<T> pages = solrTemplate.query(solrCore, q, clz);
  278. resultInfo.setTotal(pages.getTotalElements());
  279. resultInfo.setList(pages.getContent());
  280. return resultInfo;
  281. }
  282. /**
  283. * 根据ID获取记录
  284. *
  285. * @param id 索引ID
  286. * @param clz Class对象
  287. * @param <T> 泛型
  288. * @return
  289. */
  290. @Override
  291. public <T> T getById(String id, Class<T> clz) {
  292. Optional<T> bean = solrTemplate.getById(solrCore, id, clz);
  293. return bean.isPresent() ? bean.get() : null;
  294. }
  295. }

■ 返回对象封装类(SolrResultInfo.java)

  1. import java.util.List;
  2. /**
  3. * Description 查询Solr返回的对象,对象类型为T的集合,还包含Solr中符合条件记录总数
  4. */
  5. public class SolrResultInfo<T> {
  6. private List<T> list = null;
  7. private Long total = null;
  8. public List<T> getList() {
  9. return list;
  10. }
  11. public void setList(List<T> list) {
  12. this.list = list;
  13. }
  14. public Long getTotal() {
  15. return total;
  16. }
  17. public void setTotal(Long total) {
  18. this.total = total;
  19. }
  20. }

■ 工具类(CommonUtils.java)

  1. import com.google.common.collect.Lists;
  2. import com.google.common.collect.Maps;
  3. import org.apache.commons.beanutils.BeanUtils;
  4. import org.springframework.cglib.beans.BeanMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. /**
  8. * Description 工具类(Map与JavaBean互转)
  9. */
  10. public class CommonUtils {
  11. /**
  12. * 把一个Map转换成指定类型的JavaBean对象
  13. *
  14. * @param map Map对象
  15. * @param clazz Class对象
  16. * @param <T> 泛型
  17. * @return
  18. */
  19. public static <T> T mapToBean(Map<String, ?> map, Class<T> clazz) {
  20. try {
  21. T bean = clazz.newInstance();
  22. BeanUtils.populate(bean, map);
  23. return bean;
  24. } catch (Exception e) {
  25. throw new RuntimeException(e);
  26. }
  27. }
  28. /**
  29. * 将List<Map<String,Object>>转换为List<T>
  30. *
  31. * @param maps Map对象列表
  32. * @param clazz Class对象
  33. * @return
  34. * @throws InstantiationException
  35. * @throws IllegalAccessException
  36. */
  37. public static <T> List<T> mapsToBeans(List<Map<String, Object>> maps, Class<T> clazz)
  38. throws InstantiationException, IllegalAccessException {
  39. List<T> list = Lists.newArrayList();
  40. if (maps != null && maps.size() > 0) {
  41. Map<String, Object> map;
  42. T bean;
  43. for (int i = 0, size = maps.size(); i < size; i++) {
  44. map = maps.get(i);
  45. bean = clazz.newInstance();
  46. list.add((T) mapToBean(map, bean.getClass()));
  47. }
  48. }
  49. return list;
  50. }
  51. /**
  52. * 将对象装换为map
  53. *
  54. * @param bean
  55. * @return
  56. */
  57. public static <T> Map<String, Object> beanToMap(T bean) {
  58. Map<String, Object> map = Maps.newHashMap();
  59. if (bean != null) {
  60. BeanMap beanMap = BeanMap.create(bean);
  61. for (Object key : beanMap.keySet()) {
  62. map.put(key + "", beanMap.get(key));
  63. }
  64. }
  65. return map;
  66. }
  67. /**
  68. * 将List<T>转换为List<Map<String, Object>>
  69. *
  70. * @param beanList
  71. * @return
  72. */
  73. public static <T> List<Map<String, Object>> beansToMap(List<T> beanList) {
  74. List<Map<String, Object>> list = Lists.newArrayList();
  75. if (beanList != null && beanList.size() > 0) {
  76. Map<String, Object> map;
  77. T bean;
  78. for (int i = 0, size = beanList.size(); i < size; i++) {
  79. bean = beanList.get(i);
  80. map = beanToMap(bean);
  81. list.add(map);
  82. }
  83. }
  84. return list;
  85. }
  86. }

4. 测试

■ 测试类(SolrTemplateServiceTests.java)

  1. import com.lonton.bigdata.vo.Article;
  2. import org.junit.jupiter.api.Test;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.test.context.SpringBootTest;
  5. import org.springframework.data.solr.core.query.Criteria;
  6. import org.springframework.data.solr.core.query.SimpleFilterQuery;
  7. import org.springframework.util.Assert;
  8. import java.lang.reflect.InvocationTargetException;
  9. import java.util.ArrayList;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import java.util.Map;
  13. @SpringBootTest
  14. public class SolrTemplateServiceTests {
  15. @Autowired
  16. private SolrTemplateService service;
  17. @Test
  18. public void saveOrUpdate() {
  19. Article bean = new Article();
  20. bean.setId(1);
  21. bean.setContentId(99L);
  22. bean.setTitle("测试-SolrTemplateService.saveOrUpdate");
  23. bean.setContent("测试-作为一个中国人,我感到很自豪");
  24. bean.setType(1);
  25. bean.setCreateAt(1578912619999L);
  26. bean.setPublishAt(1578912619999L);
  27. assert service.saveOrUpdate(bean);
  28. }
  29. @Test
  30. public void saveOrUpdateByMap() {
  31. Map<String, Object> map = new HashMap<>();
  32. map.put("id", 1);
  33. map.put("contentId", 101L);
  34. map.put("title", "北京");
  35. map.put("content", "saveOrUpdateByMap作为一个中国人,我感到很自豪。");
  36. map.put("type", 1);
  37. map.put("createAt", 1578912614123L);
  38. map.put("publishAt", 1578912614125L);
  39. assert service.saveOrUpdate(map, Article.class);
  40. }
  41. @Test
  42. public void deleteById() {
  43. saveOrUpdate();
  44. assert service.deleteById("1");
  45. }
  46. @Test
  47. public void deleteByQuery() {
  48. saveOrUpdate();
  49. assert service.deleteByQuery("id:[1 TO 12]");
  50. }
  51. @Test
  52. public void deleteAll() {
  53. assert service.deleteByQuery("*:*");
  54. }
  55. @Test
  56. public void batchSaveOrUpdate() {
  57. List<Article> entities = new ArrayList<>();
  58. for (int i = 1; i <= 35; i++) {
  59. entities.add(new Article(i, i * 10L, "我是中国人,打倒小日本" + i, "作为一个中国人,我感到很自豪。", 1, 1578912614123L, 1578912614123L));
  60. }
  61. assert service.batchSaveOrUpdate(entities);
  62. }
  63. @Test
  64. public void queryForPage() {
  65. batchSaveOrUpdate();
  66. SolrResultInfo<Article> resultInfo = service.queryForPage(Article.class, (2 - 1) * 5L, 5);
  67. Long total = resultInfo.getTotal();
  68. System.out.println("total:" + total);
  69. if (total > 0) {
  70. for (Article article : resultInfo.getList()) {
  71. System.out.println(article);
  72. }
  73. }
  74. Assert.isTrue(total.intValue() == 35, "异常:返回数据与实际不符!");
  75. }
  76. @Test
  77. public void queryHeightLight() throws InvocationTargetException, IllegalAccessException {
  78. batchSaveOrUpdate();
  79. List<String> hlFieldsList = new ArrayList<>();
  80. hlFieldsList.add("title");
  81. hlFieldsList.add("content");
  82. SolrResultInfo<Article> resultInfo = service.queryHeightLight(Article.class, "中国人", hlFieldsList);
  83. Long total = resultInfo.getTotal();
  84. System.out.println("total:" + total);
  85. if (total > 0) {
  86. for (Article article : resultInfo.getList()) {
  87. System.out.println(article);
  88. }
  89. }
  90. }
  91. @Test
  92. public void query() throws InvocationTargetException, IllegalAccessException {
  93. batchSaveOrUpdate();
  94. saveOrUpdate();
  95. // 高亮设置
  96. List<String> hlFieldsList = new ArrayList<>();
  97. hlFieldsList.add("title");
  98. hlFieldsList.add("content");
  99. // 关键字
  100. Criteria criteria = new Criteria("keywords").is("中国");
  101. // 过滤条件
  102. List<SimpleFilterQuery> fqList = new ArrayList<>();
  103. SimpleFilterQuery fq1 = new SimpleFilterQuery(new Criteria("content_id").between(70, 150));
  104. SimpleFilterQuery fq2 = new SimpleFilterQuery(new Criteria("type").is(1));
  105. SimpleFilterQuery fq3 = new SimpleFilterQuery(new Criteria("content").contains("测试"));
  106. fqList.add(fq1);
  107. fqList.add(fq2);
  108. fqList.add(fq3);
  109. // 执行查询
  110. SolrResultInfo<Article> resultInfo = service.query(Article.class, criteria, hlFieldsList, fqList, (1 - 1) * 5L, 5);
  111. Long total = resultInfo.getTotal();
  112. System.out.println("total:" + total);
  113. if (total > 0) {
  114. for (Article article : resultInfo.getList()) {
  115. System.out.println(article);
  116. }
  117. }
  118. }
  119. @Test
  120. void getById() {
  121. saveOrUpdate();
  122. Article bean = service.getById("1", Article.class);
  123. System.out.println(bean);
  124. Assert.notNull(bean, "异常:查询结果为空!");
  125. }
  126. }

5. 完整示例代码

源码:solr-springdata-example01-src.zip

方式2:Spring + Spring Data Solr

1. 添加pom.xml依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>4.3.18.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.data</groupId>
  8. <artifactId>spring-data-solr</artifactId>
  9. <version>4.0.10.RELEASE</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.apache.solr</groupId>
  13. <artifactId>solr-solrj</artifactId>
  14. <version>7.7.2</version>
  15. </dependency>

2. Solr配置**

  • 单机(application-solr.xml) ```xml <?xml version=”1.0” encoding=”UTF-8”?>

  1. - **Solr配置(solr.properties)**

spring.data.solr.core=db_sync

  1. - **集群(application-solrcloud.xml)**
  2. ```xml
  3. <?xml version="1.0" encoding="UTF-8"?>
  4. <beans xmlns="http://www.springframework.org/schema/beans"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. https://www.springframework.org/schema/beans/spring-beans.xsd">
  8. <!-- 集群版solr服务配置 -->
  9. <bean id="emptyBuilder" class="org.apache.solr.client.solrj.impl.CloudSolrClient.Builder"/>
  10. <bean id="builder" factory-bean="emptyBuilder" factory-method="withZkHost">
  11. <constructor-arg value="192.168.0.101:2181,192.168.0.102:2181,192.168.0.103:2181" type="java.lang.String"/>
  12. </bean>
  13. <bean id="cloudSolrServer" factory-bean="builder" factory-method="build">
  14. <property name="defaultCollection" value="db_sync"/>
  15. </bean>
  16. <bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
  17. <constructor-arg ref="cloudSolrServer"/>
  18. </bean>
  19. <bean id="solrTemplateService" class="com.lonton.bigdata.service.impl.SolrTemplateServiceImpl"/>
  20. </beans>

注意:由于CloudSolrClient是以非静态工程方法实例化的一个对象。所以我们不能用Spring构造实例。只能用Spring的非静态工程实例化Solr。

3. 编写关键类

■ 配置类(Article.java)

  1. import lombok.AllArgsConstructor;
  2. import lombok.Data;
  3. import lombok.NoArgsConstructor;
  4. import org.apache.solr.client.solrj.beans.Field;
  5. import org.springframework.data.solr.core.mapping.SolrDocument;
  6. import java.io.Serializable;
  7. /**
  8. * Description VO-文章类
  9. */
  10. @Data
  11. @AllArgsConstructor
  12. @NoArgsConstructor
  13. @SolrDocument(solrCoreName = "db_sync")
  14. public class Article implements Serializable {
  15. // 必须实现可序列化接口,如果要在网络上传输
  16. @Field("id")
  17. private Integer id;
  18. @Field("content_id")
  19. private Long contentId;
  20. @Field("title")
  21. private String title;
  22. @Field("content")
  23. private String content;
  24. @Field("type")
  25. private Integer type;
  26. @Field("create_at")
  27. private Long createAt;
  28. @Field("publish_at")
  29. private Long publishAt;
  30. @Override
  31. public String toString() {
  32. return "Article{" +
  33. "id=" + id +
  34. ", contentId=" + contentId +
  35. ", title='" + title + '\'' +
  36. ", content='" + content + '\'' +
  37. ", type=" + type +
  38. ", createAt=" + createAt +
  39. ", publishAt=" + publishAt +
  40. '}';
  41. }
  42. }

■ 服务接口(SolrTemplateService.java)

  1. import org.springframework.data.solr.core.query.Criteria;
  2. import org.springframework.data.solr.core.query.SimpleFilterQuery;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.util.List;
  5. import java.util.Map;
  6. public interface SolrTemplateService {
  7. /**
  8. * 将JavaBean对象添加(新增或修改)到Solr中
  9. *
  10. * @param bean VO/DTO/Pojo/Domain对象
  11. * @param <T> 泛型(对象类型)
  12. * @return
  13. */
  14. <T> boolean saveOrUpdate(T bean);
  15. /**
  16. * 将Map对象添加(新增或修改)到Solr中
  17. *
  18. * @param map Map对象
  19. * @param clz JavaBean的Class对象
  20. * @param <T> 泛型(对象类型)
  21. * @return
  22. */
  23. <T> boolean saveOrUpdate(Map map, Class<T> clz);
  24. /**
  25. * 通过文档ID删除Solr中的文档
  26. *
  27. * @param id 索引ID
  28. * @return
  29. */
  30. boolean deleteById(String id);
  31. /**
  32. * 通过查询删除Solr中对应的数据集合
  33. *
  34. * @param query 查询条件
  35. */
  36. boolean deleteByQuery(String query);
  37. /**
  38. * 批量新增或更新记录
  39. *
  40. * @param entities 新增或更新对象列表
  41. * @param <T> 泛型
  42. * @return
  43. */
  44. <T> boolean batchSaveOrUpdate(List<T> entities);
  45. /**
  46. * 分页查询
  47. *
  48. * @param clz 泛型类对应java.lang.Class
  49. * @param offset 开始索引(默认:0)
  50. * @param rows 每页显示记录数(默认:10)
  51. * @return
  52. */
  53. <T> SolrResultInfo<T> queryForPage(Class<T> clz, Long offset, Integer rows);
  54. /**
  55. * 高亮查询
  56. *
  57. * @param clz 泛型类对应java.lang.Class
  58. * @param keywords 关键字
  59. * @param hlFieldsList 高亮字段列表
  60. * @param <T> 泛型
  61. * @return
  62. */
  63. <T> SolrResultInfo<T> queryHeightLight(Class<T> clz, String keywords, List<String> hlFieldsList) throws InvocationTargetException, IllegalAccessException;
  64. /**
  65. * 通过泛型获取Solr中的对象集合
  66. *
  67. * @param clz 泛型类对应java.lang.Class
  68. * @param criteria 查询条件
  69. * @param hlFieldsList 高亮显示数据域名称,是List<String>集合
  70. * @param fqList 过滤条件
  71. * @param offset 开始索引(默认:0)
  72. * @param rows 每页显示记录数(默认:10)
  73. * @return
  74. */
  75. <T> SolrResultInfo<T> query(Class<T> clz, Criteria criteria, List<String> hlFieldsList, List<SimpleFilterQuery> fqList, Long offset, Integer rows) throws InvocationTargetException, IllegalAccessException;
  76. /**
  77. * 根据ID获取记录
  78. *
  79. * @param id 索引ID
  80. * @param clz Class对象
  81. * @param <T> 泛型
  82. * @return
  83. */
  84. <T> T getById(String id, Class<T> clz);
  85. }

■ 服务接口实现类(SolrTemplateServiceImpl.java)

  1. import com.lonton.bigdata.service.SolrResultInfo;
  2. import com.lonton.bigdata.service.SolrTemplateService;
  3. import com.lonton.bigdata.util.CommonUtils;
  4. import org.apache.commons.beanutils.BeanUtils;
  5. import org.apache.solr.client.solrj.response.UpdateResponse;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.InitializingBean;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.context.annotation.PropertySource;
  11. import org.springframework.core.env.Environment;
  12. import org.springframework.data.domain.Page;
  13. import org.springframework.data.solr.core.SolrTemplate;
  14. import org.springframework.data.solr.core.query.*;
  15. import org.springframework.data.solr.core.query.result.HighlightEntry;
  16. import org.springframework.data.solr.core.query.result.HighlightPage;
  17. import org.springframework.data.solr.core.query.result.ScoredPage;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import javax.annotation.Resource;
  21. import java.lang.reflect.InvocationTargetException;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Optional;
  25. @Service
  26. @PropertySource("classpath:solr.properties")
  27. public class SolrTemplateServiceImpl implements SolrTemplateService, InitializingBean {
  28. private static final Logger log = LoggerFactory.getLogger(SolrTemplateServiceImpl.class);
  29. private String solrCore;
  30. public String getSolrCore() {
  31. return solrCore;
  32. }
  33. public void setSolrCore(String solrCore) {
  34. this.solrCore = solrCore;
  35. }
  36. @Resource
  37. private Environment environment;
  38. @Autowired
  39. private SolrTemplate solrTemplate;
  40. /**
  41. * 将JavaBean对象添加(新增或修改)到Solr中
  42. *
  43. * @param bean VO/DTO/Pojo/Domain对象
  44. * @param <T> 泛型(对象类型)
  45. * @return
  46. */
  47. @Override
  48. @Transactional(rollbackFor = Exception.class)
  49. public <T> boolean saveOrUpdate(T bean) {
  50. try {
  51. // 将对象存储到索引库中
  52. UpdateResponse resp = solrTemplate.saveBean(solrCore, bean);
  53. // 提交
  54. solrTemplate.commit(solrCore);
  55. if (resp.getStatus() == 0) {
  56. log.debug("saveOrUpdate successfully!");
  57. return true;
  58. }
  59. } catch (Exception e) {
  60. log.error(e.getMessage());
  61. e.printStackTrace();
  62. }
  63. log.debug("saveOrUpdate failure!");
  64. return false;
  65. }
  66. /**
  67. * 将Map对象添加(新增或修改)到Solr中
  68. *
  69. * @param map Map对象
  70. * @param clz JavaBean的Class对象
  71. * @param <T> 泛型(对象类型)
  72. * @return
  73. */
  74. @Override
  75. @Transactional(rollbackFor = Exception.class)
  76. public <T> boolean saveOrUpdate(Map map, Class<T> clz) {
  77. try {
  78. T bean = CommonUtils.mapToBean((Map<String, ?>) map, clz);
  79. // 将对象存储到索引库中
  80. UpdateResponse resp = solrTemplate.saveBean(solrCore, bean);
  81. // 提交
  82. solrTemplate.commit(solrCore);
  83. if (resp.getStatus() == 0) {
  84. log.debug("saveOrUpdate successfully!");
  85. return true;
  86. }
  87. } catch (Exception e) {
  88. log.error(e.getMessage());
  89. e.printStackTrace();
  90. }
  91. log.debug("saveOrUpdate failure!");
  92. return false;
  93. }
  94. /**
  95. * 通过文档ID删除Solr中的文档
  96. *
  97. * @param id 索引ID
  98. * @return
  99. */
  100. @Override
  101. @Transactional(rollbackFor = Exception.class)
  102. public boolean deleteById(String id) {
  103. try {
  104. UpdateResponse resp = solrTemplate.deleteByIds(solrCore, id);
  105. solrTemplate.commit(solrCore);
  106. if (resp.getStatus() == 0) {
  107. log.debug("delete successfully!");
  108. return true;
  109. }
  110. } catch (Exception e) {
  111. log.error(e.getMessage());
  112. e.printStackTrace();
  113. }
  114. log.debug("delete failure!");
  115. return false;
  116. }
  117. /**
  118. * 通过查询删除Solr中对应的数据集合
  119. *
  120. * @param query 查询条件
  121. */
  122. @Override
  123. @Transactional(rollbackFor = Exception.class)
  124. public boolean deleteByQuery(String query) {
  125. try {
  126. UpdateResponse resp = solrTemplate.delete(solrCore, new SimpleQuery(query));
  127. solrTemplate.commit(solrCore);
  128. if (resp.getStatus() == 0) {
  129. log.debug("delete successfully!");
  130. return true;
  131. }
  132. } catch (Exception e) {
  133. log.error(e.getMessage());
  134. e.printStackTrace();
  135. }
  136. log.debug("delete failure!");
  137. return false;
  138. }
  139. /**
  140. * 批量新增或更新记录
  141. *
  142. * @param entities 新增或更新对象列表
  143. * @param <T> 泛型
  144. * @return
  145. */
  146. @Override
  147. public <T> boolean batchSaveOrUpdate(List<T> entities) {
  148. try {
  149. // 将对象存储到索引库中
  150. UpdateResponse resp = solrTemplate.saveBeans(solrCore, entities);
  151. // 提交
  152. solrTemplate.commit(solrCore);
  153. if (resp.getStatus() == 0) {
  154. log.debug("batchSaveOrUpdate successfully!");
  155. return true;
  156. }
  157. } catch (Exception e) {
  158. log.error(e.getMessage());
  159. e.printStackTrace();
  160. }
  161. log.debug("batchSaveOrUpdate failure!");
  162. return false;
  163. }
  164. /**
  165. * 分页查询
  166. *
  167. * @param clz 泛型类对应java.lang.Class
  168. * @param offset 开始索引(默认:0)
  169. * @param rows 每页显示记录数(默认:10)
  170. * @return
  171. */
  172. @Override
  173. public <T> SolrResultInfo<T> queryForPage(Class<T> clz, Long offset, Integer rows) {
  174. // 定义返回自定义数据结构对象
  175. SolrResultInfo<T> resultInfo = new SolrResultInfo<T>();
  176. Query query = new SimpleQuery("*:*");
  177. query.setOffset(offset); // 开始索引(默认 0)
  178. query.setRows(rows); // 每页记录数(默认 10)
  179. ScoredPage<T> page = solrTemplate.queryForPage(solrCore, query, clz);
  180. resultInfo.setTotal(page.getTotalElements());
  181. resultInfo.setList(page.getContent());
  182. return resultInfo;
  183. }
  184. /**
  185. * 高亮查询
  186. *
  187. * @param clz 泛型类对应java.lang.Class
  188. * @param keywords 关键字
  189. * @param hlFieldsList 高亮字段列表
  190. * @param <T> 泛型
  191. * @return
  192. */
  193. @Override
  194. public <T> SolrResultInfo<T> queryHeightLight(Class<T> clz, String keywords, List<String> hlFieldsList) throws InvocationTargetException, IllegalAccessException {
  195. // 定义返回自定义数据结构对象
  196. SolrResultInfo<T> resultInfo = new SolrResultInfo<T>();
  197. // 封装高亮条件
  198. HighlightQuery q = new SimpleHighlightQuery();
  199. HighlightOptions highlightOptions = new HighlightOptions();
  200. // 高亮显示字段
  201. for (String s : hlFieldsList) {
  202. highlightOptions.addField(s);
  203. }
  204. // 高亮样式
  205. highlightOptions.setSimplePrefix("<em style='color: red'>");
  206. highlightOptions.setSimplePostfix("</em>");
  207. q.setHighlightOptions(highlightOptions);
  208. Criteria criteria = new Criteria("keywords").is(keywords);
  209. q.addCriteria(criteria);
  210. HighlightPage<T> pages = solrTemplate.queryForHighlightPage(solrCore, q, clz);
  211. // *********************** 设置高亮结果 ***********************
  212. for (HighlightEntry<T> h : pages.getHighlighted()) { // 循环高亮入口集合
  213. T t = h.getEntity(); // 获取原实体类
  214. if (h.getHighlights().size() > 0 && h.getHighlights().get(0).getSnipplets().size() > 0) {
  215. for (HighlightEntry.Highlight highlight : h.getHighlights()) {
  216. for (String value : highlight.getSnipplets()) {
  217. // 替换高亮部分的内容
  218. BeanUtils.setProperty(t, highlight.getField().getName(), value);
  219. }
  220. }
  221. }
  222. }
  223. resultInfo.setTotal(pages.getTotalElements());
  224. resultInfo.setList(pages.getContent());
  225. return resultInfo;
  226. }
  227. /**
  228. * 通过泛型获取Solr中的对象集合
  229. *
  230. * @param clz 泛型类对应java.lang.Class
  231. * @param criteria 查询条件
  232. * @param hlFieldsList 高亮显示数据域名称,是List<String>集合
  233. * @param fqList 过滤条件
  234. * @param offset 开始索引(默认:0)
  235. * @param rows 每页显示记录数(默认:10)
  236. * @return
  237. */
  238. @Override
  239. public <T> SolrResultInfo<T> query(Class<T> clz, Criteria criteria, List<String> hlFieldsList, List<SimpleFilterQuery> fqList, Long offset, Integer rows) throws InvocationTargetException, IllegalAccessException {
  240. // 定义返回自定义数据结构对象
  241. SolrResultInfo<T> resultInfo = new SolrResultInfo<>();
  242. // 封装高亮条件
  243. HighlightQuery q = new SimpleHighlightQuery();
  244. // 过滤条件
  245. for (SimpleFilterQuery simpleFilterQuery : fqList) {
  246. q.addFilterQuery(simpleFilterQuery);
  247. }
  248. // 分页查询
  249. q.setOffset(offset);
  250. q.setRows(rows);
  251. // 高亮显示字段
  252. HighlightOptions highlightOptions = new HighlightOptions();
  253. for (String s : hlFieldsList) {
  254. highlightOptions.addField(s);
  255. }
  256. // 高亮样式
  257. highlightOptions.setSimplePrefix("<em style='color: red'>");
  258. highlightOptions.setSimplePostfix("</em>");
  259. q.setHighlightOptions(highlightOptions);
  260. q.addCriteria(criteria);
  261. HighlightPage<T> pages = solrTemplate.queryForHighlightPage(solrCore, q, clz);
  262. // *********************** 设置高亮结果 ***********************
  263. for (HighlightEntry<T> h : pages.getHighlighted()) { // 循环高亮入口集合
  264. T t = h.getEntity(); // 获取原实体类
  265. if (h.getHighlights().size() > 0 && h.getHighlights().get(0).getSnipplets().size() > 0) {
  266. for (HighlightEntry.Highlight highlight : h.getHighlights()) {
  267. for (String value : highlight.getSnipplets()) {
  268. // 替换高亮部分的内容
  269. BeanUtils.setProperty(t, highlight.getField().getName(), value);
  270. }
  271. }
  272. }
  273. }
  274. resultInfo.setTotal(pages.getTotalElements());
  275. resultInfo.setList(pages.getContent());
  276. return resultInfo;
  277. }
  278. private <T> SolrResultInfo<T> queryNoHeightLight(Class<T> clz, String query, Criteria criteria, List<String> hlFieldsList, Long offset, Integer rows) {
  279. // 定义返回自定义数据结构对象
  280. SolrResultInfo<T> resultInfo = new SolrResultInfo<>();
  281. // 查询条件构造
  282. Query q = new SimpleQuery(query);
  283. // 分页查询
  284. q.setOffset(offset); // 开始索引(默认 0)
  285. q.setRows(rows); // 每页记录数(默认 10)
  286. // 条件查询
  287. q.addCriteria(criteria);
  288. Page<T> pages = solrTemplate.query(solrCore, q, clz);
  289. resultInfo.setTotal(pages.getTotalElements());
  290. resultInfo.setList(pages.getContent());
  291. return resultInfo;
  292. }
  293. /**
  294. * 根据ID获取记录
  295. *
  296. * @param id 索引ID
  297. * @param clz Class对象
  298. * @param <T> 泛型
  299. * @return
  300. */
  301. @Override
  302. public <T> T getById(String id, Class<T> clz) {
  303. Optional<T> bean = solrTemplate.getById(solrCore, id, clz);
  304. return bean.isPresent() ? bean.get() : null;
  305. }
  306. @Override
  307. public void afterPropertiesSet() throws Exception {
  308. setSolrCore(environment.getRequiredProperty("spring.data.solr.core"));
  309. }
  310. }
  1. import java.util.List;
  2. /**
  3. * Description 查询Solr返回的对象,对象类型为T的集合,还包含Solr中符合条件记录总数
  4. */
  5. public class SolrResultInfo<T> {
  6. private List<T> list = null;
  7. private Long total = null;
  8. public List<T> getList() {
  9. return list;
  10. }
  11. public void setList(List<T> list) {
  12. this.list = list;
  13. }
  14. public Long getTotal() {
  15. return total;
  16. }
  17. public void setTotal(Long total) {
  18. this.total = total;
  19. }
  20. }

■ 工具类(CommonUtils.java)

  1. import com.google.common.collect.Lists;
  2. import com.google.common.collect.Maps;
  3. import org.apache.commons.beanutils.BeanUtils;
  4. import org.springframework.cglib.beans.BeanMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. /**
  8. * Description 工具类(Map与JavaBean互转)
  9. */
  10. public class CommonUtils {
  11. /**
  12. * 把一个Map转换成指定类型的JavaBean对象
  13. *
  14. * @param map Map对象
  15. * @param clazz Class对象
  16. * @param <T> 泛型
  17. * @return
  18. */
  19. public static <T> T mapToBean(Map<String, ?> map, Class<T> clazz) {
  20. try {
  21. T bean = clazz.newInstance();
  22. BeanUtils.populate(bean, map);
  23. return bean;
  24. } catch (Exception e) {
  25. throw new RuntimeException(e);
  26. }
  27. }
  28. /**
  29. * 将List<Map<String,Object>>转换为List<T>
  30. *
  31. * @param maps Map对象列表
  32. * @param clazz Class对象
  33. * @return
  34. * @throws InstantiationException
  35. * @throws IllegalAccessException
  36. */
  37. public static <T> List<T> mapsToBeans(List<Map<String, Object>> maps, Class<T> clazz)
  38. throws InstantiationException, IllegalAccessException {
  39. List<T> list = Lists.newArrayList();
  40. if (maps != null && maps.size() > 0) {
  41. Map<String, Object> map;
  42. T bean;
  43. for (int i = 0, size = maps.size(); i < size; i++) {
  44. map = maps.get(i);
  45. bean = clazz.newInstance();
  46. list.add((T) mapToBean(map, bean.getClass()));
  47. }
  48. }
  49. return list;
  50. }
  51. /**
  52. * 将对象装换为map
  53. *
  54. * @param bean
  55. * @return
  56. */
  57. public static <T> Map<String, Object> beanToMap(T bean) {
  58. Map<String, Object> map = Maps.newHashMap();
  59. if (bean != null) {
  60. BeanMap beanMap = BeanMap.create(bean);
  61. for (Object key : beanMap.keySet()) {
  62. map.put(key + "", beanMap.get(key));
  63. }
  64. }
  65. return map;
  66. }
  67. /**
  68. * 将List<T>转换为List<Map<String, Object>>
  69. *
  70. * @param beanList
  71. * @return
  72. */
  73. public static <T> List<Map<String, Object>> beansToMap(List<T> beanList) {
  74. List<Map<String, Object>> list = Lists.newArrayList();
  75. if (beanList != null && beanList.size() > 0) {
  76. Map<String, Object> map;
  77. T bean;
  78. for (int i = 0, size = beanList.size(); i < size; i++) {
  79. bean = beanList.get(i);
  80. map = beanToMap(bean);
  81. list.add(map);
  82. }
  83. }
  84. return list;
  85. }
  86. }

4. 测试

■ 测试类(SolrTemplateServiceTests.java)

  1. import com.lonton.bigdata.vo.Article;
  2. import org.junit.Test;
  3. import org.junit.runner.RunWith;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.data.solr.core.query.Criteria;
  6. import org.springframework.data.solr.core.query.SimpleFilterQuery;
  7. import org.springframework.test.context.ContextConfiguration;
  8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  9. import org.springframework.util.Assert;
  10. import java.lang.reflect.InvocationTargetException;
  11. import java.util.ArrayList;
  12. import java.util.HashMap;
  13. import java.util.List;
  14. import java.util.Map;
  15. @RunWith(SpringJUnit4ClassRunner.class)
  16. // 加载集群配置
  17. @ContextConfiguration(locations = {"classpath*:application.xml", "classpath*:application-solrcloud.xml"})
  18. // 加载单机配置
  19. //@ContextConfiguration(locations = {"classpath*:application.xml", "classpath*:application-solr.xml"})
  20. public class SolrTemplateServiceTests {
  21. @Autowired
  22. private SolrTemplateService service;
  23. @Test
  24. public void saveOrUpdate() {
  25. Article bean = new Article();
  26. bean.setId(1);
  27. bean.setContentId(99L);
  28. bean.setTitle("测试-SolrTemplateService.saveOrUpdate");
  29. bean.setContent("测试-作为一个中国人,我感到很自豪");
  30. bean.setType(1);
  31. bean.setCreateAt(1578912619999L);
  32. bean.setPublishAt(1578912619999L);
  33. assert service.saveOrUpdate(bean);
  34. }
  35. @Test
  36. public void saveOrUpdateByMap() {
  37. Map<String, Object> map = new HashMap<>();
  38. map.put("id", 1);
  39. map.put("contentId", 101L);
  40. map.put("title", "北京");
  41. map.put("content", "saveOrUpdateByMap作为一个中国人,我感到很自豪。");
  42. map.put("type", 1);
  43. map.put("createAt", 1578912614123L);
  44. map.put("publishAt", 1578912614125L);
  45. assert service.saveOrUpdate(map, Article.class);
  46. }
  47. @Test
  48. public void deleteById() {
  49. saveOrUpdate();
  50. assert service.deleteById("1");
  51. }
  52. @Test
  53. public void deleteByQuery() {
  54. saveOrUpdate();
  55. assert service.deleteByQuery("id:[1 TO 12]");
  56. }
  57. @Test
  58. public void deleteAll() {
  59. assert service.deleteByQuery("*:*");
  60. }
  61. @Test
  62. public void batchSaveOrUpdate() {
  63. List<Article> entities = new ArrayList<>();
  64. for (int i = 1; i <= 35; i++) {
  65. entities.add(new Article(i, i * 10L, "我是中国人,打倒小日本" + i, "作为一个中国人,我感到很自豪。", 1, 1578912614123L, 1578912614123L));
  66. }
  67. assert service.batchSaveOrUpdate(entities);
  68. }
  69. @Test
  70. public void queryForPage() {
  71. batchSaveOrUpdate();
  72. SolrResultInfo<Article> resultInfo = service.queryForPage(Article.class, (2 - 1) * 5L, 5);
  73. Long total = resultInfo.getTotal();
  74. System.out.println("total:" + total);
  75. if (total > 0) {
  76. for (Article article : resultInfo.getList()) {
  77. System.out.println(article);
  78. }
  79. }
  80. Assert.isTrue(total.intValue() == 35, "异常:返回数据与实际不符!");
  81. }
  82. @Test
  83. public void queryHeightLight() throws InvocationTargetException, IllegalAccessException {
  84. batchSaveOrUpdate();
  85. List<String> hlFieldsList = new ArrayList<>();
  86. hlFieldsList.add("title");
  87. hlFieldsList.add("content");
  88. SolrResultInfo<Article> resultInfo = service.queryHeightLight(Article.class, "中国人", hlFieldsList);
  89. Long total = resultInfo.getTotal();
  90. System.out.println("total:" + total);
  91. if (total > 0) {
  92. for (Article article : resultInfo.getList()) {
  93. System.out.println(article);
  94. }
  95. }
  96. }
  97. @Test
  98. public void query() throws InvocationTargetException, IllegalAccessException {
  99. batchSaveOrUpdate();
  100. saveOrUpdate();
  101. // 高亮设置
  102. List<String> hlFieldsList = new ArrayList<>();
  103. hlFieldsList.add("title");
  104. hlFieldsList.add("content");
  105. // 关键字
  106. Criteria criteria = new Criteria("keywords").is("中国");
  107. // 过滤条件
  108. List<SimpleFilterQuery> fqList = new ArrayList<>();
  109. SimpleFilterQuery fq1 = new SimpleFilterQuery(new Criteria("content_id").between(70, 150));
  110. SimpleFilterQuery fq2 = new SimpleFilterQuery(new Criteria("type").is(1));
  111. SimpleFilterQuery fq3 = new SimpleFilterQuery(new Criteria("content").contains("测试"));
  112. fqList.add(fq1);
  113. fqList.add(fq2);
  114. fqList.add(fq3);
  115. // 执行查询
  116. SolrResultInfo<Article> resultInfo = service.query(Article.class, criteria, hlFieldsList, fqList, (1 - 1) * 5L, 5);
  117. Long total = resultInfo.getTotal();
  118. System.out.println("total:" + total);
  119. if (total > 0) {
  120. for (Article article : resultInfo.getList()) {
  121. System.out.println(article);
  122. }
  123. }
  124. }
  125. @Test
  126. public void getById() {
  127. saveOrUpdate();
  128. Article bean = service.getById("1", Article.class);
  129. System.out.println(bean);
  130. Assert.notNull(bean, "异常:查询结果为空!");
  131. }
  132. }

5. 完整示例代码

源码:solr-springdata-example02-src.zip

参考

语雀:Spring Data Solr入门
https://www.yuque.com/carve/lhrlki/0d12e69e3bfb541d5262b49699daae30
https://www.yuque.com/itmoon/fzz84n/zso8rp
博客园:Spring Data Solr操作Solr的简单案例
https://www.cnblogs.com/blazeZzz/p/9526178.html
博客园:SpringBoot整合Spring Data Solr
https://www.cnblogs.com/kazetotori/p/8549458.html
博客园:Solr7.x学习(8)-使用spring-data-solr
https://www.cnblogs.com/zhi-leaf/p/11612367.html
CSDN:solr 7.X 与spring-data 3.X整合
https://blog.csdn.net/alistair_chow/article/details/80801402