1 /* Copyright 2004-2005 the original author or authors. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package org.codehaus.groovy.grails.orm.hibernate; 16 17 import java.util.Collection; 18 import java.util.Collections; 19 import java.util.HashSet; 20 import java.util.LinkedHashMap; 21 import java.util.Map; 22 import java.util.Set; 23 24 import org.codehaus.groovy.grails.commons.AbstractGrailsClass; 25 import org.codehaus.groovy.grails.commons.ExternalGrailsDomainClass; 26 import org.codehaus.groovy.grails.commons.GrailsApplication; 27 import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty; 28 import org.codehaus.groovy.grails.commons.GrailsDomainConfigurationUtil; 29 import org.codehaus.groovy.grails.exceptions.InvalidPropertyException; 30 import org.codehaus.groovy.grails.validation.GrailsDomainClassValidator; 31 import org.hibernate.EntityMode; 32 import org.hibernate.MappingException; 33 import org.hibernate.SessionFactory; 34 import org.hibernate.engine.SessionFactoryImplementor; 35 import org.hibernate.metadata.ClassMetadata; 36 import org.hibernate.type.AnyType; 37 import org.hibernate.type.AssociationType; 38 import org.hibernate.type.Type; 39 import org.springframework.context.MessageSource; 40 import org.springframework.core.type.StandardAnnotationMetadata; 41 import org.springframework.validation.Validator; 42 43 /** 44 * An implementation of the GrailsDomainClass interface that allows Classes 45 * mapped in Hibernate to integrate with Grails' validation, dynamic methods 46 * etc. seamlessly. 47 * 48 * @author Graeme Rocher 49 * @since 0.1 50 */ 51 @SuppressWarnings("rawtypes") 52 public class GrailsHibernateDomainClass extends AbstractGrailsClass implements ExternalGrailsDomainClass { 53 54 private static final String HIBERNATE = "hibernate"; 55 56 private GrailsHibernateDomainClassProperty identifier; 57 private GrailsHibernateDomainClassProperty version; 58 59 private GrailsDomainClassProperty[] properties; 60 61 private Map<String, GrailsHibernateDomainClassProperty> propertyMap = new LinkedHashMap<String, GrailsHibernateDomainClassProperty>(); 62 63 private Validator validator; 64 65 private GrailsApplication application; 66 67 private Set subClasses = new HashSet(); 68 private Map constraints = Collections.emptyMap(); 69 private Map<String, Object> defaultConstraints = Collections.emptyMap(); 70 71 /** 72 * Contructor to be used by all child classes to create a new instance 73 * and get the name right. 74 * 75 * @param clazz the Grails class 76 * @param sessionFactory The Hibernate SessionFactory instance 77 * @param metaData The ClassMetaData for this class retrieved from the SF 78 * @param defaultConstraints The default global constraints definition 79 */ GrailsHibernateDomainClass(Class<?> clazz, SessionFactory sessionFactory, GrailsApplication application, ClassMetadata metaData, Map<String, Object> defaultConstraints)80 public GrailsHibernateDomainClass(Class<?> clazz, SessionFactory sessionFactory, GrailsApplication application, 81 ClassMetadata metaData, Map<String, Object> defaultConstraints) { 82 super(clazz, ""); 83 this.application = application; 84 85 new StandardAnnotationMetadata(clazz); 86 String ident = metaData.getIdentifierPropertyName(); 87 this.defaultConstraints = defaultConstraints; 88 if (ident != null) { 89 Class<?> identType = getPropertyType(ident); 90 identifier = new GrailsHibernateDomainClassProperty(this, ident); 91 identifier.setIdentity(true); 92 identifier.setType(identType); 93 propertyMap.put(ident, identifier); 94 } 95 96 // configure the version property 97 final int versionIndex = metaData.getVersionProperty(); 98 String versionPropertyName = null; 99 if (versionIndex >- 1) { 100 versionPropertyName = metaData.getPropertyNames()[versionIndex]; 101 version = new GrailsHibernateDomainClassProperty(this, versionPropertyName); 102 version.setType(getPropertyType(versionPropertyName)); 103 } 104 105 // configure remaining properties 106 String[] propertyNames = metaData.getPropertyNames(); 107 for (String propertyName : propertyNames) { 108 if (!propertyName.equals(ident) && !(versionPropertyName != null && 109 propertyName.equals(versionPropertyName))) { 110 GrailsHibernateDomainClassProperty prop = new GrailsHibernateDomainClassProperty(this, propertyName); 111 prop.setType(getPropertyType(propertyName)); 112 Type hibernateType = metaData.getPropertyType(propertyName); 113 114 // if its an association type 115 if (hibernateType.isAssociationType()) { 116 prop.setAssociation(true); 117 // get the associated type from the session factory and set it on the property 118 AssociationType assType = (AssociationType) hibernateType; 119 if (assType instanceof AnyType) { 120 continue; 121 } 122 try { 123 String associatedEntity = assType.getAssociatedEntityName((SessionFactoryImplementor) sessionFactory); 124 ClassMetadata associatedMetaData = sessionFactory.getClassMetadata(associatedEntity); 125 prop.setRelatedClassType(associatedMetaData.getMappedClass(EntityMode.POJO)); 126 } 127 catch (MappingException me) { 128 // other side must be a value object 129 if (hibernateType.isCollectionType()) { 130 prop.setRelatedClassType(Collection.class); 131 } 132 } 133 // configure type of relationship 134 if (hibernateType.isCollectionType()) { 135 prop.setOneToMany(true); 136 } 137 else if (hibernateType.isEntityType()) { 138 prop.setManyToOne(true); 139 // might not really be true, but for our purposes this is ok 140 prop.setOneToOne(true); 141 } 142 } 143 propertyMap.put(propertyName, prop); 144 } 145 } 146 147 properties = propertyMap.values().toArray(new GrailsDomainClassProperty[propertyMap.size()]); 148 // process the constraints 149 evaluateConstraints(); 150 } 151 152 /** 153 * Evaluates the constraints closure to build the list of constraints 154 * @param defaultContraints The default global constraints definition 155 */ evaluateConstraints()156 private void evaluateConstraints() { 157 Map existing = (Map) getPropertyOrStaticPropertyOrFieldValue(GrailsDomainClassProperty.CONSTRAINTS, Map.class); 158 if (existing == null) { 159 constraints = GrailsDomainConfigurationUtil.evaluateConstraints( 160 getClazz(), getProperties(), defaultConstraints); 161 } 162 else { 163 constraints = existing; 164 } 165 } 166 isOwningClass(Class domainClass)167 public boolean isOwningClass(Class domainClass) { 168 return false; 169 } 170 getProperties()171 public GrailsDomainClassProperty[] getProperties() { 172 return properties; 173 } 174 175 /** 176 * @deprecated 177 */ 178 @Deprecated getPersistantProperties()179 public GrailsDomainClassProperty[] getPersistantProperties() { 180 return properties; 181 } 182 getPersistentProperties()183 public GrailsDomainClassProperty[] getPersistentProperties() { 184 return properties; 185 } 186 getIdentifier()187 public GrailsDomainClassProperty getIdentifier() { 188 return identifier; 189 } 190 getVersion()191 public GrailsDomainClassProperty getVersion() { 192 return version; 193 } 194 getPropertyByName(String name)195 public GrailsDomainClassProperty getPropertyByName(String name) { 196 if (propertyMap.containsKey(name)) { 197 return propertyMap.get(name); 198 } 199 200 throw new InvalidPropertyException("No property found for name ["+name+"] for class ["+getClazz()+"]"); 201 } 202 getFieldName(String propertyName)203 public String getFieldName(String propertyName) { 204 return getPropertyByName(propertyName).getFieldName(); 205 } 206 hasSubClasses()207 public boolean hasSubClasses() { 208 return false; 209 } 210 getMappedBy()211 public Map getMappedBy() { 212 return Collections.emptyMap(); 213 } 214 hasPersistentProperty(String propertyName)215 public boolean hasPersistentProperty(String propertyName) { 216 for (GrailsDomainClassProperty persistantProperty : properties) { 217 if (persistantProperty.getName().equals(propertyName)) return true; 218 } 219 return false; 220 } 221 setMappingStrategy(String strategy)222 public void setMappingStrategy(String strategy) { 223 // do nothing, read-only 224 } 225 isOneToMany(String propertyName)226 public boolean isOneToMany(String propertyName) { 227 GrailsDomainClassProperty prop = getPropertyByName(propertyName); 228 return prop != null && prop.isOneToMany(); 229 } 230 isManyToOne(String propertyName)231 public boolean isManyToOne(String propertyName) { 232 GrailsDomainClassProperty prop = getPropertyByName(propertyName); 233 return prop != null && prop.isManyToOne(); 234 } 235 isBidirectional(String propertyName)236 public boolean isBidirectional(String propertyName) { 237 return false; 238 } 239 getRelatedClassType(String propertyName)240 public Class<?> getRelatedClassType(String propertyName) { 241 GrailsDomainClassProperty prop = getPropertyByName(propertyName); 242 if (prop == null) { 243 return null; 244 } 245 246 return prop.getReferencedPropertyType(); 247 } 248 getConstrainedProperties()249 public Map getConstrainedProperties() { 250 return constraints; 251 } 252 getValidator()253 public Validator getValidator() { 254 if (validator == null) { 255 GrailsDomainClassValidator gdcv = new GrailsDomainClassValidator(); 256 gdcv.setDomainClass(this); 257 MessageSource messageSource = application.getMainContext().getBean(MessageSource.class); 258 gdcv.setMessageSource(messageSource); 259 validator = gdcv; 260 } 261 return validator; 262 } 263 setValidator(Validator validator)264 public void setValidator(Validator validator) { 265 this.validator = validator; 266 } 267 getMappingStrategy()268 public String getMappingStrategy() { 269 return HIBERNATE; 270 } 271 272 @SuppressWarnings("unchecked") getSubClasses()273 public Set getSubClasses() { 274 return subClasses; 275 } 276 refreshConstraints()277 public void refreshConstraints() { 278 evaluateConstraints(); 279 } 280 isRoot()281 public boolean isRoot() { 282 return getClazz().getSuperclass().equals(Object.class); 283 } 284 getAssociationMap()285 public Map getAssociationMap() { 286 return Collections.emptyMap(); 287 } 288 } 289