Red Hat

In Relation To

The Hibernate team blog on everything data.

While Hibernate ORM applies some more polish before releasing the final version, so did the Hibernate Search team.

Hibernate Search version 5.4.0.CR2 is now available! It was built and tested with Hibernate ORM 5.0.0.CR3, again we’re just waiting for that to be final but decided to release another Candidate Release as some fixes and improvements were recently applied.

The "Meridian crossing issue"

The Spatial query feature was affected by a math sign mistake which would reveal itself on distance based queries which would cross the 180' meridian. Thanks to Alan Field for the test and Nicholas Helleringer for debugging and fixing the issue!

Serialization issue on enabling Term Vectors

Kudos to Benoit Guillon, who debugged and fixed a mistake in the Avro based serializer, for which values of Term Vectors would not be indexed when enabling Term Vectors in combination with a remote backend. See also HSEARCH-1936.

Various small improvements

You can now have the MassIndexer set a different timeout for the internal transactions it will start, so if you’re running Hibernate Search in a container like WildFly you no longer have to make a choice between having a deadline of 5 minutes or changing the default timeout of the whole container.

  • Improved validation when choosing an invalid Sort definition

  • The Infinispan module will now get you a warning if your Maven project is using the old artifact ids

  • The performance integration tests have been re-enabled, and fixed to run on latest WildFly and Java8 and Java9

  • Fixed a potential issue with timezone encoding for indexed dates

  • Improved the code generating the path for index storage to handle "." elements in the configured paths

  • Upgraded to Hibernate ORM 5.0.0.CR3 and WildFly 10.0.0.Alpha6 (WildFly only used for testing)

Where to download it from

Everything you need is available on Hibernate Search’s web site. Download the full distribution from here, or get it from Maven Central, and don’t hesitate to reach us in our forums

What’s next?

Soon we’ll be releasing a preview of 5.5 to have you upgrade to Apache Lucene 5.2. The Hibernate Search version 5.4.0.Final will be released when Hibernate ORM 5 is released as stable.

Third Candidate Release for ORM 5.0

Posted by Steve Ebersole    |       |    Tagged as Hibernate ORM Releases

Yesterday I released the third candidate release for Hibernate ORM 5.0 (5.0.0.CR3). We felt another CR was warranted because we had some minor integration (SPI) work that we needed to make in to Final, but too much development had happened since the second CR to be considered risk free to just include everything into Final. At any rate CR3 got lots of great TLC :) The complete set of changes can be seen in the Jira changelog. The main changes include:

Minor changes to the caching SPI

Essentially passing Session along to the various region access strategy methods to allow integrating with non-JDBC transactions.

Work on schema tooling

Improved namespace (catalog/schema) support overall in schema tools. Improved handling of views and synonyms for migrating and validating.

Work on bytecode enhancement

Lots of fixes based on feedback.

Consistency in Transaction API

A few changes were made to the JDCB-based TransactionCoordinator to work more like in JTA environments. Specifically:

  • implemented support for marking the Transaction for rollback-only.

  • transaction is now rolled back automatically on a failed commit.

Work on the documentation

Besides updating the content, the content has been split into 3 separate guides:

  • User Guide

  • Domain Model Mapping Guide

  • Integrations Guide

How to get it

Additionally many other improvements and bugfixes are included. See https://hibernate.atlassian.net/projects/HHH/versions/20150 for the complete list of changes.

As always, see http://hibernate.org/orm/downloads/ for information on obtaining the releases.

Hibernate Validator 5.2.1.Final*

Posted by Hardy Ferentschik    |       |    Tagged as Hibernate Validator

I know you have been waiting in anticipation, but now it is available - Hibernate Validator 5.2.1.Final :-). Given that it is a drop-in replacement for all 5.x releases, there is no reason to delay an upgrade. Just go and get it.

For the more cautious, here again the highlights of the 5.2 release with pointers to more information.

The highlights

  • Java 8 Support, most notably the ability to validate Optional and the new date/time types. Types, which do not represent an instant on the time-line, however, are not supported out of the box. This includes, for example, LocalDate and its sub-types. Semantically it does not make sense to talk about future and past, if your data type cannot be unambiguously tied to an instant in time. This is still a bit of a open discussion (see HV-981) though. Contact us if you have an opinion.

    Support for Java 8 also includes the use of type annotation constraints like:

    private List<@AcmeEmail String> emails;

    In this case each element of the emails list (or more generally of any Iterable) will be validated using the @AcmeEmail. Note the use of a custom constraint and not the Hibernate Validator provided @Email. The reason is that all provided constraints are missing java.lang.annotation.ElementType.TYPE_USE in their definition for now. Adding it would break backwards compatibility to Java 6.

    Java 8 is not a requirement for Hibernate Validator 5.2. It is still backward compatible with Java 6. Java 8 specific features are only enabled in case a Java 8 runtime is detected.

  • Ability to provide custom constraints discoverable via the Java ServiceLoader mechanism. Under the hood this uses the new ConstraintMappingContributor SPI. read more…​

  • Ability to use Hibernate Validator without dependency to the Expression Language libraries by using the new ParameterMessageInterpolator. read more…​

  • Ability to provide an external ClassLoader. Potentially handy for modularized environments. read more…​

  • Apache Karaf feature file. read more…​

  • Extension of the Path API for nodes of type ElementKind.PROPERTY, which allows to obtain the value of the represented property. read more…​

  • TimeProvider contract. read more…​

  • Tons of bug fixes

Where to get it

Maven artifacts can be found in the JBoss Maven repository, as well as in Maven Central. The GAV is org.hibernate:hibernate-validator:5.2.1.Final. Distribution bundles will be uploaded to SourceForge once it has recovered from its storage fault.

What’s next

Further development will be driven by the upcoming Bean Validation update - Bean Validation 1.2. Most likely this will align with a Hibernate Validator 6 requiring Java 8. This will be necessary to make use of all the new features Java 8 offers, amongst others the use of @Repeatable. This is in alignment with other technologies which are part of the Java EE 8 standard.

Watch for changes to the Hibernate Validator roadmap, as well as announcements on this and the Bean Validation blog.

Let us know what you think

Feedback and questions are welcome via the Hibernate Validator forum or on stackoverflow using the hibernate-validator tag. If that is not enough, check out the other ways you can get in contact with us.

Thank you

Last but not least, a big thank you to all of you who were lending a helping hand along the way, be it via a bug report or more hands on via a pull request. Special thanks to Khalid who kicked of the work on the 5.2 release series with his Google Summer of Code work. Thank you also for the smaller contributions provided by Denis Tiago, Nicolas Francois, Xavier Sosnovsky, dernasherbrezon, stawny and tonnyyi. Finally a thank you to all other Hibernate team members who were involved.

Enjoy!


* In case you are wondering what happened with 5.2.0.Final, it became obsolete while waiting for SourceForge to recover. In the meantime an IBM JVM specific issue (HV-1007) was reported and fixed.

Handling queries on complex types in Hibernate Search

Posted by Emmanuel Bernard    |       |    Tagged as Hibernate Search

Writing queries using complex types can be a bit surprising in Hibernate Search. For these multi-fields types, the key is to target each individual field in the query. Let’s discuss how this works.

What’s a complex type?

Hibernate Search lets you write custom types that take a Java property and create Lucene fields in a document. As long as there is a one property for one field relationship, you are good. It becomes more subtle if your custom bridge stores the property in several Lucene fields. Say an Amount type which has the numeric part and the currency part.

Let’s take a real example from a user using Infinispan's search engine - proudly served by Hibernate Search.

The FieldBridge
public class JodaTimeSplitBridge implements TwoWayFieldBridge {

    /**
     * Set year, month and day in separate fields
     */
    @Override
    public void set(String name, Object value, Document document, LuceneOptions luceneoptions) {
        DateTime datetime = (DateTime) value;
        luceneoptions.addFieldToDocument(
            name+".year", String.valueOf(datetime.getYear()), document
        );
        luceneoptions.addFieldToDocument(
            name+".month", String.format("%02d", datetime.getMonthOfYear()), document
        );
        luceneoptions.addFieldToDocument(
            name+".day", String.format("%02d", datetime.getDayOfMonth()), document
        );
    }

    @Override
    public Object get(String name, Document document) {
        IndexableField fieldyear = document.getField(name+".year");
        IndexableField fieldmonth = document.getField(name+".month");
        IndexableField fieldday = document.getField(name+".day");
        String strdate = fieldday.stringValue()+"/"+fieldmonth.stringValue()+"/"+fieldyear.stringValue();
        DateTime value = DateTime.parse(strdate, DateTimeFormat.forPattern("dd/MM/yyyy"));
        return String.valueOf(value);
    }

    @Override
    public String objectToString(Object date) {
        DateTime datetime = (DateTime) date;
        int year = datetime.getYear();
        int month = datetime.getMonthOfYear();
        int day = datetime.getDayOfMonth();
        String value = String.format("%02d",day)+"/"+String.format("%02d",month)+"/"+String.valueOf(year);
        return String.valueOf(value);
    }
}
The entity using the bridge
[...]
@Indexed
class BlogEntry {
    [...]

    @Field(store=Store.YES, index=Index.YES)
    @FieldBridge(impl=JodaTimeSplitBridge.class)
    DateTime creationdate;
}

Let’s query this field

A naive but intuitive query looks like this.

Incorrect query
QueryBuilder qb = sm.buildQueryBuilderForClass(BlogEntry.class).get();
Query q = qb.keyword().onField("creationdate").matching(new DateTime()).createQuery();
CacheQuery cq = sm.getQuery(q, BlogEntry.class);
System.out.println(cq.getResultSize());

Unfortunately that query will always return 0 result. Can you spot the problem?

It turns out that Hibernate Search does not know about these subfields creationdate.year, creationdate.month and creationdate.day. A FieldBridge is a bit of a blackbox for the Hibernate Search query DSL, so it assumes that you index the data in the field name provided by the name parameter (creationdate in this example).

We have plans in a not so future version of Hibernate Search to address that problem. It will only require you to provide a bit of metadata when you write such advanced custom field bridge. But that’s the future, so what to do now?

Use a single field

I am cheating here but as much as you can, try and keep the one property = one field mapping. Life will be much simpler to you. In this specific JodaTime type example, this is extremely easy. Use the custom bridge but instead of creating three fields (for year, month, day), keep it as a single field in the form of yyyymmdd.

Let’s again use our user real life solution.

A bridge using one field
public class JodaTimeSingleFieldBridge implements TwoWayFieldBridge {

    /**
     * Store the data in a single field in yyymmdd format
     */
    @Override
    public void set(String name, Object value, Document document, LuceneOptions luceneoptions) {
        DateTime datetime = (DateTime) value;
        luceneoptions.addFieldToDocument(
            name, datetime.toString(DateTimeFormat.forPattern("yyyyMMdd")), document
        );
    }


    @Override
    public Object get(String name, Document document) {
        IndexableField strdate = document.getField(name);
        return DateTime.parse(strdate.stringValue(), DateTimeFormat.forPattern("yyyyMMdd"));
    }

    @Override
    public String objectToString(Object date) {
        DateTime datetime = (DateTime) date;
        return datetime.toString(DateTimeFormat.forPattern("yyyyMMdd"));
    }
}

In this case, it would even be better to use a Lucene numeric format field. They are more compact and more efficient at range queries. Use luceneOptions.addNumericFieldToDocument( name, numericDate, document );.

The query above will work as expected now.

But my type must have multiple fields!

OK, OK. I won’t avoid the question. The solution is to disable the Hibernate Query DSL magic and target the fields directly.

Let’s see how to do it based on the first FieldBridge implementation.

Query targeting multiple fields
int year = datetime.getYear();
int month = datetime.getMonthOfYear();
int day = datetime.getDayOfMonth();

QueryBuilder qb = sm.buildQueryBuilderForClass(BlogEntry.class).get();
Query q = qb.bool()
    .must( qb.keyword().onField("creationdate.year").ignoreFieldBridge().ignoreAnalyzer()
                .matching(year).createQuery() )
    .must( qb.keyword().onField("creationdate.month").ignoreFieldBridge().ignoreAnalyzer()
                .matching(month).createQuery() )
    .must( qb.keyword().onField("creationdate.day").ignoreFieldBridge().ignoreAnalyzer()
                .matching(day).createQuery() )
   .createQuery();

CacheQuery cq = sm.getQuery(q, BlogEntry.class);
System.out.println(cq.getResultSize());

The key is to:

  • target directly each field,

  • disable the field bridge conversion for the query,

  • and it’s probably a good idea to disable the analyzer.

It’s a rather advanced topic and the query DSL will do the right thing most of the time. No need to panic just yet.

But in case you hit a complex type needs, it’s interesting to understand what is going on underneath.

Map me if you can - Advanced embeddable mappings

Posted by Gunnar Morling    |       |    Tagged as Hibernate ORM

The other day I came across an interesting mapping challenge which I thought may be worth sharing. If you are a seasoned JPA user, it will probably be nothing new to you, but those not as experienced may find it helpful :)

TL;DR - JPA let’s you override database columns for embedded objects but also for collections of embedded objects; @AttributeOverride and @AssocationOverride can be used for that.

Let’s assume the following entity model representing a person and their home and business address:

Entity model

The interesting part is that Person has two associations with Address, one with the "homeAddress" role and another one with the "businessAddress" role. Address in turn has one ore more address lines.

When mapping these types with JPA, Person naturally becomes an entity. As there is a composition relationship between Person and Address, the latter is mapped with @Embeddable. The same goes for AddressLine, which also is an @Embeddable, contained within an element collection owned by Address.

So you’d end up with these classes:

@Entity
public class Person {

    @Id
    private long id;
    private String name;
    private Address homeAddress;
    private Address businessAddress;

    // constructor, getters and setters...
}
@Embeddable
public class Address {

    private boolean active;

    @ElementCollection
    private List<AddressLine> lines = new ArrayList<AddressLine>();

    // constructor, getters and setters...
}
@Embeddable
public class AddressLine {

    private String value;

    // constructor, getters and setters...
}

@AttributeOverride

Let’s fire up a session factory with these types (this uses the brand-new bootstrapping API coming in Hibernate ORM 5) and see how that goes:

StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
    .applySetting( AvailableSettings.SHOW_SQL, true )
    .applySetting( AvailableSettings.FORMAT_SQL, true )
    .applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" )
    .build();

SessionFactory sessionFactory = new MetadataSources( registry )
    .addAnnotatedClass( Person.class )
    .buildMetadata()
    .buildSessionFactory();

Hum, that did not go too well:

org.hibernate.MappingException: Repeated column in mapping for entity:
org.hibernate.bugs.Person column: active (should be mapped with insert="false" update="false")

Of course this makes sense; As Address is embedded twice within Person, its properties must be mapped to unique column names within the Person table. The @AttributeOverride annotation can be used for that:

@Entity
public class Person {

    @Id
    private long id;
    private String name;

    @AttributeOverride(name = "active", column = @Column(name = "home_address_active"))
    private Address homeAddress;

    @AttributeOverride(name = "active", column = @Column(name = "business_address_active"))
    private Address businessAddress;

    // constructor, getters and setters...
}

With that, the session factory boots successfully. But let’s take a look at the tables that get created:

create table Person (
    id bigint not null,
    business_address_active boolean,
    home_address_active boolean,
    name varchar(255),
    primary key (id)
)

create table Person_lines (
    Person_id bigint not null,
    "value" varchar(255)
)

@AssociationOverride

The Person table looks alright, but there is only a single table for the address lines. That’s a problem as it means there is no way to tell apart the lines of the business address from the lines of the home address when reading back a person from the database.

So what to do? @AttributeOverride is of no help this time, as it’s not a single column which needs to be re-defined but an entire table. But luckily, @AttributeOverride has a companion, @AssociationOverride. This annotation can be used to configure the required tables in this case:

@Entity
public class Person {

    @Id
    private long id;
    private String name;

    @AttributeOverride(name = "active", column = @Column(name = "home_address_active"))
    @AssociationOverride(name = "lines", joinTable = @JoinTable(name = "Person_HomeAddress_Line"))
    private Address homeAddress;

    @AttributeOverride(name = "active", column = @Column(name = "business_address_active"))
    @AssociationOverride(name = "lines", joinTable = @JoinTable(name = "Person_BusinessAddress_Line"))
    private Address businessAddress;

    // constructor, getters and setters...
}

Et voilĂ , now you’ll get the DDL for creating the Person table and two different tables for the address lines:

create table Person (
    id bigint not null,
    business_address_active boolean,
    home_address_active boolean,
    name varchar(255),
    primary key (id)
)

create table Person_BusinessAddress_Line (
    Person_id bigint not null,
    "value" varchar(255)
)

create table Person_HomeAddress_Line (
    Person_id bigint not null,
    "value" varchar(255)
)

Updated Roadmap for Hibernate Search

Posted by Sanne Grinovero    |       |    Tagged as Hibernate Search

The Hibernate Search project roadmap was quite outdated, so after some team chats on IRC and our developer’s mailing list I’ve summarized our plans on the project website.

What’s the plan?

Current 80% progress

Upgrade to Hibernate ORM 5

Coming in Hibernate Search 5.4, which is almost ready, and you could try it already.

Transactional improvements for the JMS backend

Also included in version 5.4, with improved documentation and configuration examples to follow probably in 5.4.

MassIndexer Improvements

The long standing limitation of transaction timeouts was finally resolved.

Apache Lucene 5 support

We’ve been working on this on separate branches, but we won’t merge it in 5.4 as this branch is stable now. The Lucene 5 update code doesn’t look too scary but we plan that to be 5.5 's highlight so that you can focus on one thing at a time: update to Hibernate ORM 5 now, and just after to Apache Lucene 5.

Neither upgrade is particularly complex, but I hope you’ll appreciate the effort to let you update step by step and take a break in between in case there is some surprise which needs extra attention.

Consider that the upgrade to Hibernate ORM 5 implies an upgrade to WildFly 10.

Near Future Intentions

Clustering, backends and reliability

We want to make it much simpler for you to use Hibernate Search in a clustered / cloud deployment. This has been possible since years, but the amount of requests for help are soaring and we have to admit that we can make several things easier. This is going to be an ongoing effort, with small improvements like the usage of JMSXGroupID already in 5.4, improved JMS examples in 5.5, and the driver for several of the improvements I’ve listed in the roadmap for 5.6…​ however depending on your help and feedback you could read that as "5.6 and beyond".

Java 8, Java 9, …​

With Java 8 very popular and Java 9 getting closer I hope that roadmap items such as out of the box indexing for the new Date/Time types don’t need any explanation. They could use some help though! We made it easy to register new bridges for any type so you can define your own custom types and define how they should best be indexed; this implies you can make an independent plug-in package for any type we don’t support yet, and if it’s a popular type like the ones from JSR 310 or JSR 354 we’d love to integrate it.

ElasticSearch and Apache Solr

There has been interest in such integrations for a while, but we failed to make any progress as the problem needs to be broken down in smaller steps. Several points on JIRA and the roadmap might seem only tangential but resolve roadblocks and pave the road to integrate Hibernate with such services. While we’re intimately familiar with Apache Lucene and these Lucene-as-a-service alternatives provide similar features, we don’t have direct experience with these, so some help will be appreciated. The plan is to prefer merging small iterative improvements over stalling development for months, so we’ll see several steps in a 5.6+ series and use that as the foundation to assess how we can deal with API inconsistencies across these slightly different backends.

Next!

After having all the above-listed new features nicely chiseled in our current stable API, I’m confident that to make the most of features like the ElasticSearch integration we’ll need to make changes to the API. This last refinement step will define Hibernate Search 6.0.

As always, these plans might need to change and we’ll always look forward to your suggestions. I’m unable to commit on dates or seasons; I would love to see this all happen before this winter but we’ll need your contribution for that to be realistic.

IntelliJ IDEA, Mac OS X and Hibernate ORM

Posted by Emmanuel Bernard    |       |    Tagged as Hibernate ORM

Hibernate ORM, IntelliJ IDEA and Mac OS X have been a pretty tough combination to work with since our move to Gradle. It’s not exactly anyone’s fault but the combination of

  • gradle / IntelliJ integration

  • JDK 6 running IntelliJ IDEA

  • Hibernate ORM advanced use of Gradle and of custom plugins

made for a long and painful journey.

These days are over. Steve found the last blocking issue and you can now import Hibernate ORM natively in IntelliJ IDEA.

Importing Hibernate ORM in IntelliJ IDEA on Mac OS X

git clone git@github.com:hibernate/hibernate-orm.git
  • Open IntelliJ IDEA

  • File → Open

  • Select hibernate-orm

  • The defaults should be good

    • Make especially sure that you select Use default gradle wrapper

    • I use Gradle JVM to 1.8

  • Click OK and let it work and download the internet

You now have a properly imported Hibernate ORM project :)

This worked on Mac OS X 10.10.4 and IntelliJ IDEA CE 14.1.4.

Importing Hibernate ORM in NetBeans on Mac OS X

And this incidentally fixed an import bug for NetBeans.

  • Open NetBeans

  • Install the Gradle plugin

  • Open project

  • Select hibernate-orm

This worked on Mac OS X 10.10.4 and NetBeans 8.0.2.

Hibernate Search is ready for Hibernate ORM 5

Posted by Sanne Grinovero    |       |    Tagged as Hibernate Search Releases

Hibernate Search version 5.4.0.CR1 is now available! It was built and tested with Hibernate ORM 5.0.0.CR2, essentially it’s all ready for ORM 5 and we’ll just be waiting for this to be marked Final.

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-search-orm</artifactId>
    <version>5.4.0.CR1</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.0.0.CR2</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.0.0.CR2</version>
</dependency>

No longer timeouts when using the MassIndexer in a container

You can now have the MassIndexer set a different timeout for the internal transactions it will start, so if you’re running Hibernate Search in a container like WildFly you no longer have to make a choice between having a deadline of 5 minutes or changing the default timeout of the whole container.

fullTextSession
   .createIndexer( User.class )
   .transactionTimeout( 1800 ) //seconds
   .startAndWait();

Great improvements in the JMS backend

Transactional JMS backend

As explained in more detail in last week’s post, Hibernate Search now provides an option to include its indexing operations within the same transaction as the RDBMS.

In short, it’s enabled by setting this property:

hibernate.search.worker.enlist_in_transaction=true

But keep in mind: it’s a global setting! If you want to use it, all your backends shall be set to use an XA enabled, transactional JMS queue.

Please let us know if you have a great use case which would require us to allow some form of mixed mode.

The JMS message header JMSXGroupID

Hibernate Search will now use the specific JMSXGroupID message header and set it to the index name. That will allow users of the JMS backend to take advantage of message grouping.

Modules versions and running in WildFly

The first WildFly version to use Hibernate ORM 5 is version 10.0.0.Alpha5, which was released the past weekend. So the JBoss Modules we create for this appserver are targeting now WildFly 10, but at least version 10.0.0.Alpha5.

Where to download it from

Everything you need is available on Hibernate Search’s web site. Download the full distribution from here, or get it from Maven Central, and don’t hesitate to reach us in our forums

Second Candidate Release for 5.0

Posted by Steve Ebersole    |       |    Tagged as Hibernate ORM Releases

The second candidate release for Hibernate ORM 5.0. CR2 was required because of a bug in how class-loading worked in certain managed JPA environments. The details can be found in HHH-9887. The main changes since CR1 include:

Corrected ClassLoader usage

This is the blocking issue mentioned above. The problem would occur in environments passing a "temp ClassLoader" into Hibernate via the JPA integration SPI (javax.persistence.spi.PersistenceUnitInfo#getNewTempClassLoader) and manifest itself as ClassCastExceptions.

Second-level caching SPI improvement

The second-level caching SPI was improved, allowing the caching providers to generate cache keys. This includes the addition of the following methods:

  • org.hibernate.cache.spi.access.EntityRegionAccessStrategy#generateCacheKey

  • org.hibernate.cache.spi.access.CollectionRegionAccessStrategy#generateCacheKey

  • org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy#generateCacheKey

Improved cache-by-reference support

For those making use of caching immutable entities by reference (rather than by "disassembled" state), an additional performance improvement is included in CR2.

Ability to disable auto-quoting of keyword-as-identifier

A new feature in 5.0 is the automatic quoting of identifiers that are believed to be keywords in the underlying database. As this support is new, often the Dialects are too aggressive in what they deem to be keywords. Thus we added a new setting to disable this behavior if it causes problems in your application. This can be controlled by specifying hibernate.auto_quote_keyword as false in configuration settings.

Significantly improved bytecode enhancement support

Including:

  • added ability for automatic management of bi-directional associations

  • self-contained dirty state tracking (more efficient flushing), including switchable algorithms for dirty determination

Odds-n-ends

Additionally many other improvements and bugfixes are included. See the changelogs for the complete list of changes.

The release tag is available on GitHub.

As always, see http://hibernate.org/orm/downloads/ for information on obtaining the releases.

Hibernate Search, JMS and transactions

Posted by Emmanuel Bernard    |       |    Tagged as Hibernate Search

Hibernate Search sends the indexing requests in the post transaction phase. Until now. The JMS backend can now send its indexing requests transactionally with the database changes. Why is that useful? Read on.

A bit of context

When you change indexed entities, Hibernate Search collects these changes during the database transaction. It then waits for the transaction to be successful before pushing them to the backend.

Hibernate Search has a few backends:

  • lucene: this one uses Lucene to index the entities

  • JMS: this one sends a JMS message with the list of index changes. This JMS queue is then read by a master which uses the Lucene backend.

  • and a few more that are not interesting here

Running the backend after the transaction (in the afterTransaction phase to be specific) is generally what you want. Just to name a few reasons:

  • you don’t want index changes to be executed if you end up rollbacking the database transaction

  • you don’t necessarily want your database changes to fail because the indexing fails: you can always rebuild the index from your database.

  • and most backends don’t support transactions anyways

Hibernate Search lets you enlist an error callback so that you are notified of these indexing problems when they happen and react the way you want (log, raise an exception, retry etc).

So why make the JMS backend join the transaction

If you make the JMS backend join the transaction, then either the database changes happen and the JMS messages are received by the queue, or nothing happens (no database change and no JMS message).

The non transactional approach is still our recommended approach. But there are a few reasons why you want to go transactional.

No code to handle the message failure

It eliminates the need to write an error callback and handle this problematic case.

Simpler exploitation processes

It simplifies your exploitation processes. You can focus on monitoring your JMS queue (rates of messages coming in, rates of messages coming out) which will give you an accurate status of the health of Hibernate Search’s work.

Transactional mass indexing

When doing changes to lots of indexed entities, it is common to use the following pseudo pattern to avoiod OutOfMemoryException

for (int i = 0 ; i < workLoadSize ; i++) {
    // do changes
    if ( i % 500 == 0 ) {
        fullTextSession.flush();
        fullTextSession.flushToIndexes();
        fullTextSession.clear();
    }
}

If you use the transactional JMS backend, then all the message will be sent or none of them.

Make sure your JMS implementation and your JTA transaction manager are smart and don’t keep the messages in memory or you might face an OutOfMemoryException.

More consistent batching frameworks flow

If you use a batching framework like Spring Batch which keeps its "done" status in a database, you have a guarantee that changes, indexing requests and batch status are all consistent.

How to use the feature

This feature is now integrated in master and will be in an Hibernate Search release any time soon.

We kept the configuration as simple as possible. Simply add the following property

hibernate.search.worker.enlist_in_transaction=true

If you try and use this option on a non transactional backend (i.e. not JMS), Hibernate Search will yell at you.

Make sure to use a XA JMS queue and that your database supports XA as we are talking about coordinated transactional systems.

Many thanks to Yoann, one of our customers, who helped us refine the why and how of that feature.

back to top