Java tutorial
/* Copyright 2004-2005 Graeme Rocher * * Licensed 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.codehaus.groovy.grails.orm.hibernate.validation; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler; import org.codehaus.groovy.grails.commons.GrailsApplication; import org.codehaus.groovy.grails.commons.GrailsClassUtils; import org.codehaus.groovy.grails.commons.GrailsDomainClass; import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty; import org.codehaus.groovy.grails.exceptions.GrailsRuntimeException; import org.codehaus.groovy.runtime.InvokerHelper; import org.hibernate.Criteria; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.TransientObjectException; import org.hibernate.criterion.Restrictions; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.validation.Errors; /** * A constraint that validates the uniqueness of a property (will query the * database during validation process). * * @author Graeme Rocher * @author Sergey Nebolsin * @since 0.4 */ public class UniqueConstraint extends AbstractPersistentConstraint { private static final String DEFAULT_NOT_UNIQUE_MESSAGE_CODE = "default.not.unique.message"; public static final String UNIQUE_CONSTRAINT = "unique"; private boolean unique; private List<String> uniquenessGroup = new ArrayList<String>(); /** * @return Returns the unique. */ public boolean isUnique() { return unique; } /** * @return Whether the property is unique within a group */ public boolean isUniqueWithinGroup() { return !uniquenessGroup.isEmpty(); } /* (non-Javadoc) * @see org.codehaus.groovy.grails.validation.ConstrainedProperty.AbstractConstraint#setParameter(java.lang.Object) */ @Override public void setParameter(Object constraintParameter) { if (!(constraintParameter instanceof Boolean || constraintParameter instanceof String || constraintParameter instanceof CharSequence || constraintParameter instanceof List<?>)) { throw new IllegalArgumentException( "Parameter for constraint [" + UNIQUE_CONSTRAINT + "] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a boolean or string value"); } if (constraintParameter instanceof List<?>) { for (Object parameter : ((List<?>) constraintParameter)) { if (!(parameter instanceof String || parameter instanceof CharSequence)) { throw new IllegalArgumentException("Parameter for constraint [" + UNIQUE_CONSTRAINT + "] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a boolean or string value"); } uniquenessGroup.add(parameter.toString()); } } else if (constraintParameter instanceof String || constraintParameter instanceof CharSequence) { uniquenessGroup.add(constraintParameter.toString()); unique = true; } else { unique = ((Boolean) constraintParameter).booleanValue(); } if (!uniquenessGroup.isEmpty()) { unique = true; for (Object anUniquenessGroup : uniquenessGroup) { String propertyName = (String) anUniquenessGroup; if (GrailsClassUtils.getPropertyType(constraintOwningClass, propertyName) == null) { throw new IllegalArgumentException("Scope for constraint [" + UNIQUE_CONSTRAINT + "] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a valid property name of same class"); } } } super.setParameter(constraintParameter); } public String getName() { return UNIQUE_CONSTRAINT; } @Override protected void processValidate(final Object target, final Object propertyValue, Errors errors) { if (!unique) { return; } final Object id; try { id = InvokerHelper.invokeMethod(target, "ident", null); } catch (Exception e) { throw new GrailsRuntimeException("Target of [unique] constraints [" + target + "] is not a domain instance. Unique constraint can only be applied to to domain classes and not custom user types or embedded instances"); } HibernateTemplate hibernateTemplate = getHibernateTemplate(); if (hibernateTemplate == null) throw new IllegalStateException("Unable use [unique] constraint, no Hibernate SessionFactory found!"); List<?> results = hibernateTemplate.executeFind(new HibernateCallback<List<?>>() { public List<?> doInHibernate(Session session) throws HibernateException { session.setFlushMode(FlushMode.MANUAL); try { boolean shouldValidate = true; Class<?> constraintClass = constraintOwningClass; if (propertyValue != null && DomainClassArtefactHandler.isDomainClass(propertyValue.getClass())) { shouldValidate = session.contains(propertyValue); } if (shouldValidate) { GrailsApplication application = (GrailsApplication) applicationContext .getBean(GrailsApplication.APPLICATION_ID); GrailsDomainClass domainClass = (GrailsDomainClass) application .getArtefact(DomainClassArtefactHandler.TYPE, constraintClass.getName()); if (domainClass != null && !domainClass.isRoot()) { GrailsDomainClassProperty property = domainClass .getPropertyByName(constraintPropertyName); while (property.isInherited() && domainClass != null) { domainClass = (GrailsDomainClass) application.getArtefact( DomainClassArtefactHandler.TYPE, domainClass.getClazz().getSuperclass().getName()); if (domainClass != null) { property = domainClass.getPropertyByName(constraintPropertyName); } } constraintClass = domainClass != null ? domainClass.getClazz() : constraintClass; } Criteria criteria = session.createCriteria(constraintClass) .add(Restrictions.eq(constraintPropertyName, propertyValue)); if (uniquenessGroup != null) { for (Object anUniquenessGroup : uniquenessGroup) { String uniquenessGroupPropertyName = (String) anUniquenessGroup; Object uniquenessGroupPropertyValue = GrailsClassUtils .getPropertyOrStaticPropertyOrFieldValue(target, uniquenessGroupPropertyName); if (uniquenessGroupPropertyValue != null && DomainClassArtefactHandler .isDomainClass(uniquenessGroupPropertyValue.getClass())) { try { // We are merely verifying that the object is not transient here session.lock(uniquenessGroupPropertyValue, LockMode.NONE); } catch (TransientObjectException e) { shouldValidate = false; } } if (shouldValidate) { criteria.add(Restrictions.eq(uniquenessGroupPropertyName, uniquenessGroupPropertyValue)); } else { break; // we aren't validating, so no point continuing } } } if (shouldValidate) { return criteria.list(); } return Collections.EMPTY_LIST; } return Collections.EMPTY_LIST; } finally { session.setFlushMode(FlushMode.AUTO); } } }); if (results.isEmpty()) { return; } boolean reject = false; if (id != null) { Object existing = results.get(0); Object existingId = null; try { existingId = InvokerHelper.invokeMethod(existing, "ident", null); } catch (Exception e) { // result is not a domain class } if (!id.equals(existingId)) { reject = true; } } else { reject = true; } if (reject) { Object[] args = new Object[] { constraintPropertyName, constraintOwningClass, propertyValue }; rejectValue(target, errors, UNIQUE_CONSTRAINT, args, getDefaultMessage(DEFAULT_NOT_UNIQUE_MESSAGE_CODE)); } } public List<String> getUniquenessGroup() { return uniquenessGroup; } @SuppressWarnings("rawtypes") public boolean supports(Class type) { return true; } }