Hibernate Search : Mapping Entities
Join the DZone community and get the full member experience.
Join For FreeAll Hibernate Search mapping metadata is described through annotations. Hibernate Search metadata is very code centric and shares a lot of information from the class structure: annotations are a natural fit and avoid lots of redundancy compared to other metadata models like XML. It would be quite simple to add an XML deployment descriptor support for Hibernate Search. Nobody has found the time and interest to write this layer (so far) which seems to prove that this feature is not that strongly desired by the Hibernate Search community.
All Hibernate Search annotations are contained in the org.hibernate.search.annotations package.
To mark an entity as indexed by Hibernate Search, just place the @Indexed annotation on the class.
Listing 1 An entity is indexed by Hibernate Search when marked @Indexed
@Entity
@Indexed
public class Dvd {
...
}
A lot of information is inferred and a lot of work is triggered from this single annotation. When the Hibernate SessionFactory bootstraps, Hibernate Search looks for all mapped entities marked as @Indexed and processes them.
The benefit for us is that we do not have to explicitly list the indexed entities in a configuration file: it both reduces our work and limits the risk of mistakes.
The Lucene directory name is also inferred from this annotation. Because we have not explicitly defined an index name, the default naming applies. The index name is the fully qualified class name of the entity: in our example com.manning.hsia.dvdstore.model.Dvd. You can override this name by using the name attribute of @Indexed.
Listing 2 An indexed entity overriding its index name to refine the targeted Lucene Directory
@Entity
@Indexed(name="Item")
public class Dvd {
...
}
The underlying mapping between a Hibernate Search index name and a physical Lucene Directory depends entirely on the directory provider. Let's explore the two most common scenarios: the in memory directory provider (RamDirectoryProvider) and the file system directory provider (FSDirectroyProvider).
Indexes using the RamDirectoryProvider are uniquely identified by their index name for a given SessionFactory (or EntityManagerFactory if you use Java Persistence). Said otherwise, Hibernate Search keeps an instance of RamDirectory per index name when the index targets the RamDirectoryProvider.
When using FSDirectoryProvider, the index name represents the path to the physical file system directory. Relative path are prefixed with the indexBase property.
It is perfectly safe to share the same physical Lucene directory for several entities: Hibernate Search does partition the information. If you want to share the same physical Lucene directory across several entities, they just need to share the same @Indexed.name value (as well as the same DirectoryProvider type).
Listing 3 Indexed entity sharing the same underlying Lucene directory
@Entity
@Indexed(name="Item") The same index name is shared by both entities
public class Dvd {
...
}
@Entity
@Indexed(name="Item")
public class Drink {
...
}
Should I share the same Lucene directory for all my entities?
Usually, this is not necessary.
Sharing the same index will help to optimize queries as Hibernate Search will have to handle less resources at the same time. This is particularly true when the amount of index data is low. On the other hand, when the amount of data starts to grow significantly, splitting the index into several ones will help Lucene to grow. Hibernate Search offers such a possibility through what is called index sharding.
The gain provided by sharing the directory is usually not significant enough to make a difference. Maintenance might be a stronger criterion but you can see the glass half full or half empty:
•
Having one index per entity helps maintainability and allows incremental rebuild if something goes wrong on an index file
•
But having one single index (arguably) reduces maintenance
As you can see, there is no clear winner and Hibernate Search let's you do what you prefer. The authors tend to use the Hibernate Search defaults and let it mind its own business. Ease of development is really where the gain
Entity structure can be more complex than the previous example, especially when subclasses are involved.
Subclasses:
Hibernate Search, just like Hibernate is fully polymorphic and let you map hierarchies of classes as well as express polymorphic queries. Practically for you, it means that you can write classes and subclasses in your domain model without worrying about Hibernate Search. Back to your store example, our client forgot to tell us that on top of DVDs, the website also has to sell food such as popcorns and drinks such as wines. (I don't know about popcorn, but chilling out with a glass of decent wine in front of a good movie is definitely something I would be ready to pay for). We will refactor our domain model to cope with this new requirement. The website will see Items that will be declined in Dvds, Food and Drinks.
Listing 4 Mapping a class and its subclass
Hibernate Search will not only index the marked properties of a given class but also all the marked property of its superclass. In Listing 4, the Drink entity will be searchable by the following properties:
- alcoholicBeverage from Drink
- id from Item
- title from Item
You might have noticed that the Item entity is not marked as @Indexed. While marking Item with @Indexed will do no harm, it is simply not necessary. You should only mark entities with @Indexed when:
- You want to be able to search by this entity
- And the entity is of a concrete type (not abstract).
In our system, Item is an abstract class and hence will have no instances of it. Subclasses are denormalized in the Lucene index: all instances of Drink will be stored in the org.manning.hsia.dvdstore.model.Drink index including the information about its superclass' properties id and title.
Denormalization does not prevent you from executing polymorphic queries. As we will see later in this book, you can perfectly search for all Items whose title contains “valley”. Both the DVD “In the Valley of Elah” and “Nappa valley” wines will show up even if Item has not been marked with @Indexed.
Hibernate Search does not read metadata annotations from interfaces but you can of course map the implementation class and its properties.
This article is based on chapter 3 from Hibernate Search in Action by Emmanuel Bernard and John Griffin. It is being reproduced here by permission from Manning Publications.
Manning early access books and ebooks are sold exclusively through Manning.Visit the book's page for more information.
Opinions expressed by DZone contributors are their own.
Comments