This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit da832c7ae6ba89f02fc087d67c3c52de67648f6c Author: Walter Duque de Estrada <[email protected]> AuthorDate: Sun Feb 22 17:45:12 2026 -0600 CompileStatic HibernateMappingBuilder --- .../hibernate/HibernateMappingBuilder.groovy | 635 +++++++-------------- 1 file changed, 196 insertions(+), 439 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy index 048ccc2489..cb991a1439 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingBuilder.groovy @@ -5,11 +5,10 @@ * 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 + * 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. + * 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. */ @@ -22,14 +21,12 @@ import org.grails.datastore.mapping.reflect.ClassPropertyFetcher import org.hibernate.FetchMode import org.slf4j.Logger import org.slf4j.LoggerFactory - import jakarta.persistence.AccessType - import org.grails.orm.hibernate.cfg.CacheConfig +import org.grails.orm.hibernate.cfg.SortConfig import org.grails.orm.hibernate.cfg.ColumnConfig import org.grails.orm.hibernate.cfg.CompositeIdentity import org.grails.orm.hibernate.cfg.Identity -import org.grails.orm.hibernate.cfg.JoinTable import org.grails.orm.hibernate.cfg.Mapping import org.grails.orm.hibernate.cfg.NaturalId import org.grails.orm.hibernate.cfg.PropertyConfig @@ -42,8 +39,8 @@ import org.grails.orm.hibernate.cfg.PropertyDefinitionDelegate * @author Graeme Rocher * @since 1.0 */ - -class HibernateMappingBuilder implements MappingConfigurationBuilder<Mapping, PropertyConfig>{ +@CompileStatic +class HibernateMappingBuilder implements MappingConfigurationBuilder<Mapping, PropertyConfig> { private static final String INCLUDE_PARAM = 'include' private static final String EXCLUDE_PARAM = 'exclude' @@ -56,11 +53,6 @@ class HibernateMappingBuilder implements MappingConfigurationBuilder<Mapping, Pr private List<String> methodMissingExcludes = [] private List<String> methodMissingIncludes - /** - * Constructor for builder - * - * @param className The name of the class being mapped - */ HibernateMappingBuilder(String className) { this.className = className } @@ -76,121 +68,70 @@ class HibernateMappingBuilder implements MappingConfigurationBuilder<Mapping, Pr return mapping.columns } - /** - * Central entry point for the class. Passing a closure that defines a set of mappings will evaluate said mappings - * and populate the "mapping" property of this class which can then be obtained with getMappings() - * - * @param mappingClosure The closure that defines the ORM DSL - */ - @Override - @CompileStatic - Mapping evaluate(Closure mappingClosure, Object context = null) { + Mapping evaluate(@DelegatesTo(value = HibernateMappingBuilder, strategy = Closure.DELEGATE_ONLY) Closure mappingClosure, Object context = null) { if (mapping == null) { mapping = new Mapping() } mappingClosure.resolveStrategy = Closure.DELEGATE_ONLY mappingClosure.delegate = this try { - if(context != null) { + if (context != null) { mappingClosure.call(context) - } - else { + } else { mappingClosure.call() } - } - finally { + } finally { mappingClosure.delegate = null } mapping } - /** - * Include another config in this one - */ - @CompileStatic - void includes(Closure callable) { + + void includes(@DelegatesTo(value = HibernateMappingBuilder, strategy = Closure.DELEGATE_ONLY) Closure callable) { if (!callable) { return } - callable.resolveStrategy = Closure.DELEGATE_ONLY callable.delegate = this try { callable.call() - } - finally { + } finally { callable.delegate = null } } - @CompileStatic + void hibernateCustomUserType(Map args) { if (args.type && (args['class'] instanceof Class)) { - mapping.userTypes[args['class']] = args.type + mapping.userTypes[(Class)args['class']] = args.type.toString() } } - /** - * <p>Configures the table name. Example: - * <code> { table 'foo' } - * - * @param name The name of the table - */ - @CompileStatic void table(String name) { mapping.tableName = name } - /** - * <p>Configures the discriminator name. Example: - * <code> { discriminator 'foo' } - * - * @param name The name of the table - */ - @CompileStatic void discriminator(String name) { mapping.discriminator(name) } - /** - * <p>Configures the discriminator name. Example: - * <code> { discriminator value:'foo', column:'type' } - * - * @param name The name of the table - */ - @CompileStatic void discriminator(Map args) { mapping.discriminator(args) } - /** - * <p>Configures whether to auto import packages domain classes in HQL queries. Default is true - * <code> { autoImport false } - */ - @CompileStatic void autoImport(boolean b) { mapping.autoImport = b } - /** - * <p>Configures the table name. Example: - * <code> { table name:'foo', schema:'dbo', catalog:'CRM' } - */ - @CompileStatic void table(Map tableDef) { mapping.table.name = tableDef?.name?.toString() mapping.table.schema = tableDef?.schema?.toString() mapping.table.catalog = tableDef?.catalog?.toString() } - /** - * <p>Configures the default sort column. Example: - * <code> { sort 'foo' } - * - * @param name The name of the property to sort by - */ void sort(String name) { if (name) { - mapping.getSort().name = name + SortConfig sc = (SortConfig) mapping.getSort() + sc.name = name } } @@ -198,510 +139,326 @@ class HibernateMappingBuilder implements MappingConfigurationBuilder<Mapping, Pr mapping.autowire = autowire } - /** - * Whether to use dynamic update queries - */ - @CompileStatic void dynamicUpdate(boolean b) { mapping.dynamicUpdate = b } - /** - * Whether to use dynamic update queries - */ - @CompileStatic void dynamicInsert(boolean b) { mapping.dynamicInsert = b } - /** - * <p>Configures the default sort column. Example: - * <code> { sort foo:'desc' } - * - * @param namesAndDirections The names and directions of the property to sort by - */ void sort(Map namesAndDirections) { if (namesAndDirections) { - mapping.getSort().namesAndDirections = namesAndDirections + SortConfig sc = (SortConfig) mapping.getSort() + sc.namesAndDirections = (Map<String, String>)namesAndDirections } } - /** - * Configures the batch-size used for lazy loading - * @param num The batch size to use - */ - @CompileStatic void batchSize(Integer num) { if (num) { mapping.batchSize = num } } - /** - * <p>Configures the default sort direction. Example: - * <code> { order 'desc' } - * - * @param name The name of the property to sort by - */ void order(String direction) { if ("desc".equalsIgnoreCase(direction) || "asc".equalsIgnoreCase(direction)) { - mapping.getSort().direction = direction + SortConfig sc = (SortConfig) mapping.getSort() + sc.direction = direction } } - /** - * Set whether auto time stamping should occur for last_updated and date_created columns - */ - @CompileStatic void autoTimestamp(boolean b) { mapping.autoTimestamp = b } - /** - * <p>Configures whether to use versioning for optimistic locking - * <code> { version false } - * - * @param isVersioned True if a version property should be configured - */ - @CompileStatic void version(boolean isVersioned) { mapping.version(isVersioned) } - /** - * <p>Configures the name of the version column - * <code> { version 'foo' } - * - * @param isVersioned True if a version property should be configured - */ - @CompileStatic void version(String versionColumn) { mapping.version(versionColumn) } - /** - * Sets the tenant id - * - * @param tenantIdProperty The tenant id property - */ void tenantId(String tenantIdProperty) { mapping.tenantId(tenantIdProperty) } - /** - * <p>Configures the second-level cache for the class - * <code> { cache usage:'read-only', include:'all' } - * - * @param args Named arguments that contain the "usage" and/or "include" parameters - */ - @CompileStatic void cache(Map args) { - mapping.cache = new CacheConfig(enabled:true) + mapping.cache = new CacheConfig(enabled: true) if (args.usage) { - if (CacheConfig.USAGE_OPTIONS.contains(args.usage)) { - mapping.cache.usage = args.usage - } - else { - LOG.warn("ORM Mapping Invalid: Specified [usage] with value [$args.usage] of [cache] in class [$className] is not valid") + String usage = args.usage.toString() + if (CacheConfig.USAGE_OPTIONS.contains(usage)) { + mapping.cache.usage = usage + } else { + LOG.warn("ORM Mapping Invalid: Specified [usage] with value [$usage] of [cache] in class [$className] is not valid") } } if (args.include) { - if (CacheConfig.INCLUDE_OPTIONS.contains(args.include)) { - mapping.cache.include = args.include - } - else { - LOG.warn("ORM Mapping Invalid: Specified [include] with value [$args.include] of [cache] in class [$className] is not valid") + String include = args.include.toString() + if (CacheConfig.INCLUDE_OPTIONS.contains(include)) { + mapping.cache.include = include + } else { + LOG.warn("ORM Mapping Invalid: Specified [include] with value [$include] of [cache] in class [$className] is not valid") } } } - /** - * <p>Configures the second-level cache for the class - * <code> { cache 'read-only' } - * - * @param usage The usage type for the cache which is one of CacheConfig.USAGE_OPTIONS - */ - @CompileStatic void cache(String usage) { - cache(usage:usage) + cache(usage: usage) } - /** - * <p>Configures the second-level cache for the class - * <code> { cache 'read-only', include:'all } - * - * @param usage The usage type for the cache which is one of CacheConfig.USAGE_OPTIONS - */ - @CompileStatic void cache(String usage, Map args) { - args = args ? args : [:] - args.usage = usage - cache(args) + Map finalArgs = args ? new HashMap(args) : [:] + finalArgs.usage = usage + cache(finalArgs) } - /** - * If true the class and its sub classes will be mapped with table per hierarchy mapping - */ - @CompileStatic void tablePerHierarchy(boolean isTablePerHierarchy) { mapping.tablePerHierarchy = isTablePerHierarchy } - /** - * If true the class and its subclasses will be mapped with table per subclass mapping - */ - @CompileStatic void tablePerSubclass(boolean isTablePerSubClass) { mapping.tablePerHierarchy = !isTablePerSubClass } - /** - * If true the class and its subclasses will be mapped with table per subclass mapping - */ - @CompileStatic void tablePerConcreteClass(boolean isTablePerConcreteClass) { - if(isTablePerConcreteClass) { + if (isTablePerConcreteClass) { mapping.tablePerHierarchy = false mapping.tablePerConcreteClass = true } } - - /** - * <p>Configures the second-level cache with the default usage of 'read-write' and the default include of 'all' if - * the passed argument is true - * - * <code> { cache true } - * - * @param shouldCache True if the default cache configuration should be applied - */ - @CompileStatic void cache(boolean shouldCache) { - mapping.cache = new CacheConfig(enabled:shouldCache) + mapping.cache = new CacheConfig(enabled: shouldCache) } - /** - * <p>Configures the identity strategy for the mapping. Examples - * - * <code> - * { id generator:'sequence' } - * { id composite: ['one', 'two'] } - * </code> - * - * @param args The named arguments to the id method - */ void id(Map args) { if (args.composite) { - mapping.identity = new CompositeIdentity(propertyNames:args.composite as String[]) + mapping.identity = new CompositeIdentity(propertyNames: (String[]) args.composite) if (args.compositeClass) { - ((CompositeIdentity)mapping.identity).compositeClass = (Class)args.compositeClass + (mapping.identity as CompositeIdentity).compositeClass = (Class) args.compositeClass } - } - else { + } else { if (args?.generator) { - ((Identity)mapping.identity).generator = args.remove('generator').toString() + ((Identity) mapping.identity).generator = args.remove('generator').toString() } if (args?.name) { - ((Identity)mapping.identity).name = args.remove('name').toString() + ((Identity) mapping.identity).name = args.remove('name').toString() } if (args?.params) { - def params = (Map)args.remove('params') - for (entry in params) { - params[entry.key] = entry.value?.toString() - } - ((Identity)mapping.identity).params = params + Map params = (Map) args.remove('params') + Map<String, String> stringParams = [:] + params.each { k, v -> stringParams[k.toString()] = v?.toString() } + ((Identity) mapping.identity).params = stringParams } } if (args?.natural) { - def naturalArgs = args.remove('natural') - def propertyNames = naturalArgs instanceof Map ? ((Map)naturalArgs).remove('properties') : naturalArgs - + Object naturalArgs = args.remove('natural') + Object propertyNames = naturalArgs instanceof Map ? ((Map) naturalArgs).remove('properties') : naturalArgs if (propertyNames) { - def ni = new NaturalId() - ni.mutable = (naturalArgs instanceof Map) && ((Map)naturalArgs).mutable ?: false + NaturalId ni = new NaturalId() + ni.mutable = (naturalArgs instanceof Map) && ((Map) naturalArgs).mutable ?: false if (propertyNames instanceof List) { - ni.propertyNames = (List<String>)propertyNames - } - else { + ni.propertyNames = (List<String>) propertyNames + } else { ni.propertyNames = [propertyNames.toString()] } mapping.identity.natural = ni } } if (!args.composite && args) { - handleMethodMissing("id", [args] as Object[]) + handlePropertyInternal("id", args, null) } } /** - * A closure used by methodMissing to create column definitions + * Typed property method for CompileStatic support. */ - private Closure handleMethodMissing = { String name, Object args -> - if (args && ((args[0] instanceof Map) || (args[0] instanceof Closure))) { - Map namedArgs = args[0] instanceof Map ? args[0] : [:] - - def newConfig = new PropertyConfig() - if(defaultConstraints != null && namedArgs.containsKey('shared')) { - PropertyConfig sharedConstraints = mapping.columns.get(namedArgs.shared) - if(sharedConstraints != null) { - newConfig = (PropertyConfig)sharedConstraints.clone() - } - } - else if(mapping.columns.containsKey('*')) { - // apply global constraints constraints - PropertyConfig globalConstraints = mapping.columns.get('*') - if(globalConstraints != null) { - newConfig = (PropertyConfig)globalConstraints.clone() - } - } - + void property(Map args, String name) { + handlePropertyInternal(name, args, null) + } - PropertyConfig property = mapping.columns[name] ?: newConfig - property.name = namedArgs.name ?: property.name - property.generator = namedArgs.generator ?: property.generator - property.formula = namedArgs.formula ?: property.formula - property.accessType = namedArgs.accessType instanceof AccessType ? namedArgs.accessType : property.accessType - property.type = namedArgs.type ?: property.type - property.setLazy( namedArgs.lazy instanceof Boolean ? namedArgs.lazy : property.getLazy() ) - property.insertable = namedArgs.insertable != null ? namedArgs.insertable : property.insertable - property.updatable = namedArgs.updateable != null ? namedArgs.updateable : property.updatable - property.updatable = namedArgs.updatable != null ? namedArgs.updatable : property.updatable - property.cascade = namedArgs.cascade ?: property.cascade - property.cascadeValidate = namedArgs.cascadeValidate != null ? namedArgs.cascadeValidate : property.cascadeValidate - property.sort = namedArgs.sort ?: property.sort - property.order = namedArgs.order ?: property.order - property.batchSize = namedArgs.batchSize instanceof Integer ? namedArgs.batchSize : property.batchSize - property.ignoreNotFound = namedArgs.ignoreNotFound instanceof Boolean ? namedArgs.ignoreNotFound : property.ignoreNotFound - property.typeParams = namedArgs.params ?: property.typeParams - property.setUnique( namedArgs.unique ? namedArgs.unique : property.unique) - property.nullable = namedArgs.nullable instanceof Boolean ? namedArgs.nullable : property.nullable - property.maxSize = namedArgs.maxSize instanceof Number ? namedArgs.maxSize : property.maxSize - property.minSize = namedArgs.minSize instanceof Number ? namedArgs.minSize : property.minSize - if(namedArgs.size instanceof IntRange) { - property.size = (IntRange)namedArgs.size + /** + * Internal logic for building property configurations. + */ + protected void handlePropertyInternal(String name, Map namedArgs, Closure subClosure) { + PropertyConfig newConfig = new PropertyConfig() + if (defaultConstraints != null && namedArgs.containsKey('shared')) { + PropertyConfig sharedConstraints = mapping.columns.get(namedArgs.shared.toString()) + if (sharedConstraints != null) { + newConfig = (PropertyConfig) sharedConstraints.clone() } - property.max = namedArgs.max instanceof Comparable ? namedArgs.max : property.max - property.min = namedArgs.min instanceof Comparable ? namedArgs.min : property.min - property.range = namedArgs.range instanceof ObjectRange ? namedArgs.range : null - property.inList = namedArgs.inList instanceof List ? namedArgs.inList : property.inList - - // Need to guard around calling getScale() for multi-column properties (issue #1048) - if (namedArgs.scale instanceof Integer) { - property.scale = (Integer)namedArgs.scale + } else if (mapping.columns.containsKey('*')) { + PropertyConfig globalConstraints = mapping.columns.get('*') + if (globalConstraints != null) { + newConfig = (PropertyConfig) globalConstraints.clone() } + } - if (namedArgs.fetch) { - switch(namedArgs.fetch) { - case ~/(join|JOIN)/: - property.fetch = FetchMode.JOIN; break - case ~/(select|SELECT)/: - property.fetch = FetchMode.SELECT; break - default: - property.fetch = FetchMode.DEFAULT - } - } + PropertyConfig property = mapping.columns[name] ?: newConfig + property.name = namedArgs.name?.toString() ?: property.name + property.generator = namedArgs.generator?.toString() ?: property.generator + property.formula = namedArgs.formula?.toString() ?: property.formula + property.accessType = namedArgs.accessType instanceof AccessType ? (AccessType)namedArgs.accessType : property.accessType + property.type = namedArgs.type ?: property.type + property.setLazy(namedArgs.lazy instanceof Boolean ? (Boolean)namedArgs.lazy : property.getLazy()) + property.insertable = namedArgs.insertable instanceof Boolean ? (Boolean)namedArgs.insertable : property.insertable + property.updatable = (namedArgs.updateable != null ? namedArgs.updateable : namedArgs.updatable) instanceof Boolean ? (Boolean)(namedArgs.updateable ?: namedArgs.updatable) : property.updatable + property.cascade = namedArgs.cascade?.toString() ?: property.cascade + property.cascadeValidate = namedArgs.cascadeValidate instanceof Boolean ? (Boolean)namedArgs.cascadeValidate : property.cascadeValidate + property.sort = namedArgs.sort?.toString() ?: property.sort + property.order = namedArgs.order?.toString() ?: property.order + property.batchSize = namedArgs.batchSize instanceof Integer ? (Integer)namedArgs.batchSize : property.batchSize + property.ignoreNotFound = namedArgs.ignoreNotFound instanceof Boolean ? (Boolean)namedArgs.ignoreNotFound : property.ignoreNotFound + if (namedArgs.params instanceof Map) { + Properties typeProps = new Properties() + ((Map<Object, Object>)namedArgs.params).each { k, v -> typeProps.put(k, v) } + property.typeParams = typeProps + } + + if (namedArgs.unique instanceof Boolean) property.setUnique((boolean)(Boolean)namedArgs.unique) + else if (namedArgs.unique instanceof String) property.setUnique((String)namedArgs.unique) + else if (namedArgs.unique instanceof List) property.setUnique((List<String>)namedArgs.unique) + property.nullable = namedArgs.nullable instanceof Boolean ? (Boolean)namedArgs.nullable : property.nullable + property.maxSize = namedArgs.maxSize instanceof Number ? (Number)namedArgs.maxSize : property.maxSize + property.minSize = namedArgs.minSize instanceof Number ? (Number)namedArgs.minSize : property.minSize + + if (namedArgs.size instanceof IntRange) property.size = (IntRange) namedArgs.size + property.max = namedArgs.max instanceof Comparable ? (Comparable) namedArgs.max : property.max + property.min = namedArgs.min instanceof Comparable ? (Comparable) namedArgs.min : property.min + property.range = namedArgs.range instanceof ObjectRange ? (ObjectRange) namedArgs.range : null + property.inList = namedArgs.inList instanceof List ? (List) namedArgs.inList : property.inList + + if (namedArgs.scale instanceof Integer) property.scale = (Integer) namedArgs.scale + + if (namedArgs.fetch) { + String fetchStr = namedArgs.fetch.toString() + if (fetchStr.equalsIgnoreCase("join")) property.fetch = FetchMode.JOIN + else if (fetchStr.equalsIgnoreCase("select")) property.fetch = FetchMode.SELECT + else property.fetch = FetchMode.DEFAULT + } - // Deal with any column configuration for this property. - if (args[-1] instanceof Closure) { - // Multiple column definitions for this property. - Closure c = args[-1] - c.delegate = new PropertyDefinitionDelegate(property) - c.resolveStrategy = Closure.DELEGATE_ONLY - c.call() + if (subClosure != null) { + subClosure.delegate = new PropertyDefinitionDelegate(property) + subClosure.resolveStrategy = Closure.DELEGATE_ONLY + subClosure.call() + } else { + ColumnConfig cc = property.columns ? property.columns[0] : new ColumnConfig() + if (!property.columns) property.columns << cc + + if (namedArgs["column"]) cc.name = namedArgs["column"].toString() + if (namedArgs["sqlType"]) cc.sqlType = namedArgs["sqlType"].toString() + if (namedArgs["enumType"]) cc.enumType = namedArgs["enumType"].toString() + if (namedArgs["index"]) cc.index = namedArgs["index"].toString() + if (namedArgs["unique"]) cc.unique = namedArgs["unique"] + if (namedArgs["read"]) cc.read = namedArgs["read"].toString() + if (namedArgs["write"]) cc.write = namedArgs["write"].toString() + if (namedArgs.defaultValue) cc.defaultValue = namedArgs.defaultValue.toString() + if (namedArgs.comment) cc.comment = namedArgs.comment.toString() + if (namedArgs["length"] instanceof Integer) cc.length = (Integer)namedArgs["length"] + if (namedArgs["precision"] instanceof Integer) cc.precision = (Integer)namedArgs["precision"] + if (namedArgs["scale"] instanceof Integer) cc.scale = (Integer)namedArgs["scale"] + + if (namedArgs.joinTable instanceof String) { + property.joinTable((String)namedArgs.joinTable) + } else if (namedArgs.joinTable instanceof Map) { + property.joinTable((Map)namedArgs.joinTable) } - else { - // There is no sub-closure containing multiple column - // definitions, so pick up any column settings from - // the argument map. - ColumnConfig cc - if (property.columns) { - cc = property.columns[0] - } - else { - cc = new ColumnConfig() - property.columns << cc - } - if (namedArgs["column"]) cc.name = namedArgs["column"] - if (namedArgs["sqlType"]) cc.sqlType = namedArgs["sqlType"] - if (namedArgs["enumType"]) cc.enumType = namedArgs["enumType"] - if (namedArgs["index"]) cc.index = namedArgs["index"] - if (namedArgs["unique"]) cc.unique = namedArgs["unique"] - if (namedArgs["read"]) cc.read = namedArgs["read"] - if (namedArgs["write"]) cc.write = namedArgs["write"] - if (namedArgs.defaultValue) cc.defaultValue = namedArgs.defaultValue - if (namedArgs.comment) cc.comment = namedArgs.comment - cc.length = namedArgs["length"] ?: cc.length - cc.precision = namedArgs["precision"] ?: cc.precision - cc.scale = namedArgs["scale"] ?: cc.scale + if (namedArgs.indexColumn instanceof Map) { + Map icArgs = (Map)namedArgs.indexColumn + PropertyConfig ic = new PropertyConfig() + ColumnConfig icc = new ColumnConfig() + if (icArgs.name) icc.name = icArgs.name.toString() + if (icArgs.type) icc.sqlType = icArgs.type.toString() + if (icArgs.length instanceof Integer) icc.length = (Integer)icArgs.length + ic.columns << icc + ic.type = icArgs.type + property.indexColumn = ic } + } - if (namedArgs.cache instanceof String) { - CacheConfig cc = new CacheConfig() - if (CacheConfig.USAGE_OPTIONS.contains(namedArgs.cache)) { - cc.usage = namedArgs.cache - } - else { - LOG.warn("ORM Mapping Invalid: Specified [usage] of [cache] with value [$args.usage] for association [$name] in class [$className] is not valid") - } + // Cache association handling + if (namedArgs.cache != null) { + CacheConfig cc = new CacheConfig() + if (namedArgs.cache instanceof String && CacheConfig.USAGE_OPTIONS.contains(namedArgs.cache)) { + cc.usage = namedArgs.cache.toString() property.cache = cc - } - else if (namedArgs.cache == true) { - property.cache = new CacheConfig() - } - else if (namedArgs.cache instanceof Map) { - def cacheArgs = namedArgs.cache - CacheConfig cc = new CacheConfig() - if (CacheConfig.USAGE_OPTIONS.contains(cacheArgs.usage)) { - cc.usage = cacheArgs.usage - } - else { - LOG.warn("ORM Mapping Invalid: Specified [usage] of [cache] with value [$args.usage] for association [$name] in class [$className] is not valid") - } - if (CacheConfig.INCLUDE_OPTIONS.contains(cacheArgs.include)) { - cc.include = cacheArgs.include - } - else { - LOG.warn("ORM Mapping Invalid: Specified [include] of [cache] with value [$args.include] for association [$name] in class [$className] is not valid") - } + } else if (namedArgs.cache == true) { + property.cache = cc + } else if (namedArgs.cache instanceof Map) { + Map cacheArgs = (Map) namedArgs.cache + cc.usage = cacheArgs.usage?.toString() + cc.include = cacheArgs.include?.toString() property.cache = cc } - - if (namedArgs.indexColumn) { - def pc = new PropertyConfig() - property.indexColumn = pc - def cc = new ColumnConfig() - pc.columns << cc - def indexColArg = namedArgs.indexColumn - if (indexColArg instanceof Map) { - if (indexColArg.type) { - pc.type = indexColArg.remove('type') - } - bindArgumentsToColumnConfig(indexColArg, cc) - } - else { - cc.name = indexColArg.toString() - } - } - if (namedArgs.joinTable) { - def join = new JoinTable() - def joinArgs = namedArgs.joinTable - if (joinArgs instanceof String) { - join.name = joinArgs - } - else if (joinArgs instanceof Map) { - if (joinArgs.schema) join.schema = joinArgs.remove('schema') - if (joinArgs.catalog) join.catalog = joinArgs.remove('catalog') - if (joinArgs.name) join.name = joinArgs.remove('name') - if (joinArgs.key) { - join.key = new ColumnConfig(name:joinArgs.remove('key')) - } - if (joinArgs.column) { - ColumnConfig cc = new ColumnConfig(name: joinArgs.column) - join.column = cc - bindArgumentsToColumnConfig(joinArgs, cc) - } - } - property.joinTable = join - } - else if (namedArgs.containsKey('joinTable') && namedArgs.joinTable == false) { - property.joinTable = null - } - - mapping.columns[name] = property } - } - private bindArgumentsToColumnConfig(argMap, ColumnConfig cc) { - argMap.each { k, v -> - if (cc.metaClass.hasProperty(cc, k)) { - try { - cc."$k" = v - } - catch (Exception e) { - LOG.warn("Parameter [$k] cannot be used with [joinTable] argument") - } - } - } + mapping.columns[name] = property } - /** - * <p>Consumes the columns closure and populates the value into the Mapping objects columns property - * - * @param callable The closure containing the column definitions - */ - @CompileStatic - void columns(Closure callable) { + void columns(@DelegatesTo(value = Object, strategy = Closure.DELEGATE_ONLY) Closure callable) { callable.resolveStrategy = Closure.DELEGATE_ONLY callable.delegate = new Object() { def invokeMethod(String methodName, Object args) { - handleMethodMissing.call(methodName, args) + Object[] argsArray = (Object[]) args + Map namedArgs = (argsArray.length > 0 && argsArray[0] instanceof Map) ? (Map)argsArray[0] : [:] + Closure sub = (argsArray.length > 0 && argsArray[argsArray.length - 1] instanceof Closure) ? (Closure)argsArray[argsArray.length - 1] : null + handlePropertyInternal(methodName, namedArgs, sub) } } callable.call() } - @CompileStatic void datasource(String name) { mapping.datasources = [name] } - @CompileStatic void datasources(List<String> names) { mapping.datasources = names } - @CompileStatic void comment(String comment) { mapping.comment = comment } - void methodMissing(String name, Object args) { - if(methodMissingIncludes != null && !methodMissingIncludes.contains(name)) { - return - } - else if(methodMissingExcludes.contains(name)) { - return - } - - boolean hasArgs = args.asBoolean() - if ('user-type' == name && hasArgs && (args[0] instanceof Map)) { - hibernateCustomUserType(args[0]) - } - else if('importFrom' == name && hasArgs && (args[0] instanceof Class)) { - // ignore, handled by constraints - List<Closure> constraintsToImports = ClassPropertyFetcher.getStaticPropertyValuesFromInheritanceHierarchy((Class)args[0], GormProperties.CONSTRAINTS, Closure) - if(constraintsToImports) { - - List originalIncludes = this.methodMissingIncludes - List originalExludes = this.methodMissingExcludes + def methodMissing(String name, Object args) { + if (methodMissingIncludes != null && !methodMissingIncludes.contains(name)) return + if (methodMissingExcludes.contains(name)) return + + Object[] argsArray = (Object[]) args + boolean hasArgs = argsArray.length > 0 + if (name == 'user-type' && hasArgs && argsArray[0] instanceof Map) { + hibernateCustomUserType((Map) argsArray[0]) + } else if (name == 'importFrom' && hasArgs && argsArray[0] instanceof Class) { + List<Closure> constraintsToImport = ClassPropertyFetcher.getStaticPropertyValuesFromInheritanceHierarchy( + (Class) argsArray[0], GormProperties.CONSTRAINTS, Closure) + if (constraintsToImport) { + List<String> originalIncludes = this.methodMissingIncludes + List<String> originalExcludes = this.methodMissingExcludes try { - if(args[-1] instanceof Map) { - Map argMap = (Map) args[-1] - def includes = argMap.get(INCLUDE_PARAM) - def excludes = argMap.get(EXCLUDE_PARAM) - if(includes instanceof List) { - this.methodMissingIncludes = includes - } - if(excludes instanceof List) { - this.methodMissingExcludes = excludes - } + Object lastArg = argsArray[argsArray.length - 1] + if (lastArg instanceof Map) { + Map argMap = (Map) lastArg + Object includes = argMap.get(INCLUDE_PARAM) + Object excludes = argMap.get(EXCLUDE_PARAM) + if (includes instanceof List) this.methodMissingIncludes = (List<String>) includes + if (excludes instanceof List) this.methodMissingExcludes = (List<String>) excludes } - - for(Closure callable in constraintsToImports) { - callable.setDelegate(this) - callable.setResolveStrategy(Closure.DELEGATE_ONLY) + for (Closure callable in constraintsToImport) { + callable.delegate = this + callable.resolveStrategy = Closure.DELEGATE_ONLY callable.call() } } finally { this.methodMissingIncludes = originalIncludes - this.methodMissingExcludes = originalExludes + this.methodMissingExcludes = originalExcludes } } - } - else if (args && ((args[0] instanceof Map) || (args[0] instanceof Closure))) { - handleMethodMissing(name, args) + } else if (hasArgs && (argsArray[0] instanceof Map || argsArray[0] instanceof Closure)) { + Map namedArgs = argsArray[0] instanceof Map ? (Map)argsArray[0] : [:] + Closure sub = argsArray[argsArray.length - 1] instanceof Closure ? (Closure)argsArray[argsArray.length - 1] : null + handlePropertyInternal(name, namedArgs, sub) } } -} - +} \ No newline at end of file
