完整程式碼示例,請參考個人GitHub倉庫:(github.com/KimZing),包含controller/repository以及測試程式碼。
歡迎star,如有錯誤,歡迎指正_
一、環境簡介
- idea 2016.3
- jdk 1.8
- ElasticSearch 2.4(之所以不用最新的,是因為SpringBoot和ES的版本是需要相匹配的,而SpringBoot Starter目前不支援最新版)
二、ES簡介
開發過Java搜尋的同學一定都知道lucene搜尋引擎,但是lucene只是一個搜尋引擎,就好比一個汽車的發動機,重要但是卻無法直接使用。後來就有了為大家所知的solr搜尋,提供了對應的web操作介面和java api操作,但是solr的資料併發量和大資料量下的表現相比後來者ES都還是有一定差距的,而且ES是天生支援分散式叢集的。
ES是什麼?我們可以把ES比作一個Mysql資料庫,同樣用來儲存資料,不過比Mysql提供了更多的搜尋功能,例如分詞搜尋,關聯度搜尋等,而且搜尋速度也不是同一級別的,ES能夠實現百萬資料/秒的查詢速度。接下來將ES中用到的概念和Mysql進行類比
欄位 | 解釋 |
---|---|
index | 索引,相當於Mysql中的一個庫,例如有一個叫『jd』的庫,那麼裡面可以建立很多表,儲存不同型別的資料,而表在ES中就是type。 |
type | 型別,相當於Mysql中的一張表,儲存json型別的資料 |
document | 文件,一個文件相當於Mysql一行的資料 |
shards | 分片,通俗理解,就是資料分成幾塊區域來儲存,可以理解為mysql中的分庫分表(不太恰當) |
replicas | 備份,就是分片的備份數,相當於mysql的備份庫 |
ES使用json資料進行資料傳遞,例如{username:king,age:12
},那麼這一整條json資料就是一個document,而username,age就是field。
三、SpringBoot整合ES的依賴
//ES的核心依賴Startercompile('org.springframework.boot:spring-boot-starter-data-elasticsearch')//jna依賴,否則專案啟動時,會報classNotFound: native method disable的錯誤compile("com.sun.jna:jna:3.0.9")//新增web支援,方便測試compile('org.springframework.boot:spring-boot-starter-web')testCompile('org.springframework.boot:spring-boot-starter-test')複製程式碼
四、配置檔案
配置檔案是可以配置也可以不配置的,data-elasticsearch的依賴結構中已經包含了Lucene和ES的jar,SpringBoot會自動在本地給我們生成一個ES的倉庫,專案下會自動產生一個data資料夾儲存ES的資料。如果我們不配置ES例項,那麼SpringBoot就會自動生成這個ES例項,當然效能肯定是不行的,所以我們還是使用自己搭建的ES例項。
data-elasticsearch的依賴結構
連線獨立的ES例項的配置如下
spring: data: #ElasticSearch的連線地址 elasticsearch: cluster-name: elasticsearch cluster-nodes: localhost:9300複製程式碼
關於ES的安裝可以參考我的另一篇博文CentOS6.5安裝ES教程
五、編寫儲存實體類
編寫實體類主要會用到如下三個註解
1.類上註解:@Document (相當於Hibernate實體的@Entity/@Table) (必寫)
型別 | 屬性名 | 預設值 | 說明 |
---|---|---|---|
String | indexName | 無 | 索引庫的名稱,建議以專案的名稱命名 |
String | type | “” | 型別,建議以實體的名稱命名 |
short | shards | 5 | 預設分割槽數 |
short | replica | 1 | 每個分割槽預設的備份數 |
String | refreshInterval | “1s” | 重新整理間隔 |
String | indexStoreType | “fs” | 索引檔案儲存型別 |
2.主鍵註解:@Id (相當於Hibernate實體的主鍵@Id註解) (必寫)
只是一個標識,並沒有屬性。
3.屬性註解 @Field (相當於Hibernate實體的@Column註解)
@Field預設是可以不加的,預設所有屬性都會新增到ES中。
型別 | 屬性名 | 預設值 | 說明 |
---|---|---|---|
FileType | type | FieldType.Auto | 自動檢測屬性的型別 |
FileType | index | FieldIndex.analyzed | 預設情況下分詞 |
boolean | store | false | 預設情況下不儲存原文 |
String | searchAnalyzer | “” | 指定欄位搜尋時使用的分詞器 |
String | indexAnalyzer | “” | 指定欄位建立索引時指定的分詞器 |
String[] | ignoreFields | { } |
如果某個欄位需要被忽略 |
4.實體類示例
@Data //lombok註解,會自動生成setter/getter,需要引入lombok的包才能使用。@Document(indexName = "shop", type = "user", refreshInterval = "0s")public class User {
@Id private Long id;
private String username;
private String realname;
private String password;
private Integer age;
//這三個註解是為了前臺序列化java8 LocalDateTime使用的,需要引入jsr310的包才可以使用 @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime birth;
}複製程式碼
六、編寫倉庫
1.程式碼編寫
寫一個類繼承ElasticsearchRepository<
T, ID>
,需要寫兩個泛型,第一個代表要儲存的實體型別,第二個代表主鍵型別,例如寫一個User類的倉儲如下:
/** * @author kingboy--KingBoyWorld@163.com * @date 2017/11/27 下午10:10 * @desc 使用者倉庫. */public interface UserRepository extends ElasticsearchRepository<
User, Long>
{
}複製程式碼
我們來看一下ElasticsearchRepository的繼承結構(如下),其實就可以發現仍然是JPA的一套Reposiroty,那我們其實就可以用JPA的一套介面操作進行資料的增刪改查,spring會自動根據方法名為我們生成對應的代理類去實現這些方法。
2.CRUD基礎操作
先來看看ElasticsearchRepository已經實現的一些基礎方法,這些方法的名稱已經具有很好的說明解釋了,那麼大家自己看看,很容易就能理解
3.稍微複雜操作
jpa自帶的這些方法肯定是不能滿足我們的業務需求的,那麼我們如何自定義方法呢?我們只要使用特定的單詞對方法名進行定義,那麼Spring就會對我們寫的方法名進行解析,生成對應的例項進行資料處理,有木有很簡單?那麼接下來就使用Spring官方文件中的例項進行演示。
先來看下關鍵字的說明
關鍵字 | 使用示例 | 等同於的ES查詢 |
---|---|---|
And | findByNameAndPrice | {“bool” : {“must” : [ {“field” : {“name” : “?” } }, {“field” : {“price” : “?” } } ] } } |
Or | findByNameOrPrice | {“bool” : {“should” : [ {“field” : {“name” : “?” } }, {“field” : {“price” : “?” } } ] } } |
Is | findByName | {“bool” : {“must” : {“field” : {“name” : “?” } } } } |
Not | findByNameNot | {“bool” : {“must_not” : {“field” : {“name” : “?” } } } } |
Between | findByPriceBetween | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true } } } } } |
LessThanEqual | findByPriceLessThan | {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true } } } } } |
GreaterThanEqual | findByPriceGreaterThan | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true } } } } } |
Before | findByPriceBefore | {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true } } } } } |
After | findByPriceAfter | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true } } } } } |
Like | findByNameLike | {“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,“analyze_wildcard” : true } } } } } |
StartingWith | findByNameStartingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,“analyze_wildcard” : true } } } } } |
EndingWith | findByNameEndingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,“analyze_wildcard” : true } } } } } |
Contains/Containing | findByNameContaining | {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,“analyze_wildcard” : true } } } } } |
In | findByNameIn(Collectionnames) | {“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?” } }, {“field” : {“name” : “?” } } ] } } } } |
NotIn | findByNameNotIn(Collectionnames) | {“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?” } } } } } } |
True | findByAvailableTrue | {“bool” : {“must” : {“field” : {“available” : true } } } } |
False | findByAvailableFalse | {“bool” : {“must” : {“field” : {“available” : false } } } } |
OrderBy | findByAvailableTrueOrderByNameDesc | {“sort” : [{ “name” : {“order” : “desc” } }],“bool” : {“must” : {“field” : {“available” : true } } } } |
下面寫幾個示例進行演示,只把倉儲層的列出來了,整體執行是測試過的,沒問題,如果需要整體程式碼請到本文頂部的github倉庫檢視。
/** * @author kingboy--KingBoyWorld@163.com * @date 2017/11/27 下午10:10 * @desc 使用者倉庫. */public interface UserRepository extends ElasticsearchRepository<
User, Long>
{
/** * 查詢使用者名稱為username的使用者 * @param username * @return */ List<
User>
findByUsername(String username);
/** * 查詢使用者名稱為username並且真實姓名為realname的使用者 * @param username * @param realname */ List<
User>
findByUsernameAndRealname(String username, String realname);
/** * 查詢使用者名稱為username或者姓名為realname的使用者 */ List<
User>
findByUsernameOrRealname(String username, String realname);
/** * 查詢使用者名稱不是username的所有使用者 * @param username * @return */ List<
User>
findByUsernameNot(String username);
/** * 查詢年齡段為ageFrom到ageTo的使用者 * @param ageFrom * @param ageTo * @return */ List<
User>
findByAgeBetween(Integer ageFrom, Integer ageTo);
/** * 查詢生日小於birthTo的使用者 */ List<
User>
findByBirthLessThan(LocalDateTime birthTo);
/** * 查詢生日段大於birthFrom的使用者 * @param birthFrom * @return */ List<
User>
findByBirthGreaterThan(LocalDateTime birthFrom);
/** * 查詢年齡小於或等於ageTo的使用者 */ List<
User>
findByAgeBefore(Integer ageTo);
/** * 查詢年齡大於或等於ageFrom的使用者 * @param ageFrom * @return */ List<
User>
findByAgeAfter(Integer ageFrom);
/** * 使用者名稱模糊查詢 * @param username * @return */ List<
User>
findByUsernameLike(String username);
/** * 查詢以start開頭的使用者 * @param start * @return */ List<
User>
findByUsernameStartingWith(String start);
/** * 查詢以end結尾的使用者 * @return */ List<
User>
findByUsernameEndingWith(String end);
/** * 查詢使用者名稱包含word的使用者 * @param word * @return */ List<
User>
findByUsernameContaining(String word);
/** * 查詢名字屬於usernames中的使用者 * @param usernames * @return */ List<
User>
findByUsernameIn(Collection<
String>
usernames);
/** * 查詢名字不屬於usernames中的使用者 * @param usernames * @return */ List<
User>
findByUsernameNotIn(Collection<
String>
usernames);
/** *最後來個複雜點的:查詢年齡小於ageTo,姓名以start開頭,id大於idTo的使用者,並且按照年齡倒序 * @return */ List<
User>
findByAgeBeforeAndUsernameStartingWithAndIdGreaterThanOrderByAgeDesc(Integer ageTo, String start, Long idTo);
}複製程式碼
4.更復雜一點的操作
我們可以使用@Query註解進行查詢,這樣要求我們需要自己寫ES的查詢語句,需要會ES查詢才可以,其實也很簡單,不會寫查就是了。看看官方給的例子
public interface BookRepository extends ElasticsearchRepository<
Book, String>
{
@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"
}
}
}
}") Page<
Book>
findByName(String name,Pageable pageable);
}複製程式碼
5.我們還可以使用類似Hibernate中criteria的方式進行查詢,
這時候我們需要自己寫查詢條件,在類中注入UserRepository,使用search方法傳入查詢引數,然後獲取查詢結果示例如下:
/** * @author kingboy--KingBoyWorld@163.com * @date 2017/11/28 下午12:53 * @desc 使用者服務. */@Servicepublic class UserService {
@Resource UserRepository userRepository;
public Page<
User>
getUsers() {
//建立builder BoolQueryBuilder builder = QueryBuilders.boolQuery();
//builder下有must、should以及mustNot 相當於sql中的and、or以及not //設定模糊搜尋,真實姓名中包含金的使用者 builder.must(QueryBuilders.fuzzyQuery("realname", "金"));
//設定使用者名稱為king builder.must(new QueryStringQueryBuilder("king").field("username"));
//排序 FieldSortBuilder sort = SortBuilders.fieldSort("age").order(SortOrder.DESC);
//設定分頁 //====注意!es的分頁和Hibernate一樣api是從第0頁開始的========= PageRequest page = new PageRequest(0, 2);
//構建查詢 NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//將搜尋條件設定到構建中 nativeSearchQueryBuilder.withQuery(builder);
//將分頁設定到構建中 nativeSearchQueryBuilder.withPageable(page);
//將排序設定到構建中 nativeSearchQueryBuilder.withSort(sort);
//生產NativeSearchQuery NativeSearchQuery query = nativeSearchQueryBuilder.build();
//執行,返回包裝結果的分頁 Page<
User>
resutlList = userRepository.search(query);
return resutlList;
}
}複製程式碼
作者:KimZing來源:CSDN原文:blog.csdn.net/KingBoyWorl…