I'm the person behind annotations in Hibernate: Hibernate Annotations, Hibernate EntityManager, Hibernate Validator and Hibernate Search. I am a member of the EJB 3.0 expert group as well as the JSR-303 Bean validation spec lead.
My latest annotations thing is Hibernate Search : an Hibernate/Apache Lucene integration letting search domain model objects in free form (google or yahoo like) without hassle.
I am also co-writing Hibernate Search in Action already available in early access program.
|
Recent Entries |
|
17. Nov 2008
|
|
|
27. Oct 2008
|
|
|
08. Oct 2008
|
|
|
02. Oct 2008
|
|
|
20. Aug 2008
|
|
|
18. Aug 2008
|
|
|
17. Jul 2008
|
|
|
09. Jun 2008
|
|
|
25. Apr 2008
|
|
|
10. Apr 2008
|
Hibernate Search 3.1.0.CR1 is out. Download it here.
One of the main work was to align more closely with the new Lucene features:
- read-only IndexReader for better concurrency at search time
- use of DocIdSet rather than BitSet in filter implementations for greater flexibility
- explicit use of Lucene's commit()
We have also added a few performance tricks:
- avoid reading unnecessary fields from Lucene when possible
- use Hibernate queries when projecting the object instance (as opposed to rely on batch size)
@DocumentId is now optional and defaults to the property marked as @Id. Scores are no longer normalized (ie no longer <= 1)
The full changelog is available here. Expect a GA in the next two weeks. Help us iron this release and provide issue reports in JIRA.
Many thanks to Hardy for literally hammering out fixes after fixes and making the release happen on time.
Hibernate Search 3.1 beta2 is out with a significant focus on performance improvements, scalability and API clean up.
Here is the main area of work:
- Upgrade to Lucene 2.4 which opened up a lot of optimization possibilities on the Hibernate Search side.
- Inserts and deletes are now done in a single index opening rather than two.
- The window of locking has been reduced a lot during writes, especially on transactions involving several entities.
- Filter caching configuration has been simplified.
- Expose scoped analyzer for a given class: queries can now use the same analyzer used at indexing time transparently.
- Properly genericize the API (no more raw type used)
- Fix a few bugs around the Solr analyzer integration and moved to Solr 1.3.
- Fix various bugs including the long standing HSEARCH-142.
We have incorporated a lot of enhancement based on our work on the book Hibernate Search in Action and some genius performance ideas from Sanne. This version is still a beta because we still have a few optimization and enhancements in our pocket but CR1 should come out mid november-ish.
The complete list of changes can be found in jira and you can download Hibernate Search here.
Let us know what you think.
Until now, it was not possible or easy to reuse constraints to make more complex constraints.
The new specification draft introduces the notion of constraint composition. Composition is useful for three main things:
- reuse more primitive constraints to build new constraints and avoid duplication
- define fine grained error reports for a given constraint
- expose how a constraint is composed and describe its primitive blocks
The last point is particularly interesting. Constraints implementations are black boxes answering yes or no to the question: Is this value valid or not?
.
When constraints need to be applied outside the Java world or applied on a different metadata model, black boxes do no good. There is no way to know that @OrderNumber actually apply some restriction on the length of the number as well as apply some CRC verification.
Composition helps to solve the problem by giving access to the primitive constituencies of a constraint. Let's first see how to define a composed constraint.
Defining a composed constraint
To define the list of constraints composing a main constraint, simply annotate the main constraint annotation with the composing constraint annotations.
@Numerical
@Size(min=5, max=5)
@ConstraintValidator(FrenchZipcodeValidator.class)
@Documented
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface FrenchZipCode {
String message() default "Wrong zipcode";
String[] groups() default {};
}
When @FrenchZipCode is placed on a property, its value is validated against @Numerical, @Size(min=5, max=5) and the constraint implementation FrenchZipcodeValidator: all the composing constraints are validated as well as the logic of the main constraint. Note that a composing constraint can itself be composed of constraints.
Each failing constraint will generate an individual error report which is useful when you want do display fine grained reports to your user. But this might be quite confusing and a single error report is more appropriate in some situations. You can force Bean Validation to raise a single error report (the composed constraint error report) if any of its composing constraint fails by using the @ReportAsSingleInvalidConstraint annotation.
@Numerical
@Size(min=5, max=5)
@ReportAsSingleInvalidConstraint
@ConstraintValidator(FrenchZipcodeValidator.class)
@Documented
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface FrenchZipCode {
String message() default "Wrong zipcode";
String[] groups() default {};
}
In the past two examples, none of the composing annotation parameters can be adjusted at declaration time. This is fine if the zip code is always of size 5. But what happens if the size can be adjusted depending on the property? The spec offers a way is a way to override
a parameter from the composing annotation based on a parameter from the composied annotation using the @OverridesParameter annotation.
@Numerical
@Size //arbitrary parameter values
@ConstraintValidator(FrenchZipcodeValidator.class)
@Documented
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface FrenchZipCode {
String message() default "Wrong zipcode";
String[] groups() default {};
@OverridesParameters( {
@OverridesParameter(constraint=Size.class, parameter="min")
@OverridesParameter(constraint=Size.class, parameter="max") } )
int size() default 5;
@OverridesParameter(constraint=Size.class, parameter="message")
String sizeMessage() default "{error.zipcode.size}";
@OverridesParameter(constraint=Numerical.class, parameter="message")
String numericalMessage() default "{error.zipcode.numerical}";
}
The following declaration
@FrenchZipcode(size=9, sizeMessage="Zipcode should be of size {value}")
is equivalent to the following definition / declaration combination
@Numerical
@Size(min=9, max=9, message="Zipcode should be of size {value}")
@ConstraintValidator(FrenchZipcodeValidator.class)
@Documented
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface FrenchZipCode {
String message() default "Wrong zipcode";
String[] groups() default {};
}
Let's now see how a tool would use this extra information.
Exploring composed constraints with the metadata API
Using the metadata API, you can explore the list of constraints on a given object or property. Each constraint is described by a ConstraintDescriptor. It lists all the composing constraints and provides a ConstraintDescriptor object for each. The ConstraintDescriptor honors overridden parameters (ie using @OverridesParameter): the annotation and the parameter values returned contain the overridden value.
ElementDescriptor ed = addressValidator.getConstraintsForProperty("zipcode");
for ( processConstraintDescriptor cd : ed.getConstraintDescriptors() ) {
processConstraintDescriptor(cd); //check all constraints on zip code
}
public void processConstraintDescriptor(processConstraintDescriptor cd) {
//Size.class is understood by the tool
if ( cd.getAnnotation().getAnnotationType().equals( Size.class ) ) {
Size m = (Size) cd.getAnnotation();
column.setLength( m.max() ); //read and use the metadata
}
for (ConstraintDescriptor composingCd : cd.getComposingConstraints() ) {
processConstraintDescriptor(cd); //check composing constraints recursively
}
}
When using the following declaration
@FrenchZipCode(size=10) public String zipCode;
the tool will set the zipCode column length to 10.
While the tool does not know what @FrenchZipCode and @Numerical means, it knows how to make use of @Max. Javascript generation libraries or persistence tools typically understand a core subset of constraints. If a complex constraint is composed of one or several of these core subset constraints, it can be partially understood and processed by Java Persistence for example.
This is one of the reasons why it is strongly recommended to build complex constraints on top of more primitive ones. The Bean Validation specification will come with a core of constraints tools will be able to rely upon.
Let us know what you think in our forum.
We have been recently working a lot on the Bean Validation spec (JSR 303) and have two good news for you:
- the bootstrap API proposal is out
- the reference implementation is available too (end of this blog)
Please have a look at both and give us feedbacks. It is still time to make it better ;)
Bootstrap API
The primary goal of the bootstrap API is trivial. Provide access to a ValidatorFactory capable of building Validator instances. These validator instances are then used to validate the domain model.
Some additional goals have been pursued as well:
- simple to use
- type safe
- extensible and flexible to the container environment (Java EE, Java SE, dependency injection framework, OSGi-esque containers and so on)
- support multiple implementations
Examples
The best way to learn an API is to see it in action.
Everyday bootstrap
The first example shows the most simple way and also the most common.
ValidatorFactory factory = Validation.getValidatorBuilder().build(); //cache the factory somewhere Validator<Address> addressValidator = factory.getValidator(Address.class);
This creates a thread-safe ValidatorFactory object that should be cached. In this example, the default Bean Validation provider is used unless an explicit provider implementation is defined in the Bean Validation XML configuration file.
Container driven bootstrap
The second example refines some of the configuration elements.
ValidatorFactory factory = Validation
.getValidatorBuilder()
.messageResolver( new WebBeansMessageResolver() )
.constraintFactory( new WebBeansComponentConstraintFactory() )
.build();
//cache the factory somewhere
Validator<Address> addressValidator = factory.getValidator(Address.class);
This example shows a typical bootstrap in a container. The container has the ability to refined the message resolution strategy to adhere it standards. In the case of Web Bean, one can imagine the message resolver to resolve contextual components and EL expressions.
A container is also responsible for the lifecycle of its components. A custom contraint implementation factory can be provided. It will be responsible for instantiating constraint implementations. In the case of Web Beans, one can imagine a constraint factory properly injecting components inside the constraint implementation.
Some containers change the standard Java rules when it comes to classloader and resource discovery. It can make Bean Validation provider discovery challenging in such environments. To work around this problem, the specification lets a container override the provider resolution strategy. This strategy can be injected at bootstrap time.
//OSGi environment is picky when it comes to class loaders.
ValidatorFactory factory = Validation
.defineBootstrapState()
.providerResolver( new OSGiServiceDiscoverer() )
.build();
//cache the factory somewhere
Validator<Address> addressValidator = factory.getValidator(Address.class);
OSGIServiceDiscoverer has the knowledge of the OSGi isolation rules and can resolve the list of available providers accordingly.
Specific provider bootstrap
The third example shows how to select a specific provider programmatically. Each provider is uniquely identified by a sub interface of ValidatorBuilder. Hibernate Validator for example provides org.hibernate.validator.HibernateValidatorBuilder.
//get Hibernate Validator provider
ValidatorFactory factory = Validation
.builderType( HibernateValidatorBuilder.class )
.getValidatorBuilder()
.build();
//cache the factory somewhere
Validator<Address> addressValidator = factory.getValidator(Address.class);
Using a specific sub interface of ValidatorBuilder as unique identifier has an other advantage. This sub interface can host provider specific configuration and still be called in a type safe way.
HibernateValidatorBuilder hibernateBuilder = Validation
.builderType( HibernateValidatorBuilder.class )
.getValidatorBuilder();
ValidatorFactory factory = hibernateBuilder
.messageResolver( new ContainerMessageResolver() ) //default configuration option
.disableDDLAlteration()
.enableConstraintHotRedeploy()
.build();
Or written in a more compact way
ValidatorFactory factory = Validation
.builderType( HibernateValidatorBuilder.class )
.getValidatorBuilder()
.messageResolver( new ContainerMessageResolver() )
.disableDDLAlteration()
.enableConstraintHotRedeploy()
.build();
Where HibernateValidatorBuilder looks like:
public interface HibernateValidatorBuilder extends ValidatorBuilder<HibernateValidatorBuilder> {
HibernateValidatorBuilder disableDDLAlteration();
HibernateValidatorBuilder enableconstraintHotRedeploy();
}
Note that nowhere we needed to down cast objects!
Main APIs
The main artifacts involved in the bootstrap process are:
Validation: API entry point. Lets you optionally define the Bean Validation provider targeted as well as a provider resolution strategy. Validation generates ValidatorBuilder objects and can bootstrap any provider implementation.
ValidationProvider: contract between the bootstrap procedure and a Bean Validation provider implementation.
ValidationProviderResolver: returns a list of all Bean Validation providers available in the execution context (generally the classpath).
ValidatorBuilder: collects the configuration details that will be used to build ValidatorFactory. A specific sub interface of ValidatorBuilder must be provided by Bean Validation providers as a unique identifier. This sub interface typically hosts provider specific configurations.
ValidatorFactory: result of the bootstrap process. Build Validator instances from a given Bean Validation provider. This object is thread-safe.
Specification
The bits of specification describing the bootstrap process is available here[1] and the APIs are available as part of the reference implementation. Please provide feedback either to our forum or in the JIRA issue tracker project component spec-general.
Reference implementation
The reference implementation is available in SVN. Simply do
svn checkout http://anonsvn.jboss.org/repos/hibernate/validator/trunk/ rito check it out so to speak. The spec is comprised of the validation API and the actual reference implementation. Both projects are Maven projects. They are distributed under the Apache Software License 2.0.
Many thanks to Hardy for taking over my bits and pieces of prototype, todos and complaints and make it an actual project. Hardy will probably write a blog entry sometimes in the future about the RI architecture.
You can provide feedback to our forum. If you have suggestions or bug reports, a JIRA issue tracker project has also been created.
It is still a work in progress but is good enough to start playing with it. Most of the ideas behind the spec are already present, so have a look!
After the release of Hibernate Core 3.3.0.GA last week, we are releasing aligned versions of its sister projects. These releases are primarily ensuring that everything works property on Core 3.3. They also improved a few other things:
- use of slf4j as the logging facade eliminating classloader headaches
- all projects are now deployed in the JBoss Maven repository under org/hibernate with their proper pom.xml files. Hopefully, the dark ages of Hibernate dependencies for Maven are behind us (we are still looking at pushing the JBoss repository changes to the central Maven one but it takes longer than expected: check the Hibernate development mailing list for more info)
- the build system is more modular and lets you work on each project independently making contributions much easier
- the regular set of bug fixes (check the change logs for more details)
The following releases are now available:
- Hibernate Annotations 3.4.0.GA (changelog)
- Hibernate EntityManager 3.4.0.GA (changelog)
- Hibernate Commons Annotations 3.1.0.GA (changelog)
- Hibernate Validator 3.1.0.CR2 * (changelog)
* A small glitch slipped through when we released Hibernate Core. Be sure to use Hibernate Validator 3.1.0.CR2 or above when using Core 3.3. We wanted to take our time fixing this, Validator CR2 will become GA in a week if no problem pops up.
All projects can be downloaded here.
Note that Hibernate Search 3.1.0.Beta1 is already aligned with Hibernate Core 3.3.0.GA.
Enjoy
| Showing 1 to 5 of 38 blog entries |
|
|