Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java
===================================================================
--- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java	(revision 549199)
+++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ant/ReverseMappingToolTask.java	(working copy)
@@ -123,6 +123,14 @@
     }
 
     /**
+     * Set whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     */
+    public void setUseGenericCollections(boolean useGenericCollections) {
+        flags.useGenericCollections = useGenericCollections; 
+    }
+
+    /**
      * Set the SQL type map overrides.
      */
     public void setTypeMap(String typeMap) {
@@ -202,6 +210,22 @@
     }
 
     /**
+     * Whether to generate annotations along with generated code. Defaults
+     * to false.
+     */
+    public void setGenerateAnnotations(boolean genAnnotations) {
+        flags.generateAnnotations = genAnnotations;
+    }
+
+    /**
+     * Whether to use field or property-based access on generated code.
+     * Defaults to field-based access.
+     */
+    public void setAccessType(AccessType accessType) {
+        flags.accessType = accessType.getValue();
+    }
+    
+    /**
      * Set a customizer class to use.
      */
     public void setCustomizerClass(String customizerClass) {
@@ -255,7 +279,19 @@
             return new String[]{
                 "package",
                 "class",
+                "none"
             };
         }
     }
+
+    public static class AccessType
+        extends EnumeratedAttribute {
+
+        public String[] getValues() {
+            return new String[]{
+                "field",
+                "property"
+            };
+        }
+    }
 }
Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java
===================================================================
--- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java	(revision 549199)
+++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ReverseMappingTool.java	(working copy)
@@ -136,9 +136,16 @@
      */
     public static final int TABLE_SUBCLASS = 5;
 
+    public static final String LEVEL_NONE = "none";
     public static final String LEVEL_PACKAGE = "package";
     public static final String LEVEL_CLASS = "class";
 
+    /**
+     * Access type for generated source, defaults to field-based access.
+     */
+    public static final String ACCESS_TYPE_FIELD = "field";
+    public static final String ACCESS_TYPE_PROPERTY = "property";
+
     private static Localizer _loc = Localizer.forPackage
         (ReverseMappingTool.class);
 
@@ -176,6 +183,7 @@
     private SchemaGroup _schema = null;
     private boolean _nullAsObj = false;
     private boolean _blobAsObj = false;
+    private boolean _useGenericColl = false;
     private Properties _typeMap = null;
     private boolean _useFK = false;
     private boolean _useSchema = false;
@@ -186,6 +194,8 @@
     private String _idSuffix = "Id";
     private boolean _inverse = true;
     private boolean _detachable = false;
+    private boolean _genAnnotations = false;
+    private String _accessType = ACCESS_TYPE_FIELD;
     private CodeFormat _format = null;
     private ReverseCustomizer _custom = null;
     private String _discStrat = null;
@@ -196,6 +206,9 @@
     // mess up certain customizers (bug 881)
     private Set _abandonedFieldNames = null;
 
+    // generated annotations, key = metadata, val = list of annotations
+    private Map _annos = null;
+
     /**
      * Constructor. Supply configuration.
      */
@@ -327,6 +340,22 @@
     }
 
     /**
+     * Whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     */
+    public boolean getUseGenericCollections() {
+        return _useGenericColl;
+    }
+
+    /**
+     * Whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     */
+    public void setUseGenericCollections(boolean useGenericCollections) {
+        _useGenericColl = useGenericCollections; 
+    }
+
+    /**
      * Map of JDBC-name to Java-type-name entries that allows customization
      * of reverse mapping columns to field types.
      */
@@ -479,6 +508,39 @@
     }
 
     /**
+     * Whether to generate annotations along with generated code. Defaults
+     * to false.
+     */
+    public boolean getGenerateAnnotations() {
+        return _genAnnotations;
+    }
+
+    /**
+     * Whether to generate annotations along with generated code. Defaults
+     * to false.
+     */
+    public void setGenerateAnnotations(boolean genAnnotations) {
+        _genAnnotations = genAnnotations;
+    }
+
+    /**
+     * Whether to use field or property-based access on generated code.
+     * Defaults to field-based access.
+     */
+    public String getAccessType() {
+        return _accessType;
+    }
+
+    /**
+     * Whether to use field or property-based access on generated code.
+     * Defaults to field-based access.
+     */
+    public void setAccessType(String accessType) {
+        this._accessType = ACCESS_TYPE_PROPERTY.equalsIgnoreCase(accessType) ?
+            ACCESS_TYPE_PROPERTY : ACCESS_TYPE_FIELD;
+    }
+
+    /**
      * The code formatter for the generated Java code.
      */
     public CodeFormat getCodeFormat() {
@@ -784,7 +846,11 @@
                 _log.info(_loc.get("class-code", mappings[i]));
 
             ApplicationIdTool aid = newApplicationIdTool(mappings[i]);
-            gen = new ReverseCodeGenerator(mappings[i], aid);
+            if (getGenerateAnnotations())
+                gen = new AnnotatedCodeGenerator(mappings[i], aid);
+            else
+                gen = new ReverseCodeGenerator(mappings[i], aid);
+
             gen.generateCode();
 
             if (output == null) {
@@ -852,7 +918,32 @@
         return files;
     }
 
+    public void buildAnnotations() {
+        Map output = new HashMap();
+        // pretend mappings are all resolved
+        ClassMapping[] mappings = getMappings();
+        for (int i = 0; i < mappings.length; i++)
+            mappings[i].setResolve(MODE_META | MODE_MAPPING, true);
+
+        // store in user's configured IO
+        MetaDataFactory mdf = _conf.newMetaDataFactoryInstance();
+        mdf.setRepository(getRepository());
+        mdf.setStoreDirectory(_dir);
+        mdf.store(mappings, new QueryMetaData[0], new SequenceMetaData[0],
+            MODE_META | MODE_MAPPING | MODE_ANN_MAPPING, output);
+        _annos = output;
+    }
+
     /**
+     * Returns a list of stringified annotations for specified meta.
+     */
+    protected List getAnnotationsForMeta(Object meta) {
+        if (null == _annos)
+            return null;
+        return (List) _annos.get(meta);
+    }
+
+    /**
      * Generate and write the application identity code.
      */
     private ApplicationIdTool newApplicationIdTool(ClassMapping mapping) {
@@ -1619,6 +1710,7 @@
         tool.setUseForeignKeyName(getUseForeignKeyName());
         tool.setNullableAsObject(getNullableAsObject());
         tool.setBlobAsObject(getBlobAsObject());
+        tool.setUseGenericCollections(getUseGenericCollections());
         tool.setPrimaryKeyOnJoin(getPrimaryKeyOnJoin());
         tool.setUseDataStoreIdentity(getUseDataStoreIdentity());
         tool.setUseBuiltinIdentityClass(getUseBuiltinIdentityClass());
@@ -1626,6 +1718,7 @@
         tool.setIdentityClassSuffix(getIdentityClassSuffix());
         tool.setInverseRelations(getInverseRelations());
         tool.setDetachable(getDetachable());
+        tool.setGenerateAnnotations(getGenerateAnnotations());
         tool.setCustomizer(getCustomizer());
         tool.setCodeFormat(getCodeFormat());
         return tool;
@@ -1667,6 +1760,9 @@
      * type instead.</li>
      * <li><i>-blobAsObject/-bo &lt;true/t | false/f&gt;</i>: Set to true
      * to make all binary columns map to Object rather than byte[].</li>
+     * <li><i>-useGenericCollections/-gc &lt;true/t | false/f&gt;</i>: Set to
+     * true to use generic collections on OneToMany and ManyToMany relations
+     * (requires JDK 1.5 or higher).</li>
      * <li><i>-typeMap/-typ &lt;types&gt;</i>: Default mapping of SQL type
      * names to Java classes.</li>
      * <li><i>-primaryKeyOnJoin/-pkj &lt;true/t | false/f&gt;</i>: Set to true
@@ -1690,9 +1786,13 @@
      * discriminator strategy to place on base classes.</li>
      * <li><i>-versionStrategy/-vs &lt;strategy&gt;</i>: The default
      * version strategy to place on base classes.</li>
-     * <li><i>-metadata/-md &lt;class | package&gt;</i>: Specify the level the
-     * metadata should be generated at. Defaults to generating a
+     * <li><i>-metadata/-md &lt;class | package | none&gt;</i>: Specify the
+     * level the metadata should be generated at. Defaults to generating a
      * single package-level metadata file.</li>
+     * <li><i>-annotations/-ann &lt;true/t | false/f&gt;</i>: Set to true to
+     * generate JPA annotations in generated code.</li>
+     * <li><i>-accessType/-access &lt;field | property&gt;</i>: Change access
+     * type for generated annotations. Defaults to field access.</li>
      * <li><i>-customizerClass/-cc &lt;class name&gt;</i>: The full class
      * name of a {@link ReverseCustomizer} implementation to use to
      * customize the reverse mapping process. Optional.</li>
@@ -1749,6 +1849,8 @@
             ("nullableAsObject", "no", flags.nullableAsObject);
         flags.blobAsObject = opts.removeBooleanProperty
             ("blobAsObject", "bo", flags.blobAsObject);
+        flags.useGenericCollections = opts.removeBooleanProperty
+            ("useGenericCollections", "gc", flags.useGenericCollections);
         flags.primaryKeyOnJoin = opts.removeBooleanProperty
             ("primaryKeyOnJoin", "pkj", flags.primaryKeyOnJoin);
         flags.useDataStoreIdentity = opts.removeBooleanProperty
@@ -1768,7 +1870,11 @@
         flags.versionStrategy = opts.removeProperty
             ("versionStrategy", "vs", flags.versionStrategy);
         flags.metaDataLevel = opts.removeProperty
-            ("metadata", "md", flags.metaDataLevel);
+            ("metadata", "md", flags.metaDataLevel);        
+        flags.generateAnnotations = opts.removeBooleanProperty
+            ("annotations", "ann", flags.generateAnnotations);
+        flags.accessType = opts.removeProperty
+            ("accessType", "access", flags.accessType);
 
         String typeMap = opts.removeProperty("typeMap", "typ", null);
         if (typeMap != null)
@@ -1869,6 +1975,7 @@
         tool.setUseForeignKeyName(flags.useForeignKeyName);
         tool.setNullableAsObject(flags.nullableAsObject);
         tool.setBlobAsObject(flags.blobAsObject);
+        tool.setUseGenericCollections(flags.useGenericCollections);
         tool.setTypeMap(flags.typeMap);
         tool.setPrimaryKeyOnJoin(flags.primaryKeyOnJoin);
         tool.setUseDataStoreIdentity(flags.useDataStoreIdentity);
@@ -1877,16 +1984,24 @@
         tool.setIdentityClassSuffix(flags.identityClassSuffix);
         tool.setInverseRelations(flags.inverseRelations);
         tool.setDetachable(flags.detachable);
+        tool.setGenerateAnnotations(flags.generateAnnotations);
+        tool.setAccessType(flags.accessType);
         tool.setCustomizer(flags.customizer);
         tool.setCodeFormat(flags.format);
 
         // run
         log.info(_loc.get("revtool-map"));
         tool.run();
+        if (flags.generateAnnotations) {
+            log.info(_loc.get("revtool-gen-annos"));
+            tool.buildAnnotations();
+        }
         log.info(_loc.get("revtool-write-code"));
         tool.recordCode();
-        log.info(_loc.get("revtool-write-metadata"));
-        tool.recordMetaData(LEVEL_CLASS.equals(flags.metaDataLevel));
+        if (!LEVEL_NONE.equals(flags.metaDataLevel)) {
+            log.info(_loc.get("revtool-write-metadata"));
+            tool.recordMetaData(LEVEL_CLASS.equals(flags.metaDataLevel));
+        }
     }
 
     /**
@@ -1900,6 +2015,7 @@
         public boolean useForeignKeyName = false;
         public boolean nullableAsObject = false;
         public boolean blobAsObject = false;
+        public boolean useGenericCollections = false;
         public Properties typeMap = null;
         public boolean primaryKeyOnJoin = false;
         public boolean useDataStoreIdentity = false;
@@ -1908,6 +2024,8 @@
         public String identityClassSuffix = "Id";
         public boolean inverseRelations = true;
         public boolean detachable = false;
+        public boolean generateAnnotations = false;
+        public String accessType = ACCESS_TYPE_FIELD;
         public String metaDataLevel = LEVEL_PACKAGE;
         public String discriminatorStrategy = null;
         public String versionStrategy = null;
@@ -1983,8 +2101,8 @@
     private class ReverseCodeGenerator
         extends CodeGenerator {
 
-        private final ClassMapping _mapping;
-        private final ApplicationIdTool _appid;
+        protected final ClassMapping _mapping;
+        protected final ApplicationIdTool _appid;
 
         public ReverseCodeGenerator(ClassMapping mapping,
             ApplicationIdTool aid) {
@@ -2046,5 +2164,37 @@
                 return null;
             return _custom.getFieldCode((FieldMapping) field);
         }
+
+        protected boolean useGenericCollections() {
+            return _useGenericColl;
+        }
     }
+
+    private class AnnotatedCodeGenerator
+        extends ReverseCodeGenerator {
+
+        public AnnotatedCodeGenerator (ClassMapping mapping,
+            ApplicationIdTool aid) {
+            super (mapping, aid);
+        }
+
+        public Set getImportPackages() {
+            Set pkgs = super.getImportPackages();
+            pkgs.add("javax.persistence");
+            return pkgs;
+        }
+
+        protected List getClassAnnotations() {
+            return getAnnotationsForMeta(_mapping);
+        }
+
+        protected List getFieldAnnotations(FieldMetaData field) {
+            return getAnnotationsForMeta(field);
+        }
+
+        protected boolean usePropertyBasedAccess () {
+            return ACCESS_TYPE_PROPERTY.equals(_accessType);
+        }
+
+    }
 }
Index: openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties
===================================================================
--- openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties	(revision 549199)
+++ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties	(working copy)
@@ -359,6 +359,7 @@
 revtool-write-code: Writing generated class source code.
 revtool-write-appid: Writing generated application identity classes.
 revtool-write-metadata: Writing generated metadata.
+revtool-gen-annos: Generating annotations.
 revtool-usage: Usage: java org.apache.openjpa.jdbc.meta.ReverseMappingTool\n\
 	\t[-properties/-p <properties file or resource>]\n\
 	\t[-<property name> <property value>]*\n\
@@ -369,6 +370,7 @@
 	\t[-useForeignKeyName/-fkn <true/t | false/f>]\n\
 	\t[-nullableAsObject/-no <true/t | false/f>]\n\
 	\t[-blobAsObject/-bo <true/t | false/f>]\n\
+	\t[-useGenericCollections/-gc <true/t | false/f>]\n\
 	\t[-typeMap/-type <types>]\n\
 	\t[-primaryKeyOnJoin/-pkj <true/t | false/f>]\n\
 	\t[-useDatastoreIdentity/-ds <true/t | false/f>]\n\
@@ -379,7 +381,9 @@
 	\t[-detachable/-det <true/t | false/f>]\n\
 	\t[-discriminatorStrategy/-ds <strategy>]\n\
 	\t[-versionStrategy/-vs <strategy>]\n\
-	\t[-metadata/-md <package | class>]\n\
+	\t[-metadata/-md <package | class | none>]\n\
+	\t[-annotations/-ann <true/t | false/f>]\n\
+	\t[-accessType/-access <field | property>]\n\
 	\t[-customizerClass/-cc <full class name>]\n\
 	\t[-customizerProperties/-cp <properties file or resource>]\n\
 	\t[-customizer/-c.<property name> <property value>]*\n\
Index: openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java
===================================================================
--- openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java	(revision 549199)
+++ openjpa-lib/src/main/java/org/apache/openjpa/lib/util/CodeFormat.java	(working copy)
@@ -308,7 +308,23 @@
         return tabs.toString();
     }
 
+
     /**
+     * Returns parametrized type string for given type(s).
+     */
+    public String getParametrizedType(String[] typenames) {
+        StringBuffer buf = new StringBuffer ();
+        buf.append("<");
+        for (int i = 0; i < typenames.length; i++) {
+            if (i > 0)
+                buf.append(", ");
+            buf.append(typenames[i]);
+        }
+        buf.append(">");
+        return buf.toString();
+    }
+
+    /**
      * Return the field name for given suggested name, possibly adding
      * leading underscore.
      */
Index: openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java
===================================================================
--- openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java	(revision 549199)
+++ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/CodeGenerator.java	(working copy)
@@ -24,8 +24,10 @@
 import java.util.Iterator;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.List;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.WordUtils;
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.lib.util.CodeFormat;
 import org.apache.openjpa.lib.util.Files;
@@ -307,27 +309,47 @@
             propertyName = propertyName.substring(1);
         String fieldType = Strings.getClassName(fmd.getDeclaredType());
 
+        String keyType = null;
+        String elementType = null;
+        String paramType = "";
+        if (useGenericCollections()) {
+            if (fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION) {
+                Class elmCls = fmd.getElement().getDeclaredType();
+                elementType = Strings.getClassName(elmCls);
+                paramType = decs.getParametrizedType(
+                    new String[] {elementType});
+            } else if (fmd.getDeclaredTypeCode() == JavaTypes.MAP) {
+                Class keyCls = fmd.getKey().getDeclaredType();
+                Class elmCls = fmd.getElement().getDeclaredType();
+                keyType = Strings.getClassName(keyCls);
+                elementType = Strings.getClassName(elmCls);
+                paramType = decs.getParametrizedType(
+                    new String[] {keyType, elementType});
+            }
+        }
+
         String fieldValue = getInitialValue(fmd);
         if (fieldValue == null) {
             if ("Set".equals(fieldType))
-                fieldValue = "new HashSet" + decs.getParens();
+                fieldValue = "new HashSet" + paramType + decs.getParens();
             else if ("TreeSet".equals(fieldType))
-                fieldValue = "new TreeSet" + decs.getParens();
+                fieldValue = "new TreeSet" + paramType + decs.getParens();
             else if ("Collection".equals(fieldType))
-                fieldValue = "new ArrayList" + decs.getParens();
+                fieldValue = "new ArrayList" + paramType + decs.getParens();
             else if ("Map".equals(fieldType))
-                fieldValue = "new HashMap" + decs.getParens();
+                fieldValue = "new HashMap" + paramType + decs.getParens();
             else if ("TreeMap".equals(fieldType))
-                fieldValue = "new TreeMap" + decs.getParens();
-            else if (fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION
-                || fmd.getDeclaredTypeCode() == JavaTypes.MAP)
-                fieldValue = "new " + fieldType + decs.getParens();
+                fieldValue = "new TreeMap" + paramType + decs.getParens();
+            else if (fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION ||
+                fmd.getDeclaredTypeCode() == JavaTypes.MAP)
+                fieldValue = "new " + fieldType + paramType + decs.getParens();
             else
                 fieldValue = "";
         }
         if (fieldValue.length() > 0)
             fieldValue = " = " + fieldValue;
 
+        boolean fieldAccess = !usePropertyBasedAccess();
         String custom = getDeclaration(fmd);
         if (decs.length() > 0)
             decs.endl();
@@ -339,12 +361,18 @@
             templ.setParameter("capFieldName", capFieldName);
             templ.setParameter("propertyName", propertyName);
             templ.setParameter("fieldType", fieldType);
+            templ.setParameter("keyType", keyType);
+            templ.setParameter("elementType", elementType);
             templ.setParameter("fieldValue", fieldValue);
             decs.append(templ.toString());
         } else {
+            if (fieldAccess)
+                writeAnnotations(decs, getFieldAnnotations(fmd), 1);
             decs.tab().append("private ").append(fieldType).
-                append(" ").append(fieldName).append(fieldValue).
-                append(";");
+                append(paramType).append(" ").append(fieldName).
+                append(fieldValue).append(";");
+            if (fieldAccess)
+                decs.endl();
         }
 
         custom = getFieldCode(fmd);
@@ -357,11 +385,16 @@
             templ.setParameter("capFieldName", capFieldName);
             templ.setParameter("propertyName", propertyName);
             templ.setParameter("fieldType", fieldType);
+            templ.setParameter("keyType", keyType);
+            templ.setParameter("elementType", elementType);
             templ.setParameter("fieldValue", fieldValue);
             code.append(templ.toString());
         } else {
             // getter
-            code.tab().append("public ").append(fieldType).append(" ");
+            if (!fieldAccess)
+                writeAnnotations(code, getFieldAnnotations(fmd), 1);
+            code.tab().append("public ").append(fieldType).append(paramType).
+                 append(" ");
             if ("boolean".equalsIgnoreCase(fieldType))
                 code.append("is");
             else
@@ -374,8 +407,8 @@
 
             // setter
             code.tab().append("public void set").append(capFieldName);
-            code.openParen(true).append(fieldType).append(" ").
-                append(propertyName).closeParen();
+            code.openParen(true).append(fieldType).append(paramType).
+                append(" ").append(propertyName).closeParen();
             code.openBrace(2).endl();
             code.tab(2);
             if (propertyName.equals(fieldName))
@@ -403,6 +436,7 @@
             append(" * ").append(getClass().getName()).endl().
             append(" */").endl();
 
+        writeAnnotations(code, getClassAnnotations(), 0);
         code.append("public class ").append(className);
         if (extendsName.length() > 0)
             code.extendsDec(1).append(" ").append(extendsName);
@@ -427,6 +461,21 @@
     }
 
     /**
+     * Appends the given list of annotations to code buffer.
+     */
+    private void writeAnnotations (CodeFormat code, List ann,
+        int tabLevel) {
+        if (ann == null || ann.size() == 0)
+            return;
+        for (Iterator i = ann.iterator(); i.hasNext();) {
+            if (tabLevel > 0)
+                code.tab(tabLevel);
+            String s = (String) i.next();
+            code.append(s).endl();
+        }
+    }
+
+    /**
      * Append the opening code-level brace to the code; this can be
      * overridden to add code to the top of the class.
      */
@@ -503,6 +552,9 @@
      * <li>${capFieldName}: The capitalized field name.</li>
      * <li>${propertyName}: The field name without leading '_', if any.</li>
      * <li>${fieldType}: The field's type name.</li>
+     * <li>${keyType}: Key type name for maps, null otherwise.</li>
+     * <li>${elementType}: Element type name for collections, null otherwise.
+     * </li>
      * <li>${fieldValue}: The field's initial value, in the form
      * " = &lt;value&gt;", or empty string if none.</li>
      * </ul> Returns null by default.
@@ -521,6 +573,9 @@
      * <li>${capFieldName}: The capitalized field name.</li>
      * <li>${propertyName}: The field name without leading '_', if any.</li>
      * <li>${fieldType}: The field's type name.</li>
+     * <li>${keyType}: Key type name for maps, null otherwise.</li>
+     * <li>${elementType}: Element type name for collections, null otherwise.
+     * </li>
      * <li>${fieldValue}: The field's initial value, in the form
      * "= &lt;value&gt;", or empty string if none.</li>
      * </ul>
@@ -529,4 +584,37 @@
 	{
 		return null;
 	}
+
+    /**
+     * Whether to use property-based access on generated code.
+     * Defaults to false (field-based).
+     */    
+    protected boolean usePropertyBasedAccess () {
+        return false;
+    }
+
+    /**
+     * Return class-level annotations. Returns null by default.
+     */
+    protected List getClassAnnotations() {
+        return null;
+    }
+
+    /**
+     * Return field-level annotations. Returns null by default.
+     */
+    protected List getFieldAnnotations(FieldMetaData field) {
+        return null;
+    }
+
+    /**
+     * Whether to use generic collections on one-to-many and many-to-many
+     * relations instead of untyped collections.
+     *
+     * Override in descendants to change default behavior.
+     */
+    protected boolean useGenericCollections() {
+        return false;
+    }
+
 }
Index: openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java
===================================================================
--- openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java	(revision 549199)
+++ openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractCFMetaDataFactory.java	(working copy)
@@ -192,7 +192,10 @@
         Parser parser;
         if (mode != MODE_QUERY) {
             int sermode = (isMappingOnlyFactory()) ? mode : mode | MODE_META;
-            ser = newSerializer();
+            if ((mode & MODE_ANN_MAPPING) != 0)
+                ser = newAnnotationSerializer();
+            else
+                ser = newSerializer();
             ser.setMode(sermode);
             if (metaFiles != null) {
                 parser = newParser(false);
@@ -231,7 +234,10 @@
             for (int i = 0; !qFiles && i < queries.length; i++)
                 qFiles = queries[i].getSourceMode() == MODE_QUERY;
             if (qFiles) {
-                ser = newSerializer();
+                if ((mode & MODE_ANN_MAPPING) != 0)
+                    ser = newAnnotationSerializer();
+                else
+                    ser = newSerializer();
                 ser.setMode(MODE_QUERY);
                 if (queryFiles != null) {
                     parser = newParser(false);
@@ -544,6 +550,11 @@
     protected abstract Serializer newSerializer();
 
     /**
+     * Create a new annotation metadata serializer.
+     */
+    protected abstract Serializer newAnnotationSerializer();
+
+    /**
      * Return the metadata that defines the given query, if any.
      *
      * @param clsNames map of class names to metadatas
Index: openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java
===================================================================
--- openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java	(revision 549199)
+++ openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataModes.java	(working copy)
@@ -32,4 +32,5 @@
     public static final int MODE_MAPPING = 2;
     public static final int MODE_QUERY = 4;
     public static final int MODE_MAPPING_INIT = 8;
+    public static final int MODE_ANN_MAPPING = 16;
 }
Index: openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataFactory.java
===================================================================
--- openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataFactory.java	(revision 549199)
+++ openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataFactory.java	(working copy)
@@ -126,6 +126,15 @@
     }
 
     /**
+     * Create a new annotation serializer.
+     */
+    protected AnnotationPersistenceMetaDataSerializer
+        newAnnotationSerializer() {
+        return new AnnotationPersistenceMetaDataSerializer
+            (repos.getConfiguration());
+    }
+
+    /**
      * Return XML metadata parser, creating it if it does not already exist.
      */
     public XMLPersistenceMetaDataParser getXMLParser() {
Index: openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java
===================================================================
--- openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java	(revision 0)
+++ openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java	(revision 0)
@@ -0,0 +1,1487 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.openjpa.persistence;
+
+import org.apache.openjpa.lib.meta.SourceTracker;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.JavaVersions;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.conf.Configurations;
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.meta.*;
+import org.apache.openjpa.kernel.QueryLanguages;
+import org.apache.openjpa.util.InternalException;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.io.FileWriter;
+import java.lang.reflect.Member;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.annotation.Annotation;
+
+import serp.util.Strings;
+
+import javax.persistence.Entity;
+import javax.persistence.Embeddable;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.NamedNativeQuery;
+import javax.persistence.NamedQuery;
+import javax.persistence.QueryHint;
+import javax.persistence.SequenceGenerator;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Version;
+import javax.persistence.Transient;
+import javax.persistence.Basic;
+import javax.persistence.Embedded;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.ManyToMany;
+import javax.persistence.OrderBy;
+import javax.persistence.MapKey;
+import javax.persistence.AttributeOverride;
+import javax.persistence.CascadeType;
+import javax.persistence.FetchType;
+
+//@todo: javadocs
+
+/**
+ * Serializes persistence metadata as annotations.
+ * This class processes all object level tags that are store-agnostic.
+ * However, it provides hooks for the subclasses to include store-specific
+ * tags to be serialized both at &lt;entity-mappings&gt; and
+ * &lt;entity&gt; level.
+ *
+ * @since 1.0.0
+ * @author Steve Kim
+ * @author Gokhan Ergul
+ * @nojavadoc
+ */
+public class AnnotationPersistenceMetaDataSerializer
+    implements PersistenceMetaDataFactory.Serializer {
+
+    // NOTE: order is important! these constants must be maintained in
+    // serialization order. constants are spaced so that subclasses can
+    // slip tags in-between
+    protected static final int TYPE_SEQ = 10;
+    protected static final int TYPE_QUERY = 20;
+    protected static final int TYPE_META = 30;
+    protected static final int TYPE_CLASS_SEQS = 40;
+    protected static final int TYPE_CLASS_QUERIES = 50;
+
+    private static final Localizer _loc = Localizer.forPackage
+        (AnnotationPersistenceMetaDataSerializer.class);
+
+    private Log _log = null;
+    
+    private final OpenJPAConfiguration _conf;
+    private Map<String, ClassMetaData> _metas = null;
+    private Map<String, List> _queries = null;
+    private Map<String, List> _seqs = null;
+    private int _mode = MetaDataModes.MODE_NONE;
+    private SerializationComparator _comp = null;
+
+    private Map<ClassMetaData, List<AnnotationBuilder>> _clsAnnos = null;
+    private Map<FieldMetaData, List<AnnotationBuilder>> _fldAnnos = null;
+    private Map<SequenceMetaData, List<AnnotationBuilder>> _seqAnnos = null;
+    private Map<QueryMetaData, List<AnnotationBuilder>> _qryAnnos = null; 
+
+    /**
+     * Constructor. Supply configuration.
+     */
+    public AnnotationPersistenceMetaDataSerializer(OpenJPAConfiguration conf) {
+        _conf = conf;
+        setLog(conf.getLog(OpenJPAConfiguration.LOG_METADATA));
+        setMode(MetaDataModes.MODE_META | MetaDataModes.MODE_MAPPING |
+            MetaDataModes.MODE_QUERY);
+    }
+
+    /**
+     * Configuration.
+     */
+    public OpenJPAConfiguration getConfiguration() {
+        return _conf;
+    }
+
+    /**
+     * The log to write to.
+     */
+    public Log getLog() {
+        return _log;
+    }
+
+    /**
+     * The log to write to.
+     */
+    public void setLog(Log log) {
+        _log = log;
+    }
+    
+    /**
+     * The serialization mode according to the expected document type. The
+     * mode constants act as bit flags, and therefore can be combined.
+     */
+    public int getMode() {
+        return _mode;
+    }
+
+    /**
+     * The serialization mode according to the expected document type. The
+     * mode constants act as bit flags, and therefore can be combined.
+     */
+    public void setMode(int mode) {
+        _mode = mode;
+    }
+
+    /**
+     * The serialization mode according to the expected document type.
+     */
+    public void setMode(int mode, boolean on) {
+        if (mode == MetaDataModes.MODE_NONE)
+            setMode(MetaDataModes.MODE_NONE);
+        else if (on)
+            setMode(_mode | mode);
+        else
+            setMode(_mode & ~mode);
+    }
+
+    /**
+     * Convenience method for interpreting {@link #getMode}.
+     */
+    protected boolean isMetaDataMode() {
+        return (_mode & MetaDataModes.MODE_META) != 0;
+    }
+
+    /**
+     * Convenience method for interpreting {@link #getMode}.
+     */
+    protected boolean isQueryMode() {
+        return (_mode & MetaDataModes.MODE_QUERY) != 0;
+    }
+
+    /**
+     * Convenience method for interpreting {@link #getMode}.
+     */
+    protected boolean isMappingMode() {
+        return (_mode & MetaDataModes.MODE_MAPPING) != 0;
+    }
+
+    /**
+     * Convenience method for interpreting {@link #getMode}. Takes into
+     * account whether mapping information is loaded for the given instance.
+     */
+    protected boolean isMappingMode(ClassMetaData meta) {
+        return isMappingMode() && (meta.getSourceMode() & MetaDataModes.MODE_MAPPING) != 0
+            && (meta.getEmbeddingMetaData() != null
+            || !meta.isEmbeddedOnly())
+            && (meta.getEmbeddingMetaData() == null
+            || isMappingMode(meta.getEmbeddingMetaData()));
+    }
+
+    /**
+     * Convenience method for interpreting {@link #getMode}. Takes into
+     * account whether mapping information is loaded for the given instance.
+     */
+    protected boolean isMappingMode(ValueMetaData vmd) {
+        return isMappingMode(vmd.getFieldMetaData().getDefiningMetaData());
+    }
+
+    /**
+     * Add a class meta data to the set to be serialized.
+     */
+    public void addMetaData(ClassMetaData meta) {
+        if (meta == null)
+            return;
+
+        if (_metas == null)
+            _metas = new HashMap<String, ClassMetaData>();
+        _metas.put(meta.getDescribedType().getName(), meta);
+    }
+
+    /**
+     * Add a sequence meta data to the set to be serialized.
+     */
+    public void addSequenceMetaData(SequenceMetaData meta) {
+        if (meta == null)
+            return;
+
+        List seqs = null;
+        String defName = null;
+        if (meta.getSourceScope() instanceof Class)
+            defName = ((Class) meta.getSourceScope()).getName();
+        if (_seqs == null)
+            _seqs = new HashMap<String, List>();
+        else
+            seqs = _seqs.get(defName);
+
+        if (seqs == null) {
+            seqs = new ArrayList(3); // don't expect many seqs / class
+            seqs.add(meta);
+            _seqs.put(defName, seqs);
+        } else if (!seqs.contains(meta))
+            seqs.add(meta);
+    }
+
+    /**
+     * Add a query meta data to the set to be serialized.
+     */
+    public void addQueryMetaData(QueryMetaData meta) {
+        if (meta == null)
+            return;
+
+        List queries = null;
+        String defName = null;
+        if (meta.getSourceScope() instanceof Class)
+            defName = ((Class) meta.getSourceScope()).getName();
+        if (_queries == null)
+            _queries = new HashMap<String, List>();
+        else
+            queries = _queries.get(defName);
+
+        if (queries == null) {
+            queries = new ArrayList(3); // don't expect many queries / class
+            queries.add(meta);
+            _queries.put(defName, queries);
+        } else if (!queries.contains(meta))
+            queries.add(meta);
+    }
+
+    /**
+     * Add all components in the given repository to the set to be serialized.
+     */
+    public void addAll(MetaDataRepository repos) {
+        if (repos == null)
+            return;
+
+        for (ClassMetaData meta : repos.getMetaDatas())
+            addMetaData(meta);
+        for (SequenceMetaData seq : repos.getSequenceMetaDatas())
+            addSequenceMetaData(seq);
+        for (QueryMetaData query : repos.getQueryMetaDatas())
+            addQueryMetaData(query);
+    }
+
+    /**
+     * Remove a metadata from the set to be serialized.
+     *
+     * @return true if removed, false if not in set
+     */
+    public boolean removeMetaData(ClassMetaData meta) {
+        return _metas != null && meta != null
+            && _metas.remove(meta.getDescribedType().getName()) != null;
+    }
+
+    /**
+     * Remove a sequence metadata from the set to be serialized.
+     *
+     * @return true if removed, false if not in set
+     */
+    public boolean removeSequenceMetaData(SequenceMetaData meta) {
+        if (_seqs == null || meta == null)
+            return false;
+        String defName = null;
+        if (meta.getSourceScope() instanceof Class)
+            defName = ((Class) meta.getSourceScope()).getName();
+        List seqs = _seqs.get(defName);
+        if (seqs == null)
+            return false;
+        if (!seqs.remove(meta))
+            return false;
+        if (seqs.isEmpty())
+            _seqs.remove(defName);
+        return true;
+    }
+
+    /**
+     * Remove a query metadata from the set to be serialized.
+     *
+     * @return true if removed, false if not in set
+     */
+    public boolean removeQueryMetaData(QueryMetaData meta) {
+        if (_queries == null || meta == null)
+            return false;
+        String defName = null;
+        if (meta.getSourceScope() instanceof Class)
+            defName = ((Class) meta.getSourceScope()).getName();
+        List queries = _queries.get(defName);
+        if (queries == null)
+            return false;
+        if (!queries.remove(meta))
+            return false;
+        if (queries.isEmpty())
+            _queries.remove(defName);
+        return true;
+    }
+
+    /**
+     * Remove all the components in the given repository from the set to be
+     * serialized.
+     *
+     * @return true if any components removed, false if none in set
+     */
+    public boolean removeAll(MetaDataRepository repos) {
+        if (repos == null)
+            return false;
+
+        boolean removed = false;
+        ClassMetaData[] metas = repos.getMetaDatas();
+        for (int i = 0; i < metas.length; i++)
+            removed |= removeMetaData(metas[i]);
+        SequenceMetaData[] seqs = repos.getSequenceMetaDatas();
+        for (int i = 0; i < seqs.length; i++)
+            removed |= removeSequenceMetaData(seqs[i]);
+        QueryMetaData[] queries = repos.getQueryMetaDatas();
+        for (int i = 0; i < queries.length; i++)
+            removed |= removeQueryMetaData(queries[i]);
+        return removed;
+    }
+
+    /**
+     * Clear the set of metadatas to be serialized.
+     */
+    public void clear() {
+        if (_metas != null)
+            _metas.clear();
+        if (_seqs != null)
+            _seqs.clear();
+        if (_queries != null)
+            _queries.clear();
+    }
+
+    /**
+     * Add system-level mapping elements to be serialized. Does nothing
+     * by default.
+     */
+    protected void addSystemMappingElements(Collection toSerialize) {
+    }
+
+    /**
+     * Sort the given collection of objects to be serialized.
+     */
+    private void serializationSort(List objs) {
+        if (objs == null || objs.isEmpty())
+            return;
+        if (_comp == null)
+            _comp = newSerializationComparator();
+        Collections.sort(objs, _comp);
+    }
+
+    /**
+     * Create a new comparator for ordering objects that are to be serialized.
+     */
+    protected SerializationComparator newSerializationComparator() {
+        return _comp;
+    }
+
+    /**
+     * Add sequence metadata to the given metadatas collection.
+     */
+    private void addSequenceMetaDatas(Collection all) {
+        if (_seqs == null)
+            return;
+
+        for (Map.Entry entry : _seqs.entrySet()) {
+            if (entry.getKey() == null)
+                all.addAll((List) entry.getValue());
+            else if (_metas == null || !_metas.containsKey(entry.getKey()))
+                all.add(new ClassSeqs((List<SequenceMetaData>)
+                    entry.getValue()));
+        }
+    }
+
+    /**
+     * Add query metadata to the given metadatas collection.
+     */
+    private void addQueryMetaDatas(Collection all) {
+        if (_queries == null)
+            return;
+
+        for (Map.Entry entry : _queries.entrySet()) {
+            if (entry.getKey() == null)
+                all.addAll((List) entry.getValue());
+            else if (_mode == MetaDataModes.MODE_QUERY || _metas == null
+                || !_metas.containsKey(entry.getKey()))
+                all.add(new ClassQueries((List<QueryMetaData>)
+                    entry.getValue()));
+        }
+    }
+
+    /**
+     * Creates a new annotation builder for the specified annotation type.
+     * @return
+     */
+    protected AnnotationBuilder newAnnotationBuilder(
+        Class<? extends Annotation> annType) {
+        return new AnnotationBuilder(annType);        
+    }
+
+    protected void addAnnotation(AnnotationBuilder ab, Object meta) {
+        if (meta instanceof ClassMetaData)
+            addAnnotation(ab, (ClassMetaData) meta);
+        else if (meta instanceof FieldMetaData)
+            addAnnotation(ab, (FieldMetaData) meta);
+        else if (meta instanceof SequenceMetaData)
+            addAnnotation(ab, (SequenceMetaData) meta);
+        else if (meta instanceof QueryMetaData)
+            addAnnotation(ab, (QueryMetaData) meta);
+    }
+
+    /**
+     * Add an annotation builder to list of builders for the specified
+     * class metadata.
+     */
+    protected void addAnnotation(AnnotationBuilder ab, ClassMetaData meta) {
+        if (_clsAnnos == null)
+            _clsAnnos = new HashMap<ClassMetaData, List<AnnotationBuilder>>();
+        List<AnnotationBuilder> list = _clsAnnos.get(meta);
+        if (list == null) {
+            list = new ArrayList<AnnotationBuilder>();
+            _clsAnnos.put(meta, list);
+        }
+        list.add(ab);        
+    }
+
+    /**
+     * Add an annotation builder to list of builders for the specified
+     * field metadata.
+     */
+    protected void addAnnotation(AnnotationBuilder ab, FieldMetaData meta) {
+        if (_fldAnnos == null)
+            _fldAnnos = new HashMap<FieldMetaData, List<AnnotationBuilder>>();
+        List<AnnotationBuilder> list = _fldAnnos.get(meta);
+        if (list == null) {
+            list = new ArrayList<AnnotationBuilder>();
+            _fldAnnos.put(meta, list);
+        }
+        list.add(ab);
+    }
+
+    /**
+     * Add an annotation builder to list of builders for the specified
+     * sequence metadata.
+     */
+    protected void addAnnotation(AnnotationBuilder ab, SequenceMetaData meta) {
+        if (_seqAnnos == null)
+            _seqAnnos = new HashMap<SequenceMetaData, List<AnnotationBuilder>>();
+        List<AnnotationBuilder> list = _seqAnnos.get(meta);
+        if (list == null) {
+            list = new ArrayList<AnnotationBuilder>();
+            _seqAnnos.put(meta, list);
+        }
+        list.add(ab);
+    }
+
+    /**
+     * Add an annotation builder to list of builders for the specified
+     * query metadata.
+     */
+    protected void addAnnotation(AnnotationBuilder ab, QueryMetaData meta) {
+        if (_qryAnnos == null)
+            _qryAnnos = new HashMap<QueryMetaData, List<AnnotationBuilder>>();
+        List<AnnotationBuilder> list = _qryAnnos.get(meta);
+        if (list == null) {
+            list = new ArrayList<AnnotationBuilder>();
+            _qryAnnos.put(meta, list);
+        }
+        list.add(ab);
+    }
+
+    /**
+     * Creates an an annotation builder for the specified class metadata
+     * and adds it to list of builders.
+     */
+    protected AnnotationBuilder addAnnotation(
+        Class<? extends Annotation> annType, ClassMetaData meta) {
+        AnnotationBuilder ab = newAnnotationBuilder(annType);
+        if (meta == null)
+            return ab;
+        addAnnotation(ab, meta);
+        return ab;
+    }
+
+    /**
+     * Creates an an annotation builder for the specified class metadata
+     * and adds it to list of builders.
+     */
+    protected AnnotationBuilder addAnnotation(
+        Class<? extends Annotation> annType, FieldMetaData meta) {
+        AnnotationBuilder ab = newAnnotationBuilder(annType);
+        if (meta == null)
+            return ab;
+        addAnnotation(ab, meta);
+        return ab;
+    }
+
+    /**
+     * Creates an an annotation builder for the specified class metadata
+     * and adds it to list of builders.
+     */
+    protected AnnotationBuilder addAnnotation(
+        Class<? extends Annotation> annType, SequenceMetaData meta) {
+        AnnotationBuilder ab = newAnnotationBuilder(annType);
+        if (meta == null)
+            return ab;
+        addAnnotation(ab, meta);
+        return ab;
+    }
+
+    /**
+     * Creates an an annotation builder for the specified class metadata
+     * and adds it to list of builders.
+     */
+    protected AnnotationBuilder addAnnotation(
+        Class<? extends Annotation> annType, QueryMetaData meta) {
+        AnnotationBuilder ab = newAnnotationBuilder(annType);
+        if (meta == null)
+            return ab;
+        addAnnotation(ab, meta);
+        return ab;
+    }
+    
+    protected void serialize(Collection objects) {
+        for (Object obj : objects) {
+            int type = type(obj);
+            switch (type) {
+                case TYPE_META:
+                    serializeClass((ClassMetaData) obj);
+                    break;
+                case TYPE_SEQ:
+                    if (isMappingMode())
+                        serializeSequence((SequenceMetaData) obj);
+                case TYPE_QUERY:
+                    serializeQuery((QueryMetaData) obj);
+                    break;
+                case TYPE_CLASS_QUERIES:
+                    for (QueryMetaData query : ((ClassQueries) obj)
+                        .getQueries())
+                        serializeQuery(query);
+                    break;
+                case TYPE_CLASS_SEQS:
+                    if (isMappingMode())
+                        for (SequenceMetaData seq : ((ClassSeqs) obj)
+                            .getSequences())
+                            serializeSequence(seq);
+                    break;
+                default:
+                    if (isMappingMode())
+                        serializeSystemMappingElement(obj);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Return the type constant for the given object based on its runtime
+     * class. If the runtime class does not correspond to any of the known
+     * types then returns -1. This can happen for tags
+     * that are not handled at this store-agnostic level.
+     */
+    protected int type(Object o) {
+        if (o instanceof ClassMetaData)
+            return TYPE_META;
+        if (o instanceof QueryMetaData)
+            return TYPE_QUERY;
+        if (o instanceof SequenceMetaData)
+            return TYPE_SEQ;
+        if (o instanceof ClassQueries)
+            return TYPE_CLASS_QUERIES;
+        if (o instanceof ClassSeqs)
+            return TYPE_CLASS_SEQS;
+        return -1;
+    }
+
+    /**
+     * Serialize unknown mapping element at system level.
+     */
+    protected void serializeSystemMappingElement(Object obj) {
+    }
+
+    /**
+     * Serialize query metadata.
+     */
+    private void serializeQuery(QueryMetaData meta) {
+        Log log = getLog();
+        if (log.isInfoEnabled()) {
+            if (meta.getSourceScope() instanceof Class)
+                log.info(_loc.get("ser-cls-query",
+                    meta.getSourceScope(), meta.getName()));
+            else
+                log.info(_loc.get("ser-query", meta.getName()));
+        }
+
+        Class<? extends Annotation> ann =
+            QueryLanguages.LANG_SQL.equals(meta.getLanguage()) ?
+            NamedNativeQuery.class : NamedQuery.class;
+        AnnotationBuilder abQry = addAnnotation(ann, meta);
+        abQry.add("name", meta.getName());
+        abQry.add("query", meta.getQueryString());
+        if (QueryLanguages.LANG_SQL.equals(meta.getLanguage())) {
+            if (meta.getResultType() != null)
+                abQry.add("resultClass", meta.getResultType());
+        }
+        serializeQueryHints(meta, abQry);
+    }
+
+    /**
+     * Serialize query hints.
+     */
+    private void serializeQueryHints(QueryMetaData meta, AnnotationBuilder ab) {
+        String[] hints = meta.getHintKeys();
+        Object[] values = meta.getHintValues();
+        for (int i = 0; i < hints.length; i++) {
+            AnnotationBuilder abHint = newAnnotationBuilder(QueryHint.class);
+            abHint.add("name", hints[i]);
+            abHint.add("value", String.valueOf(values[i]));
+            ab.add("hints", abHint);
+        }
+    }
+
+    /**
+     * Serialize sequence metadata.
+     */
+    protected void serializeSequence(SequenceMetaData meta) {
+        Log log = getLog();
+        if (log.isInfoEnabled())
+            log.info(_loc.get("ser-sequence", meta.getName()));
+
+        AnnotationBuilder ab = addAnnotation(SequenceGenerator.class, meta);
+        ab.add("name", meta.getName());
+
+        // parse out the datastore sequence name, if any
+        String plugin = meta.getSequencePlugin();
+        String clsName = Configurations.getClassName(plugin);
+        String props = Configurations.getProperties(plugin);
+        String ds = null;
+        if (props != null) {
+            Properties map = Configurations.parseProperties(props);
+            ds = (String) map.remove("Sequence");
+            if (ds != null) {
+                props = Configurations.serializeProperties(map);
+                plugin = Configurations.getPlugin(clsName, props);
+            }
+        }
+
+        if (ds != null)
+            ab.add("sequenceName", ds);
+        else if (plugin != null && !SequenceMetaData.IMPL_NATIVE.equals
+            (plugin))
+            ab.add("sequenceName", plugin);
+        if (meta.getInitialValue() != 0 && meta.getInitialValue() != -1)
+            ab.add("initialValue", meta.getInitialValue());
+        if (meta.getAllocate() != 50 && meta.getAllocate() != -1)
+            ab.add("allocationSize", meta.getAllocate());
+    }
+
+    /**
+     * Serialize class metadata.
+     */
+    protected void serializeClass(ClassMetaData meta) {
+        Log log = getLog();
+        if (log.isInfoEnabled())
+            log.info(_loc.get("ser-class", meta));
+
+        AnnotationBuilder abEntity = addAnnotation(
+            getEntityAnnotationType(meta), meta);
+        if (isMetaDataMode()
+            && !meta.getTypeAlias().equals(Strings.getClassName(meta.
+            getDescribedType())))
+            abEntity.add("name", meta.getTypeAlias());
+        
+        if (isMappingMode())
+            addClassMappingAnnotations(meta);
+
+        if (isMappingMode())
+            serializeClassMappingContent(meta);
+        if (isMetaDataMode())
+            serializeIdClass(meta);
+        if (isMappingMode())
+            serializeInheritanceContent(meta);
+
+        if (isMappingMode()) {
+            List seqs = (_seqs == null) ? null : _seqs.get
+                (meta.getDescribedType().getName());
+            if (seqs != null) {
+                serializationSort(seqs);
+                for (int i = 0; i < seqs.size(); i++)
+                    serializeSequence((SequenceMetaData) seqs.get(i));
+            }
+        }
+
+        if (isQueryMode()) {
+            List queries = (_queries == null) ? null : _queries.get
+                (meta.getDescribedType().getName());
+            if (queries != null) {
+                serializationSort(queries);
+                for (int i = 0; i < queries.size(); i++)
+                    serializeQuery((QueryMetaData) queries.get(i));
+            }
+            if (isMappingMode())
+                serializeQueryMappings(meta);
+        }
+
+        List<FieldMetaData> fields = new ArrayList(Arrays.asList
+            (meta.getDefinedFieldsInListingOrder()));
+        Collections.sort(fields, new FieldComparator());
+
+        // serialize attr-override
+        if (isMappingMode()) {
+            FieldMetaData fmd;
+            FieldMetaData orig;
+            for (Iterator<FieldMetaData> it = fields.iterator(); it.hasNext();)
+            {
+                fmd = it.next();
+                if (meta.getDefinedSuperclassField(fmd.getName()) == null)
+                    continue;
+                orig = meta.getPCSuperclassMetaData().getField(fmd.getName());
+                if (serializeAttributeOverride(fmd, orig))
+                    serializeAttributeOverrideContent(fmd, orig);
+                it.remove();
+            }
+        }
+
+        if (fields.size() > 0 && (isMetaDataMode() || isMappingMode())) {
+            FieldMetaData orig;
+            for (FieldMetaData fmd : fields) {
+                if (fmd.getDeclaringType() != fmd.getDefiningMetaData().
+                    getDescribedType()) {
+                    orig = fmd.getDeclaringMetaData().getDeclaredField
+                        (fmd.getName());
+                } else
+                    orig = null;
+                serializeField(fmd, orig);
+            }
+        }
+    }
+
+    /**
+     * Return entity annotation type.
+     */
+    private static Class<? extends Annotation> getEntityAnnotationType(
+        ClassMetaData meta) {
+        switch (getEntityTag(meta)) {
+            case ENTITY:
+                return Entity.class;
+            case EMBEDDABLE:
+                return Embeddable.class;
+            case MAPPED_SUPERCLASS:
+                return MappedSuperclass.class;
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * Return field annotation type.
+     */
+    private static Class<? extends Annotation> getFieldAnnotationType (
+        FieldMetaData fmd, PersistenceStrategy strat) {
+        Class<? extends Annotation> ann = null;
+        if (fmd.isPrimaryKey() && strat == PersistenceStrategy.EMBEDDED)
+            ann = EmbeddedId.class;
+        else if (fmd.isPrimaryKey())
+            ann = Id.class;
+        else if (fmd.isVersion())
+            ann = Version.class;
+        else {
+            switch (strat) {
+                case TRANSIENT:
+                    ann = Transient.class;
+                    break;
+                case BASIC:
+                    ann = Basic.class;
+                    break;
+                case EMBEDDED:
+                    ann = Embedded.class;
+                    break;
+                case MANY_ONE:
+                    ann = ManyToOne.class;
+                    break;
+                case ONE_ONE:
+                    ann = OneToOne.class;
+                    break;
+                case ONE_MANY:
+                    ann = OneToMany.class;
+                    break;
+                case MANY_MANY:
+                    ann = ManyToMany.class;
+                    break;
+            }
+        }
+        return ann;
+    }
+
+    /**
+     * Return the MetaDataTag for the given class meta data.
+     */
+    private static MetaDataTag getEntityTag(ClassMetaData meta) {
+        // @Embeddable classes can't declare Id fields
+        if (meta.isEmbeddedOnly() && meta.getPrimaryKeyFields().length == 0)
+            return MetaDataTag.EMBEDDABLE;
+        if (meta.isMapped())
+            return MetaDataTag.ENTITY;
+        return MetaDataTag.MAPPED_SUPERCLASS;
+    }
+
+    /**
+     * Add mapping attributes for the given class. Does nothing by default
+     */
+    protected void addClassMappingAnnotations(ClassMetaData mapping) {
+    }
+
+    /**
+     * Serialize id-class.
+     */
+    private void serializeIdClass(ClassMetaData meta) {
+        if (meta.getIdentityType() != ClassMetaData.ID_APPLICATION
+            || meta.isOpenJPAIdentity())
+            return;
+
+        ClassMetaData sup = meta.getPCSuperclassMetaData();
+        Class oid = meta.getObjectIdType();
+        if (oid != null && (sup == null || oid != sup.getObjectIdType())) {
+            AnnotationBuilder ab = addAnnotation(IdClass.class, meta);
+            ab.add(null, oid);
+        }
+    }
+
+    /**
+     * Serialize class mapping content. Does nothing by default.
+     */
+    protected void serializeClassMappingContent(ClassMetaData mapping) {
+    }
+
+    /**
+     * Serialize inheritance content. Does nothing by default.
+     */
+    protected void serializeInheritanceContent(ClassMetaData mapping) {
+    }
+
+    /**
+     * Serialize query mappings. Does nothing by default.
+     */
+    protected void serializeQueryMappings(ClassMetaData meta) {
+    }
+
+    /**
+     * Serialize the given field.
+     */
+    private void serializeField(FieldMetaData fmd, FieldMetaData orig) {
+        if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT
+            && !fmd.isExplicit())
+            return;
+
+        PersistenceStrategy strat = getStrategy(fmd);
+        ValueMetaData cascades = null;
+        AnnotationBuilder ab = addAnnotation(
+            getFieldAnnotationType (fmd, strat), fmd);        
+        if (fmd.isPrimaryKey() && strat == PersistenceStrategy.EMBEDDED)
+            ; // noop
+        else if (fmd.isPrimaryKey())
+            ; // noop
+        else if (fmd.isVersion())
+            ; // noop
+        else {
+            switch (strat) {
+                case BASIC:
+                    if (isMetaDataMode())
+                        addBasicAttributes(fmd, ab);
+                    break;
+                case MANY_ONE:
+                    if (isMetaDataMode())
+                        addManyToOneAttributes(fmd, ab);
+                    cascades = fmd;
+                    break;
+                case ONE_ONE:
+                    if (isMetaDataMode())
+                        addOneToOneAttributes(fmd, ab);
+                    cascades = fmd;
+                    break;
+                case ONE_MANY:
+                    if (isMetaDataMode())
+                        addOneToManyAttributes(fmd, ab);
+                    cascades = fmd.getElement();
+                    break;
+                case MANY_MANY:
+                    if (isMetaDataMode())
+                        addManyToManyAttributes(fmd, ab);
+                    cascades = fmd.getElement();
+                    break;
+            }
+            if (isMappingMode())
+                addStrategyMappingAttributes(fmd, ab);
+        }
+        if (isMappingMode(fmd))
+            addFieldMappingAttributes(fmd, orig, ab);
+
+        if (fmd.getOrderDeclaration() != null) {
+            if (!(Order.ELEMENT + " asc").equals(fmd.getOrderDeclaration()))
+                addAnnotation(OrderBy.class, fmd).
+                    add (null, fmd.getOrderDeclaration());
+        }
+        if (isMappingMode() && fmd.getKey().getValueMappedBy() != null) {
+            AnnotationBuilder abMapKey = addAnnotation(MapKey.class, fmd);
+            FieldMetaData mapBy = fmd.getKey().getValueMappedByMetaData();
+            if (!mapBy.isPrimaryKey() ||
+                mapBy.getDefiningMetaData().getPrimaryKeyFields().length != 1) {
+                abMapKey.add("name", fmd.getKey().getValueMappedBy());
+            }
+        }
+        if (isMappingMode(fmd))
+            serializeFieldMappingContent(fmd, strat, ab);
+        if (cascades != null && isMetaDataMode())
+            serializeCascades(cascades, ab);
+    }
+
+    /**
+     * Add mapping attributes for the given field. Does nothing by default.
+     */
+    protected void addFieldMappingAttributes(FieldMetaData fmd,
+        FieldMetaData orig, AnnotationBuilder ab) {
+    }
+    
+    /**
+     * Always returns false by default.
+     */
+    protected boolean serializeAttributeOverride(FieldMetaData fmd,
+        FieldMetaData orig) {
+        return false;
+    }
+
+    /**
+     * Serialize attribute override content.
+     */
+    private void serializeAttributeOverrideContent(FieldMetaData fmd,
+        FieldMetaData orig) {
+        AnnotationBuilder ab = addAnnotation(AttributeOverride.class, fmd);
+        ab.add("name", fmd.getName());
+        serializeAttributeOverrideMappingContent(fmd, orig, ab);
+    }
+
+    /**
+     * Serialize attribute override mapping content. Does nothing by default,
+     */
+    protected void serializeAttributeOverrideMappingContent
+        (FieldMetaData fmd, FieldMetaData orig, AnnotationBuilder ab) {
+    }
+
+
+    /**
+     * Serialize cascades.
+     */
+    private void serializeCascades(ValueMetaData vmd, AnnotationBuilder ab) {
+        EnumSet<CascadeType> cascades = EnumSet.noneOf(CascadeType.class);
+        if (vmd.getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) {
+            cascades.add(CascadeType.PERSIST);
+        }
+        if (vmd.getCascadeAttach() == ValueMetaData.CASCADE_IMMEDIATE) {
+            cascades.add(CascadeType.MERGE);
+        }
+        if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) {
+            cascades.add(CascadeType.REMOVE);
+        }
+        if (vmd.getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE) {
+            cascades.add(CascadeType.REFRESH);
+        }
+        if (cascades.size() == 4) // ALL
+        {
+            cascades.clear();
+            cascades.add(CascadeType.ALL);
+        }
+        if (!cascades.isEmpty()) {
+            ab.add("cascade", cascades);
+        }
+    }
+
+    /**
+     * Return the serialized strategy name.
+     */
+    protected PersistenceStrategy getStrategy(FieldMetaData fmd) {
+        if (fmd.getManagement() == fmd.MANAGE_NONE)
+            return PersistenceStrategy.TRANSIENT;
+
+        if (fmd.isSerialized()
+            || fmd.getDeclaredType() == byte[].class
+            || fmd.getDeclaredType() == Byte[].class
+            || fmd.getDeclaredType() == char[].class
+            || fmd.getDeclaredType() == Character[].class)
+            return PersistenceStrategy.BASIC;
+
+        FieldMetaData mappedBy;
+        switch (fmd.getDeclaredTypeCode()) {
+            case JavaTypes.PC:
+                if (fmd.isEmbedded())
+                    return PersistenceStrategy.EMBEDDED;
+                if (fmd.getMappedBy() != null)
+                    return PersistenceStrategy.ONE_ONE;
+                FieldMetaData[] inverses = fmd.getInverseMetaDatas();
+                if (inverses.length == 1 &&
+                    inverses[0].getTypeCode() == JavaTypes.PC &&
+                    inverses[0].getMappedByMetaData() == fmd) {
+                    return PersistenceStrategy.ONE_ONE;
+                }
+                return PersistenceStrategy.MANY_ONE;
+            case JavaTypes.ARRAY:
+            case JavaTypes.COLLECTION:
+            case JavaTypes.MAP:
+                mappedBy = fmd.getMappedByMetaData();
+                if (mappedBy == null || mappedBy.getTypeCode() != JavaTypes.PC)
+                    return PersistenceStrategy.MANY_MANY;
+                return PersistenceStrategy.ONE_MANY;
+            case JavaTypes.OID:
+                return PersistenceStrategy.EMBEDDED;
+            default:
+                return PersistenceStrategy.BASIC;
+        }
+    }
+
+    /**
+     * Add basic attributes.
+     */
+    private void addBasicAttributes(FieldMetaData fmd, AnnotationBuilder ab) {
+        if (!fmd.isInDefaultFetchGroup())
+            ab.add("fetch", FetchType.LAZY);
+        if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
+            ab.add("optional", false);
+    }
+
+    /**
+     * Add many-to-one attributes.
+     */
+    private void addManyToOneAttributes(FieldMetaData fmd,
+        AnnotationBuilder ab) {
+        if (!fmd.isInDefaultFetchGroup())
+            ab.add("fetch", FetchType.LAZY);
+        if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
+            ab.add("optional", false);
+    }
+
+    /**
+     * Add one-to-one attributes.
+     */
+    private void addOneToOneAttributes(FieldMetaData fmd,
+        AnnotationBuilder ab) {
+        if (!fmd.isInDefaultFetchGroup())
+            ab.add("fetch", FetchType.LAZY);
+        if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION)
+            ab.add("optional", false);
+    }
+
+    /**
+     * Add one-to-many attributes.
+     */
+    private void addOneToManyAttributes(FieldMetaData fmd,
+        AnnotationBuilder ab) {
+        if (fmd.isInDefaultFetchGroup())
+            ab.add("fetch", FetchType.EAGER);
+        addTargetEntityAttribute(fmd, ab);
+    }
+
+    /**
+     * Add many-to-many attributes.
+     */
+    private void addManyToManyAttributes(FieldMetaData fmd,
+        AnnotationBuilder ab) {
+        if (fmd.isInDefaultFetchGroup())
+            ab.add("fetch", FetchType.EAGER);
+        addTargetEntityAttribute(fmd, ab);
+    }
+
+    /**
+     * Add a target-entity attribute to collection and map fields that do
+     * not use generics.
+     */
+    private void addTargetEntityAttribute(FieldMetaData fmd,
+        AnnotationBuilder ab) {
+        Member member = fmd.getBackingMember();
+        Class[] types;
+        if (member instanceof Field)
+            types = JavaVersions.getParameterizedTypes((Field) member);
+        else if (member instanceof Method)
+            types = JavaVersions.getParameterizedTypes((Method) member);
+        else
+            types = new Class[0];
+
+        switch (fmd.getDeclaredTypeCode()) {
+            case JavaTypes.COLLECTION:
+                if (types.length != 1)
+                    ab.add("targetEntity", fmd.getElement().getDeclaredType());
+                break;
+            case JavaTypes.MAP:
+                if (types.length != 2)
+                    ab.add("targetEntity", fmd.getElement().getDeclaredType());
+                break;
+        }
+    }
+
+    /**
+     * Serialize field mapping content; this will be called before
+     * {@link #serializeValueMappingContent}. Does nothing by default.
+     */
+    protected void serializeFieldMappingContent(FieldMetaData fmd,
+        PersistenceStrategy strategy, AnnotationBuilder ab) {
+    }
+
+    /**
+     * Set mapping attributes for strategy. Sets mapped-by by default.
+     */
+    protected void addStrategyMappingAttributes(FieldMetaData fmd,
+        AnnotationBuilder ab) {
+        if (fmd.getMappedBy() != null)
+            ab.add("mappedBy", fmd.getMappedBy());
+    }
+    
+    protected Collection getObjects() {
+        List all = new ArrayList();
+        if (isQueryMode())
+            addQueryMetaDatas(all);
+        if (isMappingMode())
+            addSequenceMetaDatas(all);
+        if ((isMetaDataMode() || isMappingMode()) && _metas != null)
+            all.addAll(_metas.values());
+        if (isMappingMode())
+            addSystemMappingElements(all);
+        serializationSort(all);
+        return all;
+    }
+
+    protected void writeAnnotations(Object meta,
+        List<AnnotationBuilder> builders, Map output) {
+        List<String> annos = new ArrayList<String>();
+        for(AnnotationBuilder ab: builders)
+            annos.add(ab.toString());
+        output.put(meta, annos);        
+    }
+
+    public void serialize(Map output, int flags) throws IOException {
+        Collection all = getObjects();
+        serialize(all);
+
+        if (_clsAnnos != null)
+            for (ClassMetaData meta : _clsAnnos.keySet())
+                writeAnnotations(meta, _clsAnnos.get(meta), output);
+        if (_fldAnnos != null)
+            for (FieldMetaData meta : _fldAnnos.keySet())
+                writeAnnotations(meta, _fldAnnos.get(meta), output);
+        if (_seqAnnos != null)
+            for (SequenceMetaData meta : _seqAnnos.keySet())
+                writeAnnotations(meta, _seqAnnos.get(meta), output);
+        if (_qryAnnos != null)
+            for (QueryMetaData meta : _qryAnnos.keySet())
+                writeAnnotations(meta, _qryAnnos.get(meta), output);
+    }
+
+    public void serialize(File file, int flags) throws IOException {
+        FileWriter out = new FileWriter(file.getCanonicalPath(),
+            (flags & APPEND) > 0);
+        serialize(out, flags);
+        out.close();
+    }
+
+    public void serialize(Writer out, int flags) throws IOException {
+        Map output = new HashMap();
+        serialize(output, flags);
+
+        for(Object meta: output.keySet()) {
+            out.write("--"+meta.toString());
+            out.write("\n");
+            List<String> annos = (List<String>) output.get(meta);
+            for(String ann: annos) {
+                out.write("\t");
+                out.write(ann);
+                out.write("\n");
+            }
+        }
+    }
+
+    public void serialize(int flags) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Represents ordered set of {@link org.apache.openjpa.meta.SequenceMetaData}s with a
+     * common class scope.
+     *
+     * @author Stephen Kim
+     * @author Pinaki Poddar
+     */
+    private static class ClassSeqs
+        implements SourceTracker, Comparable<ClassSeqs>,
+        Comparator<SequenceMetaData> {
+
+        private final SequenceMetaData[] _seqs;
+
+        public ClassSeqs(List<SequenceMetaData> seqs) {
+            if (seqs == null || seqs.isEmpty())
+                throw new InternalException();
+
+            _seqs = (SequenceMetaData[]) seqs.toArray
+                (new SequenceMetaData[seqs.size()]);
+            Arrays.sort(_seqs, this);
+        }
+
+        public SequenceMetaData[] getSequences() {
+            return _seqs;
+        }
+
+        /**
+         * Compare sequence metadata on name.
+         */
+        public int compare(SequenceMetaData o1, SequenceMetaData o2) {
+            return o1.getName().compareTo(o2.getName());
+        }
+
+        public File getSourceFile() {
+            return _seqs[0].getSourceFile();
+        }
+
+        public Object getSourceScope() {
+            return _seqs[0].getSourceScope();
+        }
+
+        public int getSourceType() {
+            return _seqs[0].getSourceType();
+        }
+
+        public String getResourceName() {
+            return _seqs[0].getResourceName();
+        }
+
+        public int compareTo(ClassSeqs other) {
+            if (other == this)
+                return 0;
+            if (other == null)
+                return -1;
+            Class scope = (Class) getSourceScope();
+            Class oscope = (Class) other.getSourceScope();
+            return scope.getName().compareTo(oscope.getName());
+        }
+    }
+
+    /**
+     * Represents ordered set of {@link org.apache.openjpa.meta.QueryMetaData}s with a
+     * common class scope.
+     *
+     * @author Stephen Kim
+     * @author Pinaki Poddar
+     */
+    private static class ClassQueries
+        implements SourceTracker, Comparable<ClassQueries>,
+        Comparator<QueryMetaData> {
+
+        private final QueryMetaData[] _queries;
+
+        public ClassQueries(List<QueryMetaData> queries) {
+            if (queries == null || queries.isEmpty())
+                throw new InternalException();
+
+            _queries = (QueryMetaData[]) queries.toArray
+                (new QueryMetaData[queries.size()]);
+            Arrays.sort(_queries, this);
+        }
+
+        public QueryMetaData[] getQueries() {
+            return _queries;
+        }
+
+        /**
+         * Compare query metadata. Normal queries appear before native queries.
+         * If the given queries use same language, then their names are
+         * compared.
+         */
+        public int compare(QueryMetaData o1, QueryMetaData o2) {
+            // normal queries before native
+            if (!StringUtils.equals(o1.getLanguage(), o2.getLanguage())) {
+                if (QueryLanguages.LANG_SQL.equals(o1.getLanguage()))
+                    return 1;
+                else
+                    return -1;
+            }
+            return o1.getName().compareTo(o2.getName());
+        }
+
+        public File getSourceFile() {
+            return _queries[0].getSourceFile();
+        }
+
+        public Object getSourceScope() {
+            return _queries[0].getSourceScope();
+        }
+
+        public int getSourceType() {
+            return _queries[0].getSourceType();
+        }
+
+        public String getResourceName() {
+            return _queries[0].getResourceName();
+        }
+
+        public int compareTo(ClassQueries other) {
+            if (other == this)
+                return 0;
+            if (other == null)
+                return -1;
+            Class scope = (Class) getSourceScope();
+            Class oscope = (Class) other.getSourceScope();
+            return scope.getName().compareTo(oscope.getName());
+        }
+    }
+
+    /**
+     * Compares clases, sequences, and queries to order them for serialization.
+     * Places sequences first, then classes, then queries. Sequences and
+     * queries are ordered alphabetically by name. Classes are placed in
+     * listing order, in inheritance order within that, and in alphabetical
+     * order within that.
+     *
+     * @author Stephen Kim
+     */
+    protected class SerializationComparator
+        extends MetaDataInheritanceComparator {
+
+        public int compare(Object o1, Object o2) {
+            if (o1 == o2)
+                return 0;
+            if (o1 == null)
+                return 1;
+            if (o2 == null)
+                return -1;
+
+            int t1 = type(o1);
+            int t2 = type(o2);
+            if (t1 != t2)
+                return t1 - t2;
+
+            switch (t1) {
+                case TYPE_META:
+                    return compare((ClassMetaData) o1, (ClassMetaData) o2);
+                case TYPE_QUERY:
+                    return compare((QueryMetaData) o1, (QueryMetaData) o2);
+                case TYPE_SEQ:
+                    return compare((SequenceMetaData) o1,
+                        (SequenceMetaData) o2);
+                case TYPE_CLASS_QUERIES:
+                    return ((Comparable) o1).compareTo(o2);
+                case TYPE_CLASS_SEQS:
+                    return ((Comparable) o1).compareTo(o2);
+                default:
+                    return compareUnknown(o1, o2);
+            }
+        }
+
+        /**
+         * Compare two unrecognized elements of the same type. Throws
+         * exception by default.
+         */
+        protected int compareUnknown(Object o1, Object o2) {
+            throw new InternalException();
+        }
+
+        /**
+         * Compare between two class metadata.
+         */
+        private int compare(ClassMetaData o1, ClassMetaData o2) {
+            int li1 = o1.getListingIndex();
+            int li2 = o2.getListingIndex();
+            if (li1 == -1 && li2 == -1) {
+                MetaDataTag t1 = getEntityTag(o1);
+                MetaDataTag t2 = getEntityTag(o2);
+                if (t1.compareTo(t2) != 0)
+                    return t1.compareTo(t2);
+                int inher = super.compare(o1, o2);
+                if (inher != 0)
+                    return inher;
+                return o1.getDescribedType().getName().compareTo
+                    (o2.getDescribedType().getName());
+            }
+
+            if (li1 == -1)
+                return 1;
+            if (li2 == -1)
+                return -1;
+            return li1 - li2;
+        }
+
+        /**
+         * Compare query metadata.
+         */
+        private int compare(QueryMetaData o1, QueryMetaData o2) {
+            // normal queries before native
+            if (!StringUtils.equals(o1.getLanguage(), o2.getLanguage())) {
+                if (QueryLanguages.LANG_SQL.equals(o1.getLanguage()))
+                    return 1;
+                else
+                    return -1;
+            }
+            return o1.getName().compareTo(o2.getName());
+        }
+
+        /**
+         * Compare sequence metadata.
+         */
+        private int compare(SequenceMetaData o1, SequenceMetaData o2) {
+            return o1.getName().compareTo(o2.getName());
+        }
+    }
+
+    /**
+     * Sorts fields according to listing order, then XSD strategy order,
+     * then name order.
+     */
+    private class FieldComparator
+        implements Comparator {
+
+        public int compare(Object o1, Object o2) {
+            FieldMetaData fmd1 = (FieldMetaData) o1;
+            FieldMetaData fmd2 = (FieldMetaData) o2;
+            if (fmd1.isPrimaryKey()) {
+                if (fmd2.isPrimaryKey())
+                    return fmd1.compareTo(fmd2);
+                return -1;
+            }
+            if (fmd2.isPrimaryKey())
+                return 1;
+
+            if (fmd1.isVersion()) {
+                if (fmd2.isVersion())
+                    return compareListingOrder(fmd1, fmd2);
+				return getStrategy(fmd2) == PersistenceStrategy.BASIC ? 1 : -1;
+			}
+			if (fmd2.isVersion())
+				return getStrategy(fmd1) == PersistenceStrategy.BASIC ? -1 : 1;
+
+			int stcmp = getStrategy(fmd1).compareTo(getStrategy(fmd2));
+            if (stcmp != 0)
+                return stcmp;
+            return compareListingOrder(fmd1, fmd2);
+        }
+
+        private int compareListingOrder(FieldMetaData fmd1, FieldMetaData fmd2){
+            int lcmp = fmd1.getListingIndex() - fmd2.getListingIndex();
+            if (lcmp != 0)
+                return lcmp;
+            return fmd1.compareTo(fmd2);
+		}
+	}
+}
Index: openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java
===================================================================
--- openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java	(revision 0)
+++ openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationBuilder.java	(revision 0)
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.openjpa.persistence;
+
+import serp.util.Strings;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.EnumSet;
+import java.lang.annotation.Annotation;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Helper class to stringify annotation declarations.
+ *
+ * @author Gokhan Ergul
+ * @since 1.0.0
+ */
+public class AnnotationBuilder {
+
+    private Class<? extends Annotation> type;
+    private List<AnnotationEntry> components =
+        new ArrayList<AnnotationEntry>();
+
+    protected AnnotationBuilder(Class<? extends Annotation> type) {
+        this.type = type;
+    }
+
+    public Class<? extends Annotation> getType() {
+        return this.type;
+    }
+
+    public AnnotationBuilder add(String key, String val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, boolean val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, int val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, Class val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, EnumSet val) {
+        return doAdd(key, val);
+    }
+
+    public AnnotationBuilder add(String key, Enum val) {
+        return doAdd(key, val);
+    }
+
+    @SuppressWarnings("unchecked")
+    public AnnotationBuilder add(String key, AnnotationBuilder val) {
+        if (null == val)
+            return this;
+        AnnotationEntry ae = find(key);
+        if (null == ae) {
+            doAdd(key, val);
+        } else {
+            List<AnnotationBuilder> list;
+            if (ae.value instanceof List) {
+                list = (List<AnnotationBuilder>) ae.value;
+            } else if (ae.value instanceof AnnotationBuilder) {
+                list = new ArrayList<AnnotationBuilder> ();
+                list.add((AnnotationBuilder) ae.value);
+                ae.value = list;
+            } else {
+                throw new IllegalArgumentException(
+                    "Unexpected type: " + ae.value);
+            }
+            list.add(val);
+        }
+        return this;
+    }
+
+    public boolean hasComponents() {
+        return components.size() > 0;
+    }
+
+    private AnnotationBuilder doAdd (String key, Object val) {
+        if (null != val)
+            components.add(new AnnotationEntry(key, val));
+        return this;        
+    }
+
+    private AnnotationEntry find(String key) {
+        for(AnnotationEntry ae: components) {
+            // null key references considered equal
+            if (StringUtils.equals(ae.key, key))
+                return ae;
+        }
+        return null;
+    }
+
+    static String enumToString(Enum e) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(Strings.getClassName(e.getClass())).
+            append(".").append(e);
+        return sb.toString();
+    }
+
+    static String enumSetToString(EnumSet set) {
+        StringBuilder sb = new StringBuilder();
+        for (Iterator i = set.iterator(); i.hasNext();) {
+            Object e =  i.next();
+            sb.append(Strings.getClassName(e.getClass())).
+                append(".").append(e);
+            if (i.hasNext())
+                sb.append(", ");
+        }
+        return sb.toString();
+    }
+
+    protected void toString(StringBuilder sb) {
+        sb.append("@").append(Strings.getClassName(type));
+        if (components.size() == 0)
+            return;
+        sb.append("(");
+        for (Iterator<AnnotationEntry> i = components.iterator(); i.hasNext();) 
+        {
+            AnnotationEntry e = i.next();
+            e.toString(sb);
+            if (i.hasNext())
+                sb.append(", ");
+        }
+        sb.append(")");        
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        toString(sb);
+        return sb.toString();
+    }
+
+}
+
+class AnnotationEntry {
+
+    String key;
+    Object value;
+
+    AnnotationEntry(String key, Object value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    @SuppressWarnings("unchecked")
+    void toString(StringBuilder sb) {
+        if (null != key)
+            sb.append(key).append("=");
+
+        List.class.getTypeParameters();
+        if (value instanceof List) {
+            sb.append("{");
+            List<AnnotationBuilder> l = (List<AnnotationBuilder>) value;
+            for (Iterator<AnnotationBuilder> i = l.iterator(); i.hasNext();) {
+                AnnotationBuilder ab =  i.next();
+                sb.append(ab.toString());
+                if (i.hasNext())
+                    sb.append(", ");
+            }
+            sb.append("}");
+        } else if (value instanceof Class) {
+            String cls = ((Class) value).getName().replace('$', '.');
+            sb.append(cls).append(".class");
+        } else if (value instanceof String) {
+            sb.append('"').append(value).append('"');
+        } else if (value instanceof Enum) {
+            sb.append(AnnotationBuilder.enumToString((Enum) value));
+        } else if (value instanceof EnumSet) {
+            sb.append(AnnotationBuilder.enumSetToString((EnumSet) value));
+        } else {
+            sb.append(value);
+        }
+    }
+
+}
Index: openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java
===================================================================
--- openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java	(revision 0)
+++ openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java	(revision 0)
@@ -0,0 +1,790 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.openjpa.persistence.jdbc;
+
+import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataSerializer;
+import org.apache.openjpa.persistence.PersistenceStrategy;
+import org.apache.openjpa.persistence.AnnotationBuilder;
+import org.apache.openjpa.jdbc.meta.QueryResultMapping;
+import org.apache.openjpa.jdbc.meta.MappingRepository;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.ClassMappingInfo;
+import org.apache.openjpa.jdbc.meta.DiscriminatorMappingInfo;
+import org.apache.openjpa.jdbc.meta.MappingInfo;
+import org.apache.openjpa.jdbc.meta.SequenceMapping;
+import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
+import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.EnumValueHandler;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.schema.Unique;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.SequenceMetaData;
+import org.apache.openjpa.meta.MetaDataModes;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+import java.sql.Types;
+import java.lang.annotation.Annotation;
+
+import serp.util.Strings;
+
+import javax.persistence.TemporalType;
+import javax.persistence.EnumType;
+import javax.persistence.InheritanceType;
+import javax.persistence.Table;
+import javax.persistence.SecondaryTable;
+import javax.persistence.Inheritance;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.Lob;
+import javax.persistence.Temporal;
+import javax.persistence.Enumerated;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.TableGenerator;
+import javax.persistence.JoinColumns;
+import javax.persistence.JoinColumn;
+import javax.persistence.PrimaryKeyJoinColumn;
+import javax.persistence.PrimaryKeyJoinColumns;
+import javax.persistence.SqlResultSetMapping;
+import javax.persistence.EntityResult;
+import javax.persistence.FieldResult;
+import javax.persistence.ColumnResult;
+
+//@todo: javadocs
+
+/**
+ * Serializes persistence mappings as annotations.
+ *
+ * @since 1.0.0
+ * @author Steve Kim
+ * @author Gokhan Ergul
+ * @nojavadoc
+ */
+public class AnnotationPersistenceMappingSerializer
+    extends AnnotationPersistenceMetaDataSerializer {
+
+    private static final int TYPE_RESULTMAP = TYPE_QUERY + 1;
+
+    private List<QueryResultMapping> _results = null;
+    private boolean _sync = false;
+
+    private Map<QueryResultMapping, List<AnnotationBuilder>> _rsmAnnos = null;
+    
+    /**
+     * Constructor. Supply configuration.
+     */
+    public AnnotationPersistenceMappingSerializer(JDBCConfiguration conf) {
+        super(conf);
+    }
+
+    /**
+     * Whether to automatically synchronize mapping info with data available
+     * from mapped components before serialization. Defaults to false.
+     */
+    public boolean getSyncMappingInfo() {
+        return _sync;
+    }
+
+    /**
+     * Whether to automatically synchronize mapping info with data available
+     * from mapped components before serialization. Defaults to false.
+     */
+    public void setSyncMappingInfo(boolean sync) {
+        _sync = sync;
+    }
+
+    /**
+     * Adds the given result set mapping to local cache.
+     */
+    public void addQueryResultMapping(QueryResultMapping meta) {
+        if (_results == null)
+            _results = new ArrayList<QueryResultMapping>();
+        _results.add(meta);
+    }
+
+    /**
+     * Removes given result set mapping from the local cache.
+     */
+    public boolean removeQueryResultMapping(QueryResultMapping meta) {
+        return _results != null && _results.remove(meta);
+    }
+
+    @Override
+    public void addAll(MetaDataRepository repos) {
+        super.addAll(repos);
+        for (QueryResultMapping res : ((MappingRepository) repos)
+            .getQueryResultMappings())
+            addQueryResultMapping(res);
+    }
+
+    @Override
+    public boolean removeAll(MetaDataRepository repos) {
+        boolean removed = super.removeAll(repos);
+        for (QueryResultMapping res : ((MappingRepository) repos)
+            .getQueryResultMappings())
+            removed |= removeQueryResultMapping(res);
+        return removed;
+    }
+
+    @Override
+    public void clear() {
+        super.clear();
+        if (_results != null)
+            _results.clear();
+    }
+
+    /**
+     * Add an annotation builder to list of builders for the specified
+     * class metadata.
+     */
+    protected void addAnnotation(AnnotationBuilder ab, QueryResultMapping meta) {
+        if (_rsmAnnos == null)
+            _rsmAnnos = new HashMap<QueryResultMapping, List<AnnotationBuilder>>();
+        List<AnnotationBuilder> list = _rsmAnnos.get(meta);
+        if (list == null) {
+            list = new ArrayList<AnnotationBuilder>();
+            _rsmAnnos.put(meta, list);
+        }
+        list.add(ab);
+    }
+
+    /**
+     * Creates an an annotation builder for the specified class metadata
+     * and adds it to list of builders.
+     */
+    protected AnnotationBuilder addAnnotation(
+        Class<? extends Annotation> annType, QueryResultMapping meta) {
+        AnnotationBuilder ab = newAnnotationBuilder(annType);
+        if (meta == null)
+            return ab;
+        addAnnotation(ab, meta);
+        return ab;
+    }
+
+
+    @Override
+    protected void serializeClass(ClassMetaData meta) {
+        if (_sync && isMappingMode() && meta instanceof ClassMapping) {
+            // sync if resolved and mapped
+            ClassMapping cls = (ClassMapping) meta;
+            if ((cls.getResolve() & MetaDataModes.MODE_MAPPING) != 0 && cls.isMapped()) {
+                cls.syncMappingInfo();
+                cls.getDiscriminator().syncMappingInfo();
+                cls.getVersion().syncMappingInfo();
+                FieldMapping[] fields;
+                if (cls.getEmbeddingMetaData() == null)
+                    fields = cls.getDefinedFieldMappings();
+                else
+                    fields = cls.getFieldMappings();
+                for (FieldMapping f : fields)
+                    f.syncMappingInfo();
+            }
+        }
+        super.serializeClass(meta);
+    }
+
+    @Override
+    protected void serializeClassMappingContent(ClassMetaData mapping) {
+        ClassMapping cls = (ClassMapping) mapping;
+        ClassMappingInfo info = cls.getMappingInfo();
+        AnnotationBuilder abTable = addAnnotation(Table.class, mapping);
+        serializeTable(info.getTableName(), Strings
+            .getClassName(mapping.getDescribedType()), null,
+            info.getUniques(), abTable);
+        serializeColumns(info, ColType.PK_JOIN, null, abTable, cls);
+        for (String second : info.getSecondaryTableNames()) {
+            AnnotationBuilder abSecTable =
+                addAnnotation(SecondaryTable.class, mapping);
+            serializeTable(second, null, info, null, abSecTable);
+        }
+    }
+
+    @Override
+    protected void serializeInheritanceContent(ClassMetaData mapping) {
+        ClassMapping cls = (ClassMapping) mapping;
+        ClassMappingInfo info = cls.getMappingInfo();
+        DiscriminatorMappingInfo dinfo = cls.getDiscriminator()
+            .getMappingInfo();
+        String strat = info.getHierarchyStrategy();
+        if (null == strat)
+            return;
+        String itypecls = Strings.getClassName(InheritanceType.class);
+        AnnotationBuilder abInheritance =
+            addAnnotation(Inheritance.class, mapping);
+        if (FlatClassStrategy.ALIAS.equals(strat))
+            abInheritance.add("strategy", itypecls + ".SINGLE_TABLE");
+        else if (VerticalClassStrategy.ALIAS.equals(strat))
+            abInheritance.add("strategy", itypecls + ".JOINED");
+        else if (FullClassStrategy.ALIAS.equals(strat))
+            abInheritance.add("strategy", itypecls + ".TABLE_PER_CLASS");
+        if (dinfo.getValue() != null) {
+            AnnotationBuilder abDiscVal =
+                addAnnotation(DiscriminatorValue.class, mapping);
+            abDiscVal.add(null, dinfo.getValue());
+        }
+        AnnotationBuilder abDiscCol =
+            addAnnotation(DiscriminatorColumn.class, mapping);
+        serializeColumns(dinfo, ColType.DISC, null, abDiscCol, null);
+    }
+
+    /**
+     * Serialize table optionally listing primary-key-joins stored
+     * in the given {@link org.apache.openjpa.jdbc.meta.ClassMappingInfo}.
+     */
+    private void serializeTable(String table, String defaultName,
+        ClassMappingInfo secondaryInfo, Unique[] uniques,
+        AnnotationBuilder ab) {
+        List<Column> cols = null;
+        if (secondaryInfo != null)
+            cols = (List<Column>) secondaryInfo.getSecondaryTableJoinColumns
+                (table);
+
+        boolean print = (cols != null && cols.size() > 0) ||
+            (uniques != null && uniques.length > 0);
+        if (table != null
+            && (defaultName == null || !defaultName.equals(table))) {
+            print = true;
+            int index = table.indexOf('.');
+            if (index < 0)
+                ab.add("name", table);
+            else {
+                ab.add("schema", table.substring(0, index));
+                ab.add("name", table.substring(index + 1));
+            }
+        }
+        if (print) {
+            if (cols != null) {
+                for (Column col : cols)
+                    serializeColumn(col, ColType.PK_JOIN, null, false, ab, null);
+            }
+            if (uniques != null) {
+                for (Unique unique: uniques) {
+                    AnnotationBuilder abUniqueConst =
+                        newAnnotationBuilder(UniqueConstraint.class);
+                    serializeUniqueConstraint(unique, abUniqueConst);
+                    ab.add("uniqueConstraints", abUniqueConst);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected boolean serializeAttributeOverride(FieldMetaData fmd,
+        FieldMetaData orig) {
+        if (orig == null || fmd == orig)
+            return false;
+
+        FieldMapping field = (FieldMapping) fmd;
+        FieldMapping field2 = (FieldMapping) orig;
+        if (field.getMappingInfo().hasSchemaComponents()
+            || field2.getMappingInfo().hasSchemaComponents())
+            return true;
+
+        ValueMappingInfo info = field.getValueInfo();
+        List<Column> cols = (List<Column>) info.getColumns();
+        if (cols == null || cols.size() == 0)
+            return false;
+        ValueMappingInfo info2 = field2.getValueInfo();
+        List<Column> cols2 = (List<Column>) info2.getColumns();
+        if (cols2 == null || cols2.size() != cols.size())
+            return true;
+        if (cols.size() != 1)
+            return true;
+
+        Column col;
+        Column col2;
+        for (int i = 0; i < cols.size(); i++) {
+            col = cols.get(i);
+            col2 = cols2.get(i);
+            if (!StringUtils.equals(col.getName(), col2.getName()))
+                return true;
+            if (!StringUtils.equals(col.getTypeName(), col2.getTypeName()))
+                return true;
+            if (col.getSize() != col2.getSize())
+                return true;
+            if (col.getDecimalDigits() != col2.getDecimalDigits())
+                return true;
+            if (col.getFlag(Column.FLAG_UNINSERTABLE)
+                != col2.getFlag(Column.FLAG_UNINSERTABLE))
+                return true;
+            if (col.getFlag(Column.FLAG_UNUPDATABLE)
+                != col2.getFlag(Column.FLAG_UNUPDATABLE))
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void serializeAttributeOverrideMappingContent(FieldMetaData fmd,
+        FieldMetaData orig, AnnotationBuilder ab) {
+        FieldMapping fm = (FieldMapping) fmd;
+        serializeColumns(fm.getValueInfo(), ColType.COL, fm.getMappingInfo()
+            .getTableName(), ab, fmd);
+    }
+
+    @Override
+    protected PersistenceStrategy getStrategy(FieldMetaData fmd) {
+        PersistenceStrategy strat = super.getStrategy(fmd);
+        FieldMapping field = (FieldMapping) fmd;
+        switch (strat) {
+            case MANY_MANY:
+                // we can differentiate a one-many by the fact that there is no
+                // secondary table join, or the fk is unique
+                if (field.getMappedBy() == null
+                    && (field.getMappingInfo().getJoinDirection()
+                    == MappingInfo.JOIN_NONE
+                    || field.getElementMapping().getValueInfo().getUnique()
+                    != null))
+                    return PersistenceStrategy.ONE_MANY;
+                break;
+            case MANY_ONE:
+                // inverse join cols or unique fk?
+                if (field.getValueInfo().getJoinDirection()
+                    == MappingInfo.JOIN_INVERSE
+                    || field.getValueInfo().getUnique() != null)
+                    return PersistenceStrategy.ONE_ONE;
+
+                // scan for primary-key-join-column
+                List<Column> cols = field.getValueInfo().getColumns();
+                boolean pkJoin = cols != null && cols.size() > 0;
+                for (int i = 0; pkJoin && i < cols.size(); i++)
+                    pkJoin = cols.get(i).getFlag(Column.FLAG_PK_JOIN);
+                if (pkJoin)
+                    return PersistenceStrategy.ONE_ONE;
+                break;
+        }
+        return strat;
+    }
+
+    @Override
+    protected void serializeFieldMappingContent(FieldMetaData fmd,
+        PersistenceStrategy strategy, AnnotationBuilder ab) {
+        if (fmd.getMappedBy() != null)
+            return;
+
+        // while I'd like to do auto detection based on join directions, etc.
+        // the distinguished column / table / etc names forces our hand
+        // esp for OpenJPA custom mappings.
+        FieldMapping field = (FieldMapping) fmd;
+        switch (strategy) {
+            case ONE_ONE:
+            case MANY_ONE:
+                serializeColumns(field.getValueInfo(), ColType.JOIN,
+                    field.getMappingInfo().getTableName(), null, fmd);
+                return;
+            case ONE_MANY:
+                if (field.getMappingInfo().getJoinDirection() ==
+                    MappingInfo.JOIN_NONE) {
+                    serializeColumns(field.getElementMapping().getValueInfo(),
+                        ColType.JOIN, null, null, fmd);
+                    return;
+                }
+                // else no break
+            case MANY_MANY:
+                if (field.getMappingInfo().hasSchemaComponents()
+                    || field.getElementMapping().getValueInfo()
+                    .hasSchemaComponents()) {
+                    AnnotationBuilder abJoinTbl =
+                        addAnnotation(JoinTable.class, fmd);
+                    String table = field.getMappingInfo().getTableName();
+                    if (table != null) {
+                        int index = table.indexOf('.');
+                        if (index < 0)
+                            abJoinTbl.add("name", table);
+                        else {
+                            abJoinTbl.add("schema", table.substring(0, index));
+                            abJoinTbl.add("name", table.substring(index + 1));
+                        }
+                    }
+                    serializeColumns(field.getMappingInfo(),
+                        ColType.JOIN, null, abJoinTbl, null);
+                    serializeColumns(field.getElementMapping().getValueInfo(),
+                        ColType.INVERSE, null, abJoinTbl, null);
+                }
+                return;
+        }
+
+        serializeColumns(field.getValueInfo(), ColType.COL,
+            field.getMappingInfo().getTableName(), null, fmd);
+        if (strategy == PersistenceStrategy.BASIC && isLob(field)) {
+            addAnnotation(Lob.class, fmd);
+        }
+        TemporalType temporal = getTemporal(field);
+        if (temporal != null) {
+            addAnnotation(Temporal.class, fmd).
+                add(null, temporal);
+        }
+
+        EnumType enumType = getEnumType(field);
+        if (enumType != null && enumType != EnumType.ORDINAL) {
+            addAnnotation(Enumerated.class, fmd).
+                add(null, enumType);
+        }
+    }
+
+    /**
+     * Determine if the field is a lob.
+     */
+    private boolean isLob(FieldMapping field) {
+        for (Column col : (List<Column>) field.getValueInfo().getColumns())
+            if (col.getType() == Types.BLOB || col.getType() == Types.CLOB)
+                return true;
+        return false;
+    }
+
+    /**
+     * Return field's temporal type.
+     */
+    private TemporalType getTemporal(FieldMapping field) {
+        if (field.getDeclaredTypeCode() != JavaTypes.DATE
+            && field.getDeclaredTypeCode() != JavaTypes.CALENDAR)
+            return null;
+
+        DBDictionary dict = ((JDBCConfiguration) getConfiguration())
+            .getDBDictionaryInstance();
+        int def = dict.getJDBCType(field.getTypeCode(), false);
+        for (Column col : (List<Column>) field.getValueInfo().getColumns()) {
+            if (col.getType() == def)
+                continue;
+            switch (col.getType()) {
+                case Types.DATE:
+                    return TemporalType.DATE;
+                case Types.TIME:
+                    return TemporalType.TIME;
+                case Types.TIMESTAMP:
+                    return TemporalType.TIMESTAMP;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return enum type for the field.
+     */
+    protected EnumType getEnumType(FieldMapping field) {
+        if (field.getDeclaredTypeCode() != JavaTypes.OBJECT)
+            return null;
+        if (!(field.getHandler() instanceof EnumValueHandler))
+            return null;
+        return ((EnumValueHandler) field.getHandler()).getStoreOrdinal()
+            ? EnumType.ORDINAL : EnumType.STRING;
+    }
+
+    /**
+     * Serialize the columns in the given mapping info.
+     */
+    private void serializeColumns(MappingInfo info, ColType type,
+        String secondary, AnnotationBuilder ab, Object meta) {
+        List<Column> cols = (List<Column>) info.getColumns();
+        if (cols == null)
+            return;
+        AnnotationBuilder abContainer = ab;
+        if (cols.size() > 1) {
+            Class grpType = type.getColumnGroupAnnotationType();
+            if (null != grpType) {
+                AnnotationBuilder abGrp = newAnnotationBuilder(grpType);
+                if (null == ab)
+                    addAnnotation(abGrp, meta);
+                else
+                    ab.add(null, abGrp);
+                abContainer = abGrp;
+            }
+        }
+        for (Column col : cols)
+            serializeColumn(col, type, secondary,
+                info.getUnique() != null, abContainer, meta);
+    }
+
+    /**
+     * Serialize a single column.
+     */
+    private void serializeColumn(Column col, ColType type, String secondary,
+        boolean unique, AnnotationBuilder ab, Object meta) {
+        FieldMetaData fmd = meta instanceof FieldMetaData ?
+            (FieldMetaData) meta : null;
+        AnnotationBuilder abCol = newAnnotationBuilder(
+            type.getColumnAnnotationType());
+        if (col.getName() != null && (null == fmd ||
+            !col.getName().equalsIgnoreCase(fmd.getName())))
+            abCol.add("name", col.getName());
+        if (col.getTypeName() != null)
+            abCol.add("columnDefinition", col.getTypeName());
+        if (col.getTarget() != null
+            && (type == ColType.JOIN || type == ColType.INVERSE
+            || type == ColType.PK_JOIN))
+            abCol.add("referencedColumnName", col.getTarget());
+        if (type == ColType.COL || type == ColType.JOIN
+            || type == ColType.PK_JOIN) {
+            if (unique)
+                abCol.add("unique", true);
+            if (col.isNotNull())
+                abCol.add("nullable", false);
+            if (col.getFlag(Column.FLAG_UNINSERTABLE))
+                abCol.add("insertable", false);
+            if (col.getFlag(Column.FLAG_UNUPDATABLE))
+                abCol.add("updatable", false);
+            if (secondary != null)
+                abCol.add("table", secondary);
+
+            if (type == ColType.COL) {
+                if (col.getSize() > 0 && col.getSize() != 255)
+                    abCol.add("length", col.getSize());
+                if (col.getDecimalDigits() != 0)
+                    abCol.add("scale", col.getDecimalDigits());
+            }
+        }
+
+        if (type != ColType.COL || abCol.hasComponents()) {
+            if (null != ab) {
+                String key = null;
+                if (ab.getType() == JoinTable.class) {
+                    switch(type) {
+                        case JOIN:
+                            key = "joinColumns";
+                            break;
+                        case INVERSE:
+                            key = "inverseJoinColumns";
+                            break;
+                    }
+                }
+                ab.add(key, abCol);
+            } else {
+                addAnnotation(abCol, meta);
+            }                
+        }
+    }
+
+    private void serializeUniqueConstraint(Unique unique,
+        AnnotationBuilder ab) {
+        StringBuilder sb = new StringBuilder();
+        Column[] columns = unique.getColumns();
+        for (Column column:columns) {
+            if (sb.length() > 0)
+                sb.append(", ");
+            sb.append(column.getName());
+        }
+        if (columns.length > 1)
+            sb.insert(0, "{").append("}");
+        ab.add("columnNames", sb.toString());
+    }
+
+    @Override
+    protected SerializationComparator newSerializationComparator() {
+        return new AnnotationPersistenceMappingSerializer.MappingSerializationComparator();
+    }
+
+    @Override
+    protected void addSystemMappingElements(Collection toSerialize) {
+        if (isQueryMode())
+            toSerialize.addAll(getQueryResultMappings(null));
+    }
+
+    @Override
+    protected int type(Object o) {
+        int type = super.type(o);
+        if (type == -1 && o instanceof QueryResultMapping)
+            return TYPE_RESULTMAP;
+        return type;
+    }
+
+    /**
+     * Return the result set mappings for the given scope.
+     */
+    private List<QueryResultMapping> getQueryResultMappings(ClassMetaData cm) {
+        if (_results == null || _results.isEmpty())
+            return (List<QueryResultMapping>) Collections.EMPTY_LIST;
+
+        List<QueryResultMapping> result = null;
+        for (int i = 0; i < _results.size(); i++) {
+            QueryResultMapping element = _results.get(i);
+            if ((cm == null && element.getSourceScope() != null) || (cm != null
+                && element.getSourceScope() != cm.getDescribedType()))
+                continue;
+
+            if (result == null)
+                result = new ArrayList<QueryResultMapping>(_results.size() - i);
+            result.add(element);
+        }
+        return (result == null)
+            ? (List<QueryResultMapping>) Collections.EMPTY_LIST : result;
+    }
+
+    @Override
+    protected void serializeSystemMappingElement(Object obj) {
+        if (obj instanceof QueryResultMapping)
+            serializeQueryResultMapping((QueryResultMapping) obj, null);
+    }
+
+    @Override
+    protected void serializeQueryMappings(ClassMetaData meta) {
+        for (QueryResultMapping res : getQueryResultMappings(meta))
+            serializeQueryResultMapping(res, meta);
+    }
+
+    /**
+     * Serialize given result set mapping.
+     */
+    private void serializeQueryResultMapping(QueryResultMapping meta,
+        ClassMetaData clsmeta) {
+        AnnotationBuilder ab = addAnnotation(SqlResultSetMapping.class, meta);
+        if (null != clsmeta)
+            addAnnotation(ab, clsmeta);
+        ab.add("name", meta.getName());
+        for (QueryResultMapping.PCResult pc : meta.getPCResults()) {
+            AnnotationBuilder abEntRes =
+                newAnnotationBuilder(EntityResult.class);
+            ab.add("entities", abEntRes);
+            abEntRes.add("entityClass", pc.getCandidateType());
+            Object discrim = pc.getMapping(pc.DISCRIMINATOR);
+            if (discrim != null)
+                abEntRes.add("discriminatorColumn", discrim.toString());
+
+            for (String path : pc.getMappingPaths()) {
+                AnnotationBuilder abFldRes =
+                    newAnnotationBuilder(FieldResult.class);
+                abEntRes.add("fields", abFldRes);
+                abFldRes.add("name", path);
+                abFldRes.add("column", pc.getMapping(path).toString());
+            }
+        }
+        for (Object col : meta.getColumnResults()) {
+            AnnotationBuilder abColRes =
+                newAnnotationBuilder(ColumnResult.class);
+            abColRes.add("name", col.toString());
+        }
+    }
+
+    @Override
+    protected void serializeSequence(SequenceMetaData meta) {
+        if (SequenceMapping.IMPL_VALUE_TABLE.equals(meta.getSequencePlugin())) {
+            super.serializeSequence(meta);
+            return;
+        }
+
+        AnnotationBuilder abTblGen = addAnnotation(TableGenerator.class, meta);
+        SequenceMapping seq = (SequenceMapping) meta;
+        abTblGen.add("name", seq.getName());
+        String table = seq.getTable();
+        if (table != null) {
+            int dotIdx = table.indexOf('.');
+            if (dotIdx == -1)
+                abTblGen.add("table", table);
+            else {
+                abTblGen.add("table", table.substring(dotIdx + 1));
+                abTblGen.add("schema", table.substring(0, dotIdx));
+            }
+        }
+        if (!StringUtils.isEmpty(seq.getPrimaryKeyColumn()))
+            abTblGen.add("pkColumnName", seq.getPrimaryKeyColumn());
+        if (!StringUtils.isEmpty(seq.getSequenceColumn()))
+            abTblGen.add("valueColumnName", seq.getSequenceColumn());
+        if (!StringUtils.isEmpty(seq.getPrimaryKeyValue()))
+            abTblGen.add("pkColumnValue", seq.getPrimaryKeyValue());
+        if (seq.getAllocate() != 50 && seq.getAllocate() != -1)
+            abTblGen.add("allocationSize", seq.getAllocate() + "");
+        if (seq.getInitialValue() != 0 && seq.getInitialValue() != -1)
+            abTblGen.add("initialValue", seq.getInitialValue() + "");
+    }
+
+    /**
+     * Column types serialized under different names.
+     */
+    private static enum ColType {
+
+        COL,
+        JOIN,
+        INVERSE,
+        PK_JOIN,
+        DISC;
+
+        private Class<? extends Annotation> getColumnAnnotationType() {
+            switch(this) {
+                case COL:
+                    return javax.persistence.Column.class;
+                case JOIN:
+                case INVERSE:
+                    return JoinColumn.class;
+                case PK_JOIN:
+                    return PrimaryKeyJoinColumn.class;
+                case DISC:
+                    return DiscriminatorColumn.class;
+            }
+            return null;
+        }
+
+        private Class<? extends Annotation> getColumnGroupAnnotationType() {
+            switch(this) {
+                case JOIN:
+                case INVERSE:
+                    return JoinColumns.class;
+                case PK_JOIN:
+                    return PrimaryKeyJoinColumns.class;
+            }
+            return null;
+        }
+
+    }
+
+    /**
+     * Extends {@link SerializationComparator} for store-specific tags such
+     * as &lt;sql-result-set-mapping&gt;.
+     *
+     * @author Pinaki Poddar
+     */
+    protected class MappingSerializationComparator
+        extends SerializationComparator {
+
+        protected int compareUnknown(Object o1, Object o2) {
+            if (!(o1 instanceof QueryResultMapping))
+                return super.compareUnknown(o1, o2);
+
+            QueryResultMapping res1 = (QueryResultMapping) o1;
+            QueryResultMapping res2 = (QueryResultMapping) o2;
+
+            // system scope before class scope
+            Object scope1 = res1.getSourceScope();
+            Object scope2 = res2.getSourceScope();
+            if (scope1 == null && scope2 != null)
+                return -1;
+            if (scope1 != null && scope2 == null)
+                return 1;
+
+            // compare on listing index, or if none/same, use name
+            int listingIndex1 = res1.getListingIndex();
+            int listingIndex2 = res2.getListingIndex();
+            if (listingIndex1 != listingIndex2)
+                return listingIndex1 - listingIndex2;
+            return res1.getName ().compareTo (res2.getName ());
+		}
+	}
+}
Index: openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java
===================================================================
--- openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java	(revision 549199)
+++ openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/PersistenceMappingFactory.java	(working copy)
@@ -21,10 +21,11 @@
 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.meta.MappingRepository;
 import org.apache.openjpa.meta.MetaDataFactory;
-import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser;
 import org.apache.openjpa.persistence.PersistenceMetaDataFactory;
 import org.apache.openjpa.persistence.XMLPersistenceMetaDataParser;
 import org.apache.openjpa.persistence.XMLPersistenceMetaDataSerializer;
+import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser;
+import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataSerializer;
 
 /**
  * {@link MetaDataFactory} for JPA mapping information.
@@ -48,6 +49,15 @@
         return parser;
     }
 
+    protected AnnotationPersistenceMetaDataSerializer newAnnotationSerializer()
+    {
+        AnnotationPersistenceMappingSerializer ser =
+            new AnnotationPersistenceMappingSerializer((JDBCConfiguration)
+            repos.getConfiguration());
+        ser.setSyncMappingInfo(true);
+        return ser;
+    }
+
     @Override
     protected XMLPersistenceMetaDataParser newXMLParser(boolean loading) {
         XMLPersistenceMappingParser parser = new XMLPersistenceMappingParser
