Firstly, let's take a look at what standard Hibernate Search annotated entities would look like:
First our Game Entity:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Indexed | |
@Entity | |
@Table(name = "Game") | |
public class Game { | |
private Long id; | |
private String title; | |
private List<Vendor> vendors; | |
public Game() { | |
} | |
public Game(String title) { | |
this.title = title; | |
} | |
@Id | |
@GeneratedValue | |
public Long getId() { | |
return id; | |
} | |
public void setId(Long id) { | |
this.id = id; | |
} | |
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.NO) | |
@Column | |
public String getTitle() { | |
return title; | |
} | |
public void setTitle(String title) { | |
this.title = title; | |
} | |
@IndexedEmbedded(includeEmbeddedObjectId = true, targetElement = Vendor.class) | |
@OneToMany | |
public List<Vendor> getVendors() { | |
return vendors; | |
} | |
public void setVendors(List<Vendor> vendors) { | |
this.vendors = vendors; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Entity | |
@Table(name = "Vendor") | |
public class Vendor { | |
private Long id; | |
private String name; | |
private List<Game> games; | |
@Id | |
@GeneratedValue | |
public Long getId() { | |
return id; | |
} | |
public void setId(Long id) { | |
this.id = id; | |
} | |
@Column | |
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.NO) | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
@ManyToOne(targetEntity = Game.class) | |
@ContainedIn | |
public List<Game> getGames() { | |
return games; | |
} | |
public void setGames(List<Game> games) { | |
this.games = games; | |
} | |
} |
To get this to work properly in the current state of my Generic JPA version an additional @InIndex is needed at the class-level:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@InIndex | |
@Entity | |
@Table(name = "Vendor") | |
public class Vendor { | |
//... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@InIndex | |
@Indexed | |
@Entity | |
@Table(name = "Game") | |
public class Game { | |
//... | |
} |
This is a requirement when working with the general JPA specification as some implementors create subclasses for each Entity and Hibernate-Search needs to know which class in the hierarchy was the original one that was supposed to be in the Index.
At the moment we configure our Hibernate Search instance by subclassing JPASearchFactory. While this might be a bit unconvenient for production use it gets the job done easily during development. Later on this will be possible to be configured via a properties/xml file.
Note that this is an example for a SearchFactory in a Java EE application:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//make sure this is constructed at Container startup | |
@Singleton | |
@Startup | |
public class EJBSearchFactory extends SQLJPASearchFactory { | |
@Resource | |
private ManagedScheduledExecutorService exec; | |
@PersistenceUnit | |
private EntityManagerFactory emf; | |
/** | |
* since we can not hook into the JPA lifecycle for now | |
* we have to make sure the SearchFactory is started. | |
* This is done via super.init(); | |
*/ | |
@PostConstruct | |
public void startup() { | |
super.init(); | |
//register this SearchFactory in Search | |
Search.setup( this ); | |
} | |
/** | |
* since we can not hook into the JPA lifecycle for now | |
* we have to make sure the SearchFactory is stopped as well. | |
* This is done via super.shutdown(); | |
*/ | |
@PreDestroy | |
public void shutdown() { | |
super.shutdown(); | |
} | |
@Override | |
public void updateEvent(List<UpdateInfo> arg0) { | |
//callback, only useful for custom update event processing | |
} | |
@Override | |
protected int getBatchSizeForUpdates() { | |
//how many update-information entities should be polled from | |
//the database at once | |
return 2; | |
} | |
@Override | |
protected String getConfigFile() { | |
//Hibernate Search Engine properties are configured in this file | |
return "/hsearch.properties"; | |
} | |
@Override | |
protected long getDelay() { | |
//the delay between update polling | |
return 500; | |
} | |
@Override | |
protected TimeUnit getDelayUnit() { | |
//the timeunit for the delay | |
return TimeUnit.MILLISECONDS; | |
} | |
@Override | |
protected EntityManagerFactory getEmf() { | |
//-> the EMF used in this application | |
return this.emf; | |
} | |
@Override | |
protected List<Class<?>> getIndexRootTypes() { | |
//the Root types | |
return Arrays.asList( Game.class ); | |
} | |
@Override | |
protected TriggerSQLStringSource getTriggerSQLStringSource() { | |
//the trigger source to be used to create the updates triggers | |
//in the database (will not be discussed in this blogpost) | |
return new MySQLTriggerSQLStringSource(); | |
} | |
@Override | |
protected List<Class<?>> getUpdateClasses() { | |
//the updates classes (-> later in this post) | |
return Arrays.asList( GameUpdates.class, VendorUpdates.class, GameVendorUpdates.class ); | |
} | |
@Override | |
protected boolean isUseJTATransaction() { | |
return true; | |
} | |
@Override | |
protected ScheduledExecutorService getExecutorServiceForUpdater() { | |
//this is needed to have a transaction environemtn for the updater thread | |
//for a JTA transaction this must be a ManagedScheduledExecutorService | |
return this.exec; | |
} | |
@Override | |
protected Connection getConnectionForSetup(EntityManager em) { | |
return em.unwrap(Connection.class); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Entity | |
@Table(name = "GameUpdates") | |
@Updates(tableName = "GameUpdates", originalTableName = "Game") | |
public class GameUpdates { | |
@Id | |
private Long id; | |
@IdFor(entityClass = Game.class, columns = "gameId", columnsInOriginal = "id") | |
@Column | |
private Long gameId; | |
@Event(column = "eventType") | |
@Column | |
private Integer eventType; | |
public Long getId() { | |
return id; | |
} | |
public void setId(Long id) { | |
this.id = id; | |
} | |
public Long getGameId() { | |
return gameId; | |
} | |
public void setGameId(Long gameId) { | |
this.gameId = gameId; | |
} | |
public Integer getEventType() { | |
return eventType; | |
} | |
public void setEventType(Integer eventType) { | |
this.eventType = eventType; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Entity | |
@Table(name = "VendorUpdates") | |
@Updates(tableName = "VendorUpdates", originalTableName = "Vendor") | |
public class VendorUpdates { | |
@Id | |
private Long id; | |
@IdFor(entityClass = Vendor.class, columns = "vendorId", columnsInOriginal = "id") | |
@Column | |
private Long vendorId; | |
@Event(column = "eventType") | |
@Column | |
private Integer eventType; | |
public Long getId() { | |
return id; | |
} | |
public void setId(Long id) { | |
this.id = id; | |
} | |
public Long getVendorId() { | |
return vendorId; | |
} | |
public void setVendorId(Long vendorId) { | |
this.vendorId = vendorId; | |
} | |
public Integer getEventType() { | |
return eventType; | |
} | |
public void setEventType(Integer eventType) { | |
this.eventType = eventType; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Entity | |
@Table(name = "Game_VendorUpdates") | |
@Updates(tableName = "Game_VendorUpdates", originalTableName = "Game_Vendor") | |
public class GameVendorUpdates { | |
@Id | |
private Long id; | |
@IdFor(entityClass = Game.class, columns = "gameId", columnsInOriginal = "game_ID") | |
@Column | |
private Long gameId; | |
@IdFor(entityClass = Vendor.class, columns = "vendorId", columnsInOriginal = "vendors_ID") | |
@Column | |
private Long vendorId; | |
@Event(column = "eventType") | |
@Column | |
private Integer eventType; | |
public Long getId() { | |
return id; | |
} | |
public void setId(Long id) { | |
this.id = id; | |
} | |
public Long getGameId() { | |
return gameId; | |
} | |
public void setGameId(Long gameId) { | |
this.gameId = gameId; | |
} | |
public Integer getEventType() { | |
return eventType; | |
} | |
public void setEventType(Integer eventType) { | |
this.eventType = eventType; | |
} | |
} |
Note that the user not only has to create a table for each Entity, but also one for each mapping table as well. For an existing database the user will have to index a manually for now (However: All data that is persisted AFTER the setup was done will be updated automatically):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FullTextEntityManager fem = Search.getFullTextEntityManager( this.em ); | |
fem.beginSearchTransaction(); | |
Game newGame = new Game( "Legend of Zelda" ); | |
fem.index( newGame ); | |
fem.commitSearchTransaction(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FullTextEntityManager fem = Search.getFullTextEntityManager( this.em ); | |
FullTextQuery fullTextQuery = fem.createFullTextQuery( new TermQuery( new Term( "title", "Legend of Zelda" ) ), Game.class ); | |
List<Game> result = fullTextQuery.getResultList(); |
Happy Coding,
Martin
Enjoyed your approach to explaining how it works, hope to see more blog posts from you. thank you!
AntwortenLöschenHibernate Online Training | Java Online Training
Hibernate Training in Chennai Java Training in Chennai