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.grails.orm.hibernate.validation; import java.util.ArrayList; import java.util.Collections; import java.util.List; import grails.core.GrailsApplication; import grails.core.GrailsDomainClass; import grails.core.GrailsDomainClassProperty; import grails.util.GrailsClassUtils; import grails.validation.ConstrainedProperty; import groovy.lang.Closure; import org.grails.datastore.gorm.GormEnhancer; import org.grails.orm.hibernate.*; import org.codehaus.groovy.runtime.InvokerHelper; import org.grails.core.artefact.DomainClassArtefactHandler; import org.grails.core.exceptions.GrailsRuntimeException; import org.grails.core.lifecycle.ShutdownOperations; import org.hibernate.*; import org.hibernate.criterion.Restrictions; import org.hibernate.metadata.ClassMetadata; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; 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"; private static final String TARGET_DOMAIN_CLASS_ALIAS = "domain_"; public static final String UNIQUE_CONSTRAINT = "unique"; private boolean unique; private List<String> uniquenessGroup = new ArrayList<String>(); public UniqueConstraint() { ShutdownOperations.addOperation(new Runnable() { public void run() { ConstrainedProperty.removeConstraint(UNIQUE_CONSTRAINT, PersistentConstraintFactory.class); } }); } /** * @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; } 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 " + "domain classes and not custom user types or embedded instances"); } IHibernateTemplate hibernateTemplate = getHibernateTemplate(target); List<?> results = hibernateTemplate.execute(new Closure<List<?>>(this) { public List<?> call(Object... args) { Session session = (Session) args[0]; 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 = null; if (domainClass.getPersistentProperty(constraintPropertyName).isOneToOne()) { criteria = session.createCriteria(constraintClass, TARGET_DOMAIN_CLASS_ALIAS); String constraintPropertyAlias = constraintPropertyName + "_"; criteria.createAlias(TARGET_DOMAIN_CLASS_ALIAS + "." + constraintPropertyName, constraintPropertyAlias); GrailsDomainClassProperty property = domainClass.getPropertyByName(constraintPropertyName); ClassMetadata classMetadata = session.getSessionFactory() .getClassMetadata(property.getReferencedPropertyType()); String identifierPropertyName = classMetadata.getIdentifierPropertyName(); BeanWrapper bean = new BeanWrapperImpl(propertyValue); Object identifierPropertyValue = bean.getPropertyValue(identifierPropertyName); criteria.add(Restrictions.eq(constraintPropertyAlias + "." + identifierPropertyName, identifierPropertyValue)); } else { 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())) { // We are merely verifying that the object is not transient here shouldValidate = session.contains(uniquenessGroupPropertyValue); } 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; } }); 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 = { constraintPropertyName, constraintOwningClass, propertyValue }; rejectValue(target, errors, UNIQUE_CONSTRAINT, args, getDefaultMessage(DEFAULT_NOT_UNIQUE_MESSAGE_CODE)); } } public List<String> getUniquenessGroup() { return uniquenessGroup; } @Override public IHibernateTemplate getHibernateTemplate(Object target) { AbstractHibernateGormInstanceApi instanceApi = (AbstractHibernateGormInstanceApi) GormEnhancer .findInstanceApi(target.getClass()); sessionFactory = instanceApi.getSessionFactory(); AbstractHibernateDatastore app = (AbstractHibernateDatastore) instanceApi.getDatastore(); return app.getHibernateTemplate(AbstractHibernateDatastore.FlushMode.MANUAL.getLevel()); } }