WebObjects/EOF/使用 EOF/EOGenerator
如果您曾经使用过 EOModeler 的 Java 源代码生成器,您就会知道当您对模型对象进行更改并必须在以后合并更改时,这将是多么令人痛苦。解决此问题的一种方法是使用 EOGenerator,这是由 Rubicode Software 开发的应用程序,它使用 Generation Gap 模式从 EOModels 中创建您的 Java 文件。EOGenerator 为每个实体生成两个 Java 文件,而不是一个。以 Person 实体为例。第一个 Java 文件是 _Person.java,它包含所有自动生成的 方法。第二个 Java 文件是 Person.java,而 Person 扩展了 _Person。第二个文件是您放置所有自定义内容的地方。每当您的模型发生更改时,只有您的 _Xxx.java 文件会被更新,而您的自定义内容则保持不变。此外,EOGenerator 允许为您的文件创建大量的自定义模板,这提供了将便利方法放置在您的 _Xxx.java 文件中的能力。
与 EOModeler 的默认 Java 文件生成和与 FileMerge 合并相比,使用 EOGenerator 有几个优点。
- EOGenerator 使用 Generation Gap 模式,它提供了自动生成代码与自定义代码之间更清晰的分隔,无需处理任何合并。FileMerge 中存在边界情况,可能会导致您处理令人讨厌的冲突。
- EOGenerator 使用 MiscMerge 语言作为其模板。这使您能够使用大量的自定义内容扩展核心模板(参见下面的 EOGenerator Mods 部分),更好地支持您自己的自定义开发流程和工作流程。
- 正如 David LaBer 所说,“所有酷炫的人都在使用它 - 我们都知道,看起来很酷是最重要的标准”。
Kieran Kelleher 在他的博客上写了一篇关于 EOGenerator 简介。
它实际上非常容易使用。快速入门是
- 从 Rubicode 网站下载并解压 EOGenerator
- 运行以下命令
eogenerator -model /path/to/model/YourModel.eomodeld -destination /path/to/source/folder -subclassDestination /path/to/source/folder -templatedir /path/to/EOGenerator/templates -java -packagedirs
瞧。EOGenerator 会为您吐出您的 Java 文件。让我们分解您可以传递的命令
- -define-EOGenericRecord <class>,允许您指定 _Person 类的超类。例如,如果您使用 Project Wonder,您将指定 -define-EOGenericRecord er.extensions.ERXGenericRecord
- -destination <path>,_Person.java 样式的 Java 文件将被生成到的文件夹(不可编辑的文件)
- -java,生成 Java 文件
- -javaTemplate <filename>,要在模板目录内使用的 Java 模板的名称(_Person)
- -model <path>,传递您想要为其生成 Java 文件的 .eomodeld 的路径。您实际上可以在命令行中包含多个 -model 命令
- -packagedirs,为 Java 文件中定义的任何包语句生成包目录(如果您不在实体上指定包名称则不需要。顺便说一句,您应该在实体上指定包 :) )
- -refmodel <path>,传递 .eomodeld 的路径,该路径是生成 Java 文件所必需的,但实际上不会为其生成 Java 文件。例如,您应该 -refmodel 任何原型,或者您所依赖的其他框架中的任何模型
- -subclassDestination <path>,Person.java 样式的 Java 文件将被生成到的文件夹(可编辑的文件)
- -subclassJavaTemplate <filename>,要在模板目录内使用的 Java 子类模板的名称(Person)
- -templatedir <path>,包含 EOGenerator 模板的文件夹的路径
- -verbose,开启详细输出
允许在一对一关系上设置 null(并将其变成删除)。请注意,Jonathan Rentzsch 的模板中也包含此内容。
public void save<$ToOneRelationship.name.initialCapitalString$>(<$ToOneRelationship.destinationEntity.referenceJavaClassName$> value) { if (value == null) { <$ToOneRelationship.destinationEntity.referenceJavaClassName$> object = <$ToOneRelationship.name$>(); if (object != null) removeObjectFromBothSidesOfRelationshipWithKey(object, "<$ToOneRelationship.name$>"); } else { addObjectToBothSidesOfRelationshipWithKey(value, "<$ToOneRelationship.name$>"); } }
返回当前 EO 与 EO 的最后提交版本之间的更改列表
public NSDictionary changedProperties() { NSDictionary commitedValues = editingContext().committedSnapshotForObject(this); return changesFromSnapshot(commitedValues); }
Jonathan Rentzsch 提供了他的基本 EOGenerator 模板,这些模板是必不可少的
http://rentzsch.com/share/eogenerator52templates.zip
所有属性和关系的常量。这允许在诸如 addObjecttoBothSidesOfRelationshipWithKey(myObject, Person.TO_MANY_Children) 这样的情况下进行编译时错误检查
<$foreach attribute classAttributes.@reversedArray do$> public static final String ATTRIBUTE_<$attribute.name$> = "<$attribute.name$>";<$endforeach do$>
<$foreach ToOneRelationship classToOneRelationships.@reversedArray do$> public static final String TO_ONE_<$ToOneRelationship.name$> = "<$ToOneRelationship.name$>";<$endforeach do$>
<$foreach ToManyRelationship classToManyRelationships.@reversedArray do$> public static final String TO_MANY_<$ToManyRelationship.name$> = "<$ToManyRelationship.name$>";<$endforeach do$>
我们也大量使用实体和属性级别的用户信息字典。允许生成自定义方法等等。一个例子是作为字符串存储在 DB 中的布尔值,其值是“true”和“false”。
<$if attribute.userInfo.usage == booleanFlag $> // boolean accessors public void <$attribute.userInfo.setterName$>(boolean newBoolean) { set<$attribute.name.initialCapitalString$>(newBoolean ? "true" : "false"); } public boolean <$attribute.userInfo.getterName$>() { return "true".equals(<$attribute.name$>()) ? true : false; } // validation public String validate<$attribute.name.initialCapitalString$>(String newValue) { if ( newValue == null ) { return "false"; } else if ( !newValue.equals("true") && !newValue.equals("false") ) { String errorMessage = MessageHandler.format("INVALID_BOOLEAN_FLAG <$classNameWithoutPackage$>.<$attribute.name$>", null); throw new NSValidation.ValidationException(errorMessage); } return newValue; } <$endif$>
添加一个代表实体名称的常量,以便您可以在提取中引用 Person.ENTITY_NAME 而不是字符串(允许在 Eclipse 中进行重构支持)
public static final String ENTITY_NAME = "<$name$>";
在您的 EO 中添加一个静态工厂方法(Person createPerson(...)),它向您展示了为您的实体配置了哪些必需的属性和关系(尝试提供一个替代的“构造函数”,因为 EO 构造函数是空的)
public static <$classNameWithoutPackage$> create<$classNameWithoutPackage$>(EOEditingContext _editingContext<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$>, <$Attribute.javaValueClassName$> _<$Attribute.name$><$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> _<$ToOneRelationship.name$><$endif$><$endforeach do$>) { <$classNameWithoutPackage$> eoObject = (<$classNameWithoutPackage$>)EOUtilities.createAndInsertInstance(_editingContext, <$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME);<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$> eoObject.set<$Attribute.name.initialCapitalString$>(_<$Attribute.name$>);<$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$> eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(_<$ToOneRelationship.name$>);<$endif$><$endforeach do$> return eoObject; }
这是一个稍微复杂一些(即更难)的版本,它还处理超类强制属性和字段(一级)。它跳过任何在子类的限制限定符中引用的属性(因为您可能将在 awakeFromInsertion 中设置它)
public static <$classNameWithoutPackage$> create<$classNameWithoutPackage$>(EOEditingContext editingContext<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$>, <$Attribute.javaValueClassName$> <$Attribute.name$><$endif$><$endforeach do$><$foreach Attribute parentEntity.classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$><$set RestrictingQualifierKey = false$><$foreach QualifierKey restrictingQualifier.allQualifierKeys do$><$if Attribute.name = QualifierKey$><$set RestrictingQualifierKey = true$><$endif$><$endforeach do$><$if RestrictingQualifierKey = false$>, <$Attribute.javaValueClassName$> <$Attribute.name$><$endif$><$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> <$ToOneRelationship.name$><$endif$><$endforeach do$><$foreach ToOneRelationship parentEntity.classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$>, <$ToOneRelationship.destinationEntity.referenceJavaClassName$> <$ToOneRelationship.name$><$endif$><$endforeach do$>) { <$classNameWithoutPackage$> eoObject = (<$classNameWithoutPackage$>)EOUtilities.createAndInsertInstance(editingContext, <$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME);<$foreach Attribute classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$> eoObject.set<$Attribute.name.initialCapitalString$>(<$Attribute.name$>);<$endif$><$endforeach do$><$foreach ToOneRelationship classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$> eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(<$ToOneRelationship.name$>);<$endif$><$endforeach do$><$foreach Attribute parentEntity.classAttributes.@sortedNameArray do$><$if !Attribute.allowsNull$><$set RestrictingQualifierKey = false$><$foreach QualifierKey restrictingQualifier.allQualifierKeys do$><$if Attribute.name = QualifierKey$><$set RestrictingQualifierKey = true$><$endif$><$endforeach do$><$if RestrictingQualifierKey = false$> eoObject.set<$Attribute.name.initialCapitalString$>(<$Attribute.name$>);<$endif$><$endif$><$endforeach do$><$foreach ToOneRelationship parentEntity.classToOneRelationships.@sortedNameArray do$><$if ToOneRelationship.isMandatory$> eoObject.set<$ToOneRelationship.name.initialCapitalString$>Relationship(<$ToOneRelationship.name$>);<$endif$><$endforeach do$> return eoObject; }
添加一堆便利提取方法(fetchAllPersons、fetchRequiredPerson 和其他变体)。它在复数化方面不智能,因此它只是在实体名称的末尾添加一个“s”
public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchAll<$classNameWithoutPackage$>s(_editingContext, null); }
public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext, NSArray _sortOrderings) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, null, _sortOrderings); }
public static NSArray fetch<$classNameWithoutPackage$>s(EOEditingContext _editingContext, EOQualifier _qualifier, NSArray _sortOrderings) { EOFetchSpecification fetchSpec = new EOFetchSpecification(<$GEN_PREFIX$><$classNameWithoutPackage$>.ENTITY_NAME, _qualifier, _sortOrderings); fetchSpec.setIsDeep(true); NSArray eoObjects = _editingContext.objectsWithFetchSpecification(fetchSpec); return eoObjects; }
public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value)); }
public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext _editingContext, EOQualifier _qualifier) { NSArray eoObjects = <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, _qualifier, null); <$classNameWithoutPackage$> eoObject; int count = eoObjects.count(); if (count == 0) { eoObject = null; } else if (count == 1) { eoObject = (<$classNameWithoutPackage$>)eoObjects.objectAtIndex(0); } else { throw new IllegalStateException("There was more than one <$classNameWithoutPackage$> that matched the qualifier '" + _qualifier + "'."); } return eoObject; } public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchRequired<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value)); }
public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext _editingContext, EOQualifier _qualifier) { <$classNameWithoutPackage$> eoObject = <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, _qualifier); if (eoObject == null) { throw new NoSuchElementException("There was no <$classNameWithoutPackage$> that matched the qualifier '" + _qualifier + "'."); } return eoObject; }
添加用于获取 EO 本地实例的方法。如果您有一个可能为 null 的 EO 的引用,则静态方法很有用(它首先执行 null 检查)
public <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext) { return (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, this); }
public static <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext, <$classNameWithoutPackage$> _eo) { return (_eo == null) ? null : (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, _eo); }
如果您曾经想要能够在您的 EO 上限定一个 toMany 关系,但有时您想使用提取规范来提取它们,有时您想在内存中进行过滤,那么您可以使用以下方法
<$if !ToManyRelationship.inverseRelationship$> public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier) { return <$ToManyRelationship.name$>(qualifier, null); } <$endif$> <$if ToManyRelationship.inverseRelationship$> public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier) { return <$ToManyRelationship.name$>(qualifier, null, false); } public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier, boolean fetch) { return <$ToManyRelationship.name$>(qualifier, null, fetch); } <$endif$> public NSArray <$ToManyRelationship.name$>(EOQualifier qualifier, NSArray sortOrderings<$if ToManyRelationship.inverseRelationship$>, boolean fetch<$endif$>) { NSArray results; <$if ToManyRelationship.inverseRelationship$> if (fetch) { EOQualifier fullQualifier; EOQualifier inverseQualifier = new EOKeyValueQualifier(<$ToManyRelationship.destination.className$>.<$ToManyRelationship.inverseRelationship.name.uppercaseUnderbarString$>_KEY, EOQualifier.QualifierOperatorEqual, this); if (qualifier == null) { fullQualifier = inverseQualifier; } else { NSMutableArray qualifiers = new NSMutableArray(); qualifiers.addObject(qualifier); qualifiers.addObject(inverseQualifier); fullQualifier = new EOAndQualifier(qualifiers); } results = <$ToManyRelationship.destination.className$>.fetch<$ToManyRelationship.destination.name$>s(editingContext(), fullQualifier, sortOrderings); } else { <$endif$> results = <$ToManyRelationship.name$>(); if (qualifier != null) { results = EOQualifier.filteredArrayWithQualifier(results, qualifier); } if (sortOrderings != null) { results = EOSortOrdering.sortedArrayUsingKeyOrderArray(results, sortOrderings); } <$if ToManyRelationship.inverseRelationship$> } <$endif$> return results; }
我想分享今天学到的一点很棒的知识。如果您使用的是 Java 1.5,您可以在 _EO 基类的模板中添加 @SuppressWarnings("all"),并消除烦人的编译器消息(通常是不必要的导入语句)。
@SuppressWarnings("all") public class _Invoice extends ERXGenericRecord { }
在您的 EOGenerator 模板中创建 awakeFromInsertion() 和 awakeFromFetch() 作为方法存根,该存根只调用 super() 并且有一个“在此处初始化您的对象...”的注释。您只需要在该位置添加代码,并且不可能忘记调用 super()。以下是一个示例
/** * Initialization of the instance while inserting it into an editing context */ public void awakeFromInsertion (EOEditingContext editingContext) { super.awakeFromInsertion (editingContext); // initialize your object here }
这来自我的 JavaSubclassSourceTemplate.eotemplate