Hibernate Search 和 Lucene 的快速介紹

jopen 11年前發布 | 19K 次閱讀 Lucene 搜索引擎

在上篇博客“為你的Hibernate應用程序添加搜索功能的最簡單的方法”中我談到了一些在你應用中整合Hibernate Search的場景,還講到了一些Hibernate Search以及它與Hibernate Core, Lucene 和Solr之間的聯系。這篇博客中我們將快速瀏覽一個示例(實際上它是一個JUnit測試用例),這個示例將會用Hibernate Core和Java Persistence API(JPA)注解來持久化一個簡單實體到一個關系數據庫,并且利用Hibernate Search通過Lucene索引的創建/更新來對數據庫中更新了的hibernate管理實體進行搜索。

有些碎碎念我嚼的在深入前得先明確一下:

1. 該示例是基于Hibernate Search 4.3.0 , 源碼已經被打成Eclipse項目附在文章末位處。你可能需要m2eclipse插件來導入這個項目,或者你也可以直接編譯然后運行測試用例。

2.當你在執行新增/更新/刪除操作時,Hibernate Search 將自動的生成和維護Lucene索引,不需要寫代碼去專門維護。

3.基本上靠JPA和Hinernate Search的注解就可以驅動Hibernate框架干大部分有關創建表,持久化測試數據和讓Lucene構建和彈出可查詢的索引和數據的工作。

4 因為這是個Junit測試,我們會利用H2的內存數據庫的優勢和Lucene索引能力的優勢。如果你想轉到硬盤數據庫環境中去保存數據和索引,只需要在Hinernate的xml配置文件做少許的改動即可。

5.這片博客的目的是演示如何用Hinernate Search來讓Hibernate管理的實體可查詢和如何用Lucene索引來查詢。這個演示只是告訴你在基于Hibernate的應用中添加查詢功能是個很簡單的事。其實Lucene還有很多東西需要了解,例如Text Tonkenizers 和 analyzers 的工作原理到底是啥。并且Lucene和Hibernate Search組合還能干好多牛逼活,我的這片博客僅僅說了個皮毛而已。如果這片文章引起你的注意了,我希望你關注LuceneHinernate Search 項目和其項目網站。

我將會用一個很簡單的例子來演示你如何上手Hibernate Search。 在這個例子里 ,我們有一個汽車實體將會被持久化。我們的單元測試會持久化這個實體到我們的H2數據庫的內存存儲區間里。我會用JPA注解指引Hibernate如何去持久化這個實體。Hibernate會自動在數據庫中創建表結構。我還會用Hibernate Search 注解來通知框架給我們的實體在哪一個字段上建立索引并且這個被索引的字段的內容在查詢時是否需要返回該字段所存儲的數據。最后,我們將把Hibernate 和 Hibernate Search 通過配置文件結合起來并且創建一個Junit 測試用例,該用例會在數據庫中生成一些汽車實體并且觸發Hibernate Search把要索引的字段發送給Lucene。每一個單元測試的實現方法都通過調用HibernateSearch和Lucene的API來實現我們殊途同歸的查詢。

下面就是我們希望Hibernate為我們保存的Car實體,我們使用了JPA注解比如@Entity,@Id和@GeneratedValue,它們將分別告訴Hibernate JPA這個類是需要被持久化的,id字段被用作主鍵,而且我們希望數據庫為我們自動生成id值。我們也使用了幾個Hibernate Search的注解:

  • @Indexed: 標明這個實體需要被Lucene創建索引,從而使之可以被檢索
  • @Analyzer: 告訴Hibernate Search來標記它的域以及更新Lucene索引的時候使用哪個Lucene分析器。注意:你以后檢索的時候,使用一個與Lucene為你將要檢索的文件創建索引的時候使用的分析器相同的分析器是非常重要的。然后使用一個不同的分析器可能也會返回我們想要的結果,但是這得不到保證,所以,總是先研究你選擇的用來創建索引和檢索的分析器,然后再做出明智的選擇。
  • @DocumentId:標明Car的id字段應該被用作Lucene索引中文檔的ID,這幾乎總是和數據庫中實體的主鍵是同一個字段。
  • @Field: 告訴Hibernate Search為該字段創建愛你索引,并且提供一些其他信息,比如該字段在索引中需要被如何處置。

@Entity
@Indexed
@Analyzer(impl = org.apache.lucene.analysis.standard.StandardAnalyzer.class)
public class Car {

@Id
@GeneratedValue
@DocumentId
private Long id;

@Column
@Field(store = Store.YES)
private String make;

@Column
@Field(store = Store.YES)
private String model;

@Column
@Field(store = Store.YES)
private short year;

@Column
@Field(store = Store.NO)
private String description;

public Car() {
}

public Car(String make, String model, short year, String description) {
    super();
    this.make = make;
    this.model = model;
    this.year = year;
    this.description = description;
}

public String getMake() {
    return make;
}
// more getters/setters</pre><span>前面所講的就是Hibernate持久化Car對象和Hibernate Search 讓Cars變得可搜索所需要的。現在,我們來看一下如何搜索Cars,首先,我們需要加載Hibernate配置文件,然后創建一個數據庫Session:</span> <pre class="brush: java; title: ; notranslate">@Before

public void setUp() throws Exception { Configuration configuration = new Configuration(); configuration.configure("hibernate-test-cfg.xml"); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); hibernateSessionFactory = configuration.buildSessionFactory(serviceRegistry); hibernateSession = hibernateSessionFactory.openSession(); populateDBWithTestData(); }</pre>

我們現在可以持久化一些Car對象來測試一下:

private void populateDBWithTestData() {
        Car[] testCars = { new Car("Shelby American", "GT 350", (short) 1967, "This is Tim's car!"),
            new Car("Chevrolet", "Bel Air", (short) 1957, "This is a true classic") };

Transaction tx = hibernateSession.beginTransaction();

hibernateSession.save(testCars[0]);
hibernateSession.save(testCars[1]);

tx.commit();

}</pre>

現在我們的兩個測試用的Car對象已經保存到H2數據庫中了,而且Lucene也已經對它們創建了索引!我們可以通過Lucene來搜索Cars:

@Test
public void testUsingLuceneBooleanQueryReturningFullEntity() throws Exception {
    FullTextSession fullTextSession = Search.getFullTextSession(hibernateSession);

BooleanQuery bq = new BooleanQuery();
TermQuery gt350TermQuery = new TermQuery(new Term("model", "GT 350"));
TermQuery belAirTermQuery = new TermQuery(new Term("model", "Bel Air"));
bq.add(gt350TermQuery, BooleanClause.Occur.SHOULD);
bq.add(belAirTermQuery, BooleanClause.Occur.SHOULD);
Query q = new QueryParser(Version.LUCENE_36, "cs-method", new StandardAnalyzer(Version.LUCENE_36)).parse(bq
    .toString());

org.hibernate.Query hibernateQuery = fullTextSession.createFullTextQuery(q, Car.class);
List searchResults = hibernateQuery.list();

boolean foundShelby = false;
boolean foundBelAir = false;
for (Car car : searchResults) {
    if (car.getModel().equals("GT 350")) {
        foundShelby = true;
    } else if (car.getModel().equals("Bel Air")) {
        foundBelAir = true;
    }
}
Assert.assertEquals(2, searchResults.size());
Assert.assertTrue(foundShelby && foundBelAir);

}</pre>關于上面的搜索代碼,這里有幾個關鍵點:

  • 第3行,我們獲取了一個Hibernate Search FullTextSession。需要注意的是這個session裝飾了一個正常的Hibernate數據庫Session對象,這讓Hibernate Search能夠清楚創建過索引的對象的插入、更新、刪除操作,從而保證Lucene的索引是最新的。
  • 從第5行到第13行,我們創建了一個查詢,來檢索我們測試時保存的兩個Car對象,我們選擇使用Lucene的BooleanQuery來查詢,盡管當你現在了全部的源碼之后你會發現,有很多方法都可以創建類似的查詢。這個查詢將執行這樣的操作:“查找所有model屬性值為‘GT350’或者‘Bel Air’的Car對象”。我們創建了查詢對象,然后讓Lucene QueryParser對查詢對象進行了分析,然后用FullTextSession將它翻譯成標準的Hibernate查詢。
  • 剩下的代碼用來檢查我們創建的查詢是否的確返回了我們期望在Lucene索引中查找到的值,然后使用斷言進行驗證。
  • </ul>

    如果你想了解創建跟上面類似的查詢的其他方式或者想加載上面的代碼然后親自嘗試一下Hibernate Search,你可以下載本篇文章的Hibernate Search實例代碼

    我們的測試用例使用的Hibernate配置文件非常簡單。它配置了我們要用的H2數據庫,告訴將Hibernate指給了我們的Car實體類,然后告訴Hibernate Search存放Lucene索引的位置。我只想對Hibernate配置文件(在可供下載的工程里,它的名字是"hibernate-test-cfg.xml")的一個部分進行說明。

    <!-- 將索引保存到內存中,所以測試后不需要進行索引清理 -->
    org.hibernate.search.store.impl.RAMDirectoryProvider

    <!-- 在生產應用中需要設置下面的內容,將索引保存到硬盤上. --> <!-- <property name="hibernate.search.default.directory_provider"> org.hibernate.search.store.impl.FSDirectoryProvider </property> <property name="hibernate.search.default.indexBase">c:/temp/lucene/indexes</property> -->

    <!-- 定義Hibernate實體映射. 標準的Hibernate配置,無需指定Hibernate Search. --> <mapping class="net.timontech.hibernate.search.Car"/></pre>

    正如我上面提到的,我們同時保存了數據庫和Lucene索引在內存中。我們這樣做只是為了測試,因為這樣我們的單元測試就沒有可以清理的數據。你是不可能在生產環境中這樣做的。上面的配置文件中,Hibernate Search提供了一個用于將索引保存到內存中的RAMDirectoryProvider和一個允許你指定索引保存位置的FSDirectoryProvider。把索引存儲位置從內存調整到電腦硬盤就如同修改一個Hibernate Search屬性一樣簡單。

    上面配置文件中的的“mapping”節點告訴Hibernate Core檢查Car類中的注解,這些注解將指示hibernate如何將Car實體持久化到數據庫中。

    很明顯,關于Hibernate Search和Lucene還有很多東西需要了解,Lucene本身就是一個既強大又靈活的庫。然而,如果你還沒有接觸過Hibernate Search或者Lucene,我希望這篇文章能夠讓你體會一下這些技術的“味道”并且能為你的入門提供足夠的信息。如果對你來說Hibernate Search是完全陌生的,我強烈建議你下載附件中的工程,將它導入到Eclipse中,然后看一下Hibernate Search和Lucene的API,然后試一下它們的功能。試過之后,如果你還不了解Lucene,我建議你學習一下Lucene的文檔。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!