Warning |
---|
This is deprecated information! |
Contents | ||||||
---|---|---|---|---|---|---|
|
If you've ever used EOModeler's Java source code generator, you know how much of a pain it can be when you make changes to your model objects and have to merge changes in later. One solution for this is to use EOGenerator, an application developed by Rubicode Software, which uses the Generation Gap pattern to create your Java files from your EOModels. EOGenerator produces TWO java files for each Entity rather than one. Take the example of a Person entity. The first java file is _Person.java, which contains all of the autogenerated methods. The second java file is Person.java, and Person extends _Person. The second file is where you place all of your customizations. Any time your model changes, only your _Xxx.java files are updated, and your customizations are left untouched. Additionally, EOGenerator allows for the creation of extensive custom templates for your files, which provides the ability to place convenience methods in your _Xxx.java files.
Warning |
---|
EOGenerator doesn't work on Mac OS X 10.5. You have to use JavaEOGenerator or Velocity EOGenerator. |
There are several advantages to using EOGenerator over EOModeler's default Java file generation and merging with FileMerge.
Kieran Kelleher has writtten an Introduction to EOGenerator on his blog.
It's actually very simple to use. The quick start is:
Panel |
---|
eogenerator -model /path/to/model/YourModel.eomodeld -destination /path/to/source/folder |
Voila. EOGenerator will spit out your Java files for you. Let's break down the commands you can pass in:
Allow setting nulls on a to-one relationship (and turn it into a remove). Note, this is also included in Jonathan Rentzsch's templates.
Code Block |
---|
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$>"); } } |
Return the list of changes between the current EO and the last committed version of the EO:
Code Block |
---|
public NSDictionary changedProperties() { NSDictionary commitedValues = editingContext().committedSnapshotForObject(this); return changesFromSnapshot(commitedValues); } |
Jonathan Rentzsch has provided his base EOGenerator templates, which are a must-have:
http://rentzsch.com/share/eogenerator52templates.zip
Constants for all attributes and relationships. This allows compile time error checking in situations like
addObjecttoBothSidesOfRelationshipWithKey(myObject, Person.TO_MANY_Children)
Code Block |
---|
<$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$> |
We also make heavy use of the user info dictionary on entity and attribute level. Allows to generate customized methods and what not. One example is booleans that are stored in the DB as strings with values "true" and "false".
Code Block |
---|
<$if attribute.userInfo.usage h1. 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$> |
Add a constant that represents the name of the entity so that you can refer to Person.ENTITY_NAME in fetches rather than the String (allows refactoring support in Eclipse):
Code Block |
---|
public static final String ENTITY_NAME = "<$name$>"; |
Add a static factory method to your EO's ( Person createPerson(...) ) that shows you what required attributes and relationships are configured for you entity (attempts to provide a replacement "constructor" since EO constructors are empty):
Code Block |
---|
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; } |
Here's a little bitty fancier (read: nastier) version that also handles superclass mandatory attributes and fields (one level). It skips any attribute that is referenced in the restricting qualifier of your subclass (since you are probably going to set that in your awakeFromInsertion):
Code Block |
---|
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; } |
Add a bunch of convience fetch methods (fetchAllPersons, fetchRequiredPerson, and other variants). It's not smart about pluralization, so it's just going to put an "s" on the end of the entity name:
Code Block |
---|
public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchAll<$classNameWithoutPackage$>s(_editingContext, null); } |
Code Block |
---|
public static NSArray fetchAll<$classNameWithoutPackage$>s(EOEditingContext _editingContext, NSArray _sortOrderings) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>s(_editingContext, null, _sortOrderings); } |
Code Block |
---|
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; } |
Code Block |
---|
public static <$classNameWithoutPackage$> fetch<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetch<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value)); } |
Code Block |
---|
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; } |
Code Block |
---|
public static <$classNameWithoutPackage$> fetchRequired<$classNameWithoutPackage$>(EOEditingContext _editingContext, String _keyName, Object _value) { return <$GEN_PREFIX$><$classNameWithoutPackage$>.fetchRequired<$classNameWithoutPackage$>(_editingContext, new EOKeyValueQualifier(_keyName, EOQualifier.QualifierOperatorEqual, _value)); } |
Code Block |
---|
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; } |
Add methods for getting local instances of EO's. The static one is handy if you have a reference to an EO that might be null (it does a null check first):
Code Block |
---|
public <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext) { return (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, this); } |
Code Block |
---|
public static <$classNameWithoutPackage$> localInstanceOf<$classNameWithoutPackage$>(EOEditingContext _editingContext, <$classNameWithoutPackage$> _eo) { return (_eo == null) ? null : (<$classNameWithoutPackage$>)EOUtilities.localInstanceOfObject(_editingContext, _eo); } |
If you've ever wanted to be able to qualify a toMany relationship on your EO's, but sometimes you want to fetch them w/ a fetch spec, and sometimes you want to filter in-memory, you can use the following:
Code Block |
---|
<$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; } |
I wanted to share a wonderful bit of knowledge I learned today. If you're using Java 1.5 you can add @SuppressWarnings("all") to the template for your _EO base classes and eliminate annoying compiler messages (usually uneeded import statements).
Code Block |
---|
@SuppressWarnings("all") public class _Invoice extends ERXGenericRecord { } |
Create awakeFromInsertion() and awakeFromFetch() in your EOGenerator template as a method stub that only calls super() and has a comment for "initialize your object here ...". You only have to put in the code at that place and can't possibly forget to call super(). Here is an example:
Code Block |
---|
/** * Initialization of the instance while inserting it into an editing context */ public void awakeFromInsertion (EOEditingContext editingContext) { super.awakeFromInsertion (editingContext); // initialize your object here } |
This is from my JavaSubclassSourceTemplate.eotemplate