Tag Archives: Hibernate

Annotated constraints

We were wondering how to use Hibernate annotations to automatically create constraints (HTML “maxlength”) and validators (required fields, string length validation). After searching around, I found a wicket behavior for this in wicket-stuff from Ryan Sonnek. It was in wicket-stuff 1.3 and apparently had been removed from 1.4. Anyway, I patched it up a bit to make it work in wicket 1.4 and here it is (I also took the liberty of renaming it to AnnotatedConstraintBehavior in case we wanted to use it for annotations other than hibernate):

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.application.IComponentOnBeforeRenderListener;
import org.apache.wicket.behavior.AbstractBehavior;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.IPropertyReflectionAwareModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.validation.validator.StringValidator;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;

/**
 * Configure a Wicket Component based on Hibernate annotations (@NotNull and @Length(min=x,max=y)).
 *
 * Inspects the Model of a FormComponent and configures the Component according to the declared Hibernate Annotations used on the model object.
 * NOTE: This means the Component's Model mustbe known when configuring a Component.
 *
 * This object can be used as a Behavior to configure a single Component.
 * NOTE: this object is stateless, and the same instance can be reused to configure multiple Components.
 *
 * public class MyWebPage extends WebPage {
 * 	public MyWebPage() {
 *     TextField name = new TextField("id", new PropertyModel(user, "name");
 *     name.addBehavior(new AnnotatedConstraintBehavior());
 *     add(name);
 *   }
 * }
 *
 * This object can also be used as a component listener that will automatically configure all FormComponents based on Hibernate annotations. This ensures that an entire application respects annotations without adding custom Validators or Behaviors to each FormComponent.
 *
 * public class MyApplication extends WebApplication {
 * 	public void init() {
 * 		addPreComponentOnBeforeRenderListener(new AnnotatedConstraintBehavior());
 * 	}
 * }
 *
 * @see http://jroller.com/page/wireframe/?anchor= hibernateannotationcomponentconfigurator
 * @see http ://jroller.com/page/wireframe/?anchor=hibernate_annotations_and_wicket
 */
@SuppressWarnings("serial")
public class AnnotatedConstraintBehavior extends AbstractBehavior implements IComponentOnBeforeRenderListener {
	@SuppressWarnings("unchecked")
	private static Map configs = new HashMap() {
		{
			put(NotNull.class, new HibernateAnnotationConfig() {
				public void onAnnotatedComponent(Annotation annotation, FormComponent component) {
					component.setRequired(true);
				}
			});
			put(Length.class, new HibernateAnnotationConfig() {
				public void onAnnotatedComponent(Annotation annotation, FormComponent component) {
					int max = ((Length) annotation).max();
					component.add(new AttributeModifier("maxlength", true, new Model(Integer.toString(max))));
					component.add(StringValidator.maximumLength(max));
				}
			});
		}
	};

	@Override
	public final void bind(Component component) {
		super.bind(component);

		configure(component);
	}

	public final void onBeforeRender(Component component) {
		if (!component.hasBeenRendered()) {
			configure(component);
		}
	}

	void configure(Component component) {
		if (!isApplicableFor(component)) {
			return;
		}
		FormComponent formComponent = (FormComponent) component;
		IPropertyReflectionAwareModel propertyModel = (IPropertyReflectionAwareModel) component.getDefaultModel();
		for (Annotation annotation : getAnnotations(propertyModel)) {
			Class annotationType = annotation.annotationType();
			HibernateAnnotationConfig config = (HibernateAnnotationConfig) configs.get(annotationType);
			if (null != config) {
				config.onAnnotatedComponent(annotation, formComponent);
			}
		}
	}

	private Collection getAnnotations(IPropertyReflectionAwareModel propertyModel) {
		Field field = propertyModel.getPropertyField();
		if (field == null) {
			//Log.warn("Unable to find annotations for model: " + propertyModel);
			return Collections.emptyList();
		}
		return Arrays.asList(field.getAnnotations());
	}

	private boolean isApplicableFor(Component component) {
		if (!(component instanceof FormComponent)) {
			return false;
		}
		IModel model = component.getDefaultModel();
		if (null == model || !IPropertyReflectionAwareModel.class.isAssignableFrom(model.getClass())) {
			//Log.warn("No valid model is available for configuring Component: " + component);
			return false;
		}

		return true;
	}

	/**
	 * simple interface to abstract performing work for a specific annotation.
	 */
	private static interface HibernateAnnotationConfig {
		void onAnnotatedComponent(Annotation annotation, FormComponent component);
	}
}

To use it, you’ll need to have @Length(min=x,max=y) and/or @NotNull hibernate annotations on your objects. The behavior will find these in component models and react accordingly, adding the maxlength attributes to the markup, string validator and setting required.

Keeping your database schemas in sync with your model

Hibernate’s hbm2ddl.auto setting allows you to create, update, and validate a database schemas based on the mapping configuration. The create & update settings are great in development environments, since they will ensure your database schema will always be in sync with your model. We would not recommend these settings in an production environment because, there we need to have more fine grained control over database updates (hbm2ddl is too “magical” for production). Additionally, there may be updates to the data which cannot be handled by Hibernate’s hbm2ddl.auto implementation (e.g. content-related updates rather than structural).

Some people would say, “such database updates should not be done by the application, but rather by the database administrator”. However, for most applications, you want the code to maintain the database structure – otherwise you have terrible trouble keeping test and staging databases up to date.

To achieve this we “upgrade” our databases with our own implementation, using plain JDBC. A meta table in the schema holds a version number which gets checked during the application’s start up. If the version in the database does not match the code version, the application upgrades the database schema accordingly.

We need to have our own database upgrade execute before Hibernate intercepts. Otherwise Hibernate will complain that tables or columns which should be created by our upgrade procedure are not yet available.

This can be achieved by a bean, which implements the BeanFactoryPostProcessor.

package sample;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class DatabaseVersion implements BeanFactoryPostProcessor {

 public static void checkDatabaseStructure() {
  /*
   * This method creates a generic JDBC connection,
   * checks the database structure
   * and upgrades it if applicable.
   */
 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  DatabaseVersion.chekDatabaseStructure();
 }
}

All what’s missing now is the definition of the bean in the applicationContext.xml file.


That’s it. The application context auto-detects BeanFactoryPostProcessor beans in their bean definitions and applys them before any other beans get created. If you are concearned about the order of loading, your bean can implement the “Ordered” interface in addition.

Quartz, Spring & Hibernate/JPA

Establishing a hibernate session in a wicket application is done by means of the OpenSessionInViewFilter class, which binds a Hibernate Session to the thread for the entire processing of the request. You’ll find plenty of references by googling for Wicket and Hibernate.

If you need background jobs to be executed, Quartz is a fantastic library, providing scheduling with cron syntax. Quartz is trivial to integrate into your application (whether with Spring or without), but we had a hard time to figure out how to handle Hibernate sessions correctly. When you want to use one of your DAOs in a Quartz Job you are very likely to hit a “Session is closed!” exception from Hibernate. The reason for this is that the Quartz Job does not run within the context of the servlet, therefore nobody took care of establishing a Hibernate Session.

Below you’ll find a sample Quartz Job which shows how to set up the Hibernate session like OpenSessionInViewFilter does during a servlet request:

package sample;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class SampleJobBean extends QuartzJobBean {

  @Override
  protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    EntityManagerFactory emf = null;
    EntityManager em = null;
    try {
      ApplicationContext appContext = (ApplicationContext) jobExecutionContext.getScheduler().getContext().get("applicationContext");
      EntityManagerFactory emf = (EntityManagerFactory) appContext.getBean("entityManagerFactory", EntityManagerFactory.class);
      em = emf.createEntityManager();
      TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));

      /* Some action */

    } catch (Exception e) {
      //Exception handling..
      e.printStackTrace();
    }
    finally{
      if (em!=null) em.close();
    }
  }
}

Below you’ll find the relevant sections from the corresponding applicationContext.xml file.



  
    
  


  
    
  



  
    
      
    
  
  
    applicationContext
  




  

Thanks to Uwe Schäfer who helped us find this solution!