Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.click.extras.hibernate; import java.io.Serializable; import org.apache.click.control.Checkbox; import org.apache.click.control.Field; import org.apache.click.control.Form; import org.apache.click.control.HiddenField; import org.apache.click.util.ClickUtils; import org.apache.click.util.HtmlStringBuffer; import org.apache.commons.lang.StringUtils; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.metadata.ClassMetadata; import org.hibernate.type.Type; /** * Provides Hibernate data aware Form control: <form method='POST'>. * * <table class='htmlHeader' cellspacing='10'> * <tr> * <td> * * <table class='fields'> * <tr> * <td align='left'><b><label>First Name:</label></b></td> * <td align='left'><input type='text' name='name' value='' size='20' /></td> * </tr> * <tr> * <td align='left'><label>Middle Names:</label></td> * <td align='left'><input type='text' name='name' value='' size='20' /></td> * </tr> * <tr> * <td align='left'><b><label>Family Name:</label></b></td> * <td align='left'><input type='text' name='name' value='' size='20' /></td> * </tr> * </table> * <table class="buttons" align='right'> * <tr><td> * <input type='submit' name='ok' value=' OK '/> <input type='submit' name='cancel' value='Cancel'/> * </td></tr> * </table> * * </td> * </tr> * </table> * * <a href="http://www.hibernate.org/">Hibernate</a> is an Object Relational * Mapping (ORM) framework. The HibernateForm supports creating (inserting) and * saving (updating) POJO instances. This form will automatically apply the * given objects property required validation constraints to the form fields. * <p/> * The HibernateForm uses the thread local <tt>Session</tt> obtained via * <tt>SessionContext.getSession()</tt> for all object for persistence * operations. To use an alternative Session source override set the forms * getSession() method. * <p/> * The example below provides a <tt>User</tt> data object creation * and editing page. To edit an existing user object, the object is passed * to the page as a request parameter. Otherwise a new user object will * be created when {@link #saveChanges()} is called. * * <pre class="codeJava"> * <span class="kw">public class</span> UserEdit <span class="kw">extends</span> Page { * * <span class="kw">private</span> HibernateForm form = <span class="kw">new</span> HibernateForm(<span class="st">"form"</span>, User.<span class="kw">class</span>); * * <span class="kw">public</span> UserEdit() { * form.add(<span class="kw">new</span> TextField(<span class="st">"firstName"</span>)); * form.add(<span class="kw">new</span> TextField(<span class="st">"middleNames"</span>)); * form.add(<span class="kw">new</span> TextField(<span class="st">"FamilyName"</span>)); * * form.add(<span class="kw">new</span> Submit(<span class="st">"ok"</span>, <span class="st">" OK "</span>, <span class="kw">this</span>, <span class="st">"onOkClicked"</span>)); * form.add(<span class="kw">new</span> Submit(<span class="st">"cancel"</span>, <span class="kw">this</span>, <span class="st">"onCancelClicked"</span>)); * * form.setButtonAlign(<span class="st">"right"</span>); * form.setLabelRequiredPrefix(<span class="st">"<b>"</span>); * form.setLabelRequiredSuffix(<span class="st">"</b>"</span>); * addControl(form); * } * * <span class="kw">public void</span> setUser(User user) { * form.setValueObject(user); * } * * <span class="kw">public boolean</span> onOkClicked() { * <span class="kw">if</span> (form.isValid()) { * <span class="kw">if</span> (form.saveChanges()) { * setRedirect(<span class="st">"user-list.htm"</span>); * } * } * <span class="kw">return true</span>; * } * * <span class="kw">public boolean</span> onCancelClicked() { * setRedirect(<span class="st">"user-list.htm"</span>); * <span class="kw">return false</span>; * } * } </pre> * * @see SessionContext * @see SessionFilter */ public class HibernateForm extends Form { private static final long serialVersionUID = -7134198516606088333L; /** The form value object classname parameter name. */ protected static final String FO_CLASS = "FO_CLASS"; /** The form value object id parameter name. */ protected static final String FO_ID = "FO_ID"; // ----------------------------------------------------- Instance Variables /** The value object class name hidden field. */ protected HiddenField classField; /** The value object identifier hidden field. */ protected HiddenField oidField; /** The Hibernate session. */ protected Session session; /** The Hibernate session factory. */ protected SessionFactory sessionFactory; /** * The flag specifying that object validation meta data has been applied to * the form fields. */ protected boolean metaDataApplied = false; // ----------------------------------------------------------- Constructors /** * Create a new HibernateForm with the given form name and value object * class. * * @param name the form name * @param valueClass the value object class */ public HibernateForm(String name, Class<?> valueClass) { super(name); if (valueClass == null) { throw new IllegalArgumentException("Null valueClass parameter"); } classField = new HiddenField(FO_CLASS, String.class); classField.setValue(valueClass.getName()); add(classField); String classname = getClassname(valueClass); ClassMetadata classMetadata = getSessionFactory().getClassMetadata(classname); Type identifierType = classMetadata.getIdentifierType(); oidField = new HiddenField(FO_ID, identifierType.getReturnedClass()); add(oidField); } // --------------------------------------------------------- Public Methods /** * Return the form Hibernate <tt>Session</tt>. If form session is not * defined this method will obtain a session from the * {@link SessionContext}. * <p/> * Applications using alternative Hibernate <tt>Session</tt> sources should * set the form's session using the {@link #setSession(Session)} method. * * @return the form Hibernate session */ public Session getSession() { if (session == null) { session = SessionContext.getSession(); } return session; } /** * Set the user's Hibernate <tt>Session</tt>. * * @param session the user's Hibernate session */ public void setSession(Session session) { this.session = session; } /** * Return the application Hibernate <tt>SessionFactory</tt>. * If session factory is not defined this method will obtain the session * factory from the {@link SessionContext}. * <p/> * Applications using an alternative Hibernate <tt>SessionFactory</tt> * sources should set the form's session factory using the * {@link #setSessionFactory(SessionFactory)} method. * * @return the user's Hibernate session */ public SessionFactory getSessionFactory() { if (sessionFactory == null) { sessionFactory = SessionContext.getSessionFactory(); } return sessionFactory; } /** * Set the form Hibernate <tt>SessionFactory</tt>. * * @param sessionFactory the Hibernate SessionFactory */ public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } /** * Return a Hibernate value object from the form with the form field values * copied into the object's properties. * * @return the Hibernate object from the form with the form field values * applied to the object properties. */ public Object getValueObject() { if (StringUtils.isNotBlank(classField.getValue())) { try { Class<?> valueClass = ClickUtils.classForName(classField.getValue()); Serializable oid = (Serializable) oidField.getValueObject(); Object valueObject = null; if (oid != null) { valueObject = getSession().load(valueClass, oid); } else { valueObject = valueClass.newInstance(); } copyTo(valueObject); return valueObject; } catch (Exception e) { throw new RuntimeException(e); } } return null; } /** * Set the given Hibernate value object in the form, copying the object's * properties into the form field values. * * @param valueObject the Hibernate value object to set */ public void setValueObject(Object valueObject) { if (valueObject != null) { String classname = getClassname(valueObject.getClass()); ClassMetadata classMetadata = getSessionFactory().getClassMetadata(classname); Object identifier = classMetadata.getIdentifier(valueObject, EntityMode.POJO); oidField.setValueObject(identifier); copyFrom(valueObject); } } /** * Save or update the object to the database and return true. * If a <tt>HibernateException</tt> occurs the <tt>Transaction</tt> will be * rolled back the exception will be raised. * <p/> * If no object is added to the form using <tt>setValueObject()</tt> * then this method will: <ul> * <li>create a new instance of the Class</li> * <li>copy the form's field values to the objects properties</li> * <li>save the new object to the database</li> * </ul> * <p/> * If an existing persistent object is added to the form using * <tt>setValueObject()</tt> then this method will: <ul> * <li>load the persistent object record from the database</li> * <li>copy the form's field values to the objects properties</li> * <li>update the object in the database</li> * </ul> * * @return true if the object was saved or false otherwise * @throws HibernateException if a persistence error occurred */ public boolean saveChanges() throws HibernateException { Object valueObject = getValueObject(); Transaction transaction = null; try { Session session = getSession(); transaction = session.beginTransaction(); session.saveOrUpdate(valueObject); transaction.commit(); return true; } catch (HibernateException he) { if (transaction != null) { try { transaction.rollback(); } catch (HibernateException re) { // ignore } } throw he; } } /** * This method applies the object meta data to the form fields and then * invokes the <tt>super.onProcess()</tt> method. * * @see Form#onProcess() * * @return true to continue Page event processing or false otherwise */ @Override public boolean onProcess() { applyMetaData(); return super.onProcess(); } /** * Render the HTML representation of the HibernateForm. * <p/> * This method applies the object meta data to the form fields and then * invokes the <tt>super.toString()</tt> method. * * @see #toString() * * @param buffer the specified buffer to render the control's output to */ @Override public void render(HtmlStringBuffer buffer) { applyMetaData(); super.render(buffer); } // ------------------------------------------------------ Protected Methods /** * Applies the <tt>ClassMetadata</tt> validation database meta data to the * form fields. * <p/> * The field validation attributes include: * <ul> * <li>required - is a mandatory field and cannot be null</li> * </ul> */ protected void applyMetaData() { if (metaDataApplied) { return; } try { Class<?> valueClass = ClickUtils.classForName(classField.getValue()); String classname = getClassname(valueClass); ClassMetadata metadata = getSessionFactory().getClassMetadata(classname); String[] propertyNames = metadata.getPropertyNames(); boolean[] propertyNullability = metadata.getPropertyNullability(); for (int i = 0; i < propertyNames.length; i++) { Field field = getField(propertyNames[i]); if (field != null) { boolean isMandatory = !propertyNullability[i]; if (!field.isRequired() && isMandatory) { if (!(field instanceof Checkbox)) { field.setRequired(true); } } } } } catch (ClassNotFoundException cnfe) { throw new RuntimeException(cnfe); } metaDataApplied = true; } /** * Return the original classname for the given class removing any CGLib * proxy information. * * @param aClass the class to obtain the original name from * @return the original classname for the given class */ protected String getClassname(Class<?> aClass) { String classname = aClass.getName(); if (classname.indexOf("$$") != -1) { classname = classname.substring(0, classname.indexOf("$")); } return classname; } }