Browse DevX
Sign up for e-mail newsletters from DevX


Object Generation: A Better Approach to Hibernate Integration : Page 3

If you're using Hibernate, your business objects may be littered with unnecessary code. Learn a different, proven approach that challenges conventional wisdom and promises more streamlined integration with less code.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

The Custom Identifier Generator

Hibernate uses identifier generators to allocate identifiers for new objects. It normally retrieves a new identifier from the database or an internal cache immediately before persisting an object for the first time. You've already seen how object generation changes this behavior to assign an identifier upon object creation (see the generateEntity method listed earlier).

However, generating identifiers during object creation is only part of a full solution. The identifier strategy must work properly whether it's dealing with a newly created object or an existing one that's been retrieved from the database. No single strategy can accomplish this without introducing unnecessary overhead.

The solution is to create an identifier generator with a split personality. On the one hand, it behaves like an "assigned" generator when it detects a generated entity. On the other hand, it behaves like the entity's natural identifier generator when a new identifier is needed or when it's working with an existing persistent object. The result is a class that aggregates two generators and intelligently selects between the two.

Listing 6 is the source code for the GeneratedIdentifierGenerator class. The class includes only two methods (configure and generate), and understanding them both is important. The configure method creates the two identifier generators:

public void configure(Type type, Properties params, Dialect d) throws MappingException { String generatorName = params.getProperty(DELEGATE); ... delegateGenerator = IdentifierGeneratorFactory.create( generatorName, type, params, d); assignedGenerator = IdentifierGeneratorFactory.create( "assigned", type, params, d); }

An example Hibernate descriptor helps explain how this code works. The following descriptor snippet highlights the relevant portion for the sample User class. Notice that the generator tag names the new custom class while the original generator, a sequence in this case, has been moved to its "delegate" parameter:

<id name="id" type="long"> <generator class="GeneratedIdentifierGenerator"> <param name="delegate">sequence</param> <param name="sequence">user_id_seq</param> </generator> </id>

The configure method of the GeneratedIdentifierGenerator class creates an identifier generator of the type named in the "delegate" parameter, passing along any additional parameters it's received. The code also creates an instance of the "assigned" generator, a standard type supplied with Hibernate, that's used in the generate method:

public Serializable generate(SessionImplementor session, Object object) throws HibernateException { if (object instanceof GeneratedEntity) { return assignedGenerator.generate(session, object); } else { // Use the real generation strategy if no object's been // supplied or the supplied object isn't a generated entity return delegateGenerator.generate(session, object); } }

The generate method delegates to one of the two aggregated identifier generators. The assigned identifier generator is used for generated entities since their identifiers are assigned at creation. Otherwise, the entity's real generator, the delegate, is invoked to retrieve a new identifier. This strategy effectively supports persistent objects under all circumstances.

The Custom Interceptor

The solution described thus far will work with no additional infrastructure. However, you'll pay a slight performance penalty when persisting newly created objects. Most generators use a null identifier as their cue that an object is transient, but this doesn't work with generated entities whose identifiers are assigned before they're made persistent. It's a subtle nuance, but with an identifier already assigned, Hibernate has no way of knowing whether the object already exists in the database. It's forced to issue an additional query to find out.

To eliminate this overhead, you introduce a very minimal interceptor. Interceptors are used to customize Hibernate's behavior and are ideally suited to the task at hand.

Listing 7 contains the source for the GeneratedEntityInterceptor class. Its only purpose is to manage the transient property on generated entities. Remember, this property is provided by the GeneratedEntity interface that's implemented at runtime. The interceptor's isTransient method simply delegates to the generated entity:

public Boolean isTransient(Object entity) { if (entity instanceof GeneratedEntity) { return ((GeneratedEntity)entity).isTransient() ? Boolean.TRUE : Boolean.FALSE; } else { return super.isTransient(entity); } }

With the interceptor installed, Hibernate can now efficiently save generated entities using the appropriate INSERT or UPDATE statement.

Thanks for your registration, follow us on our social networks to keep up-to-date