@Override
  public void createDateField(
      ClassOrInterfaceTypeDetails javaTypeDetails,
      JavaType fieldType,
      JavaSymbolName fieldName,
      boolean notNull,
      boolean nullRequired,
      boolean future,
      boolean past,
      DateFieldPersistenceType persistenceType,
      String column,
      String comment,
      DateTime dateFormat,
      DateTime timeFormat,
      String pattern,
      String value,
      boolean permitReservedWords,
      boolean transientModifier,
      List<AnnotationMetadataBuilder> extraAnnotations) {
    final String physicalTypeIdentifier = javaTypeDetails.getDeclaredByMetadataId();
    final DateField fieldDetails = new DateField(physicalTypeIdentifier, fieldType, fieldName);
    fieldDetails.setNotNull(notNull);
    fieldDetails.setNullRequired(nullRequired);
    fieldDetails.setFuture(future);
    fieldDetails.setPast(past);
    if (JdkJavaType.isDateField(fieldType)) {
      fieldDetails.setPersistenceType(persistenceType);
    }
    if (column != null) {
      fieldDetails.setColumn(column);
    }
    if (comment != null) {
      fieldDetails.setComment(comment);
    }
    if (dateFormat != null) {
      fieldDetails.setDateFormat(dateFormat);
    }
    if (timeFormat != null) {
      fieldDetails.setTimeFormat(timeFormat);
    }
    if (pattern != null) {
      fieldDetails.setPattern(pattern);
    }
    if (value != null) {
      fieldDetails.setValue(value);
    }

    if (extraAnnotations != null && !extraAnnotations.isEmpty()) {
      fieldDetails.addAnnotations(extraAnnotations);
    }

    insertField(fieldDetails, permitReservedWords, false);
  }
  /**
   * Returns the type of ID that DBRE should use for the given entity
   *
   * @param entity the entity for which to get the ID type (required)
   * @return a non-<code>null</code> ID type
   */
  private JavaType getIdentifierType(final JavaType entity) {
    final PhysicalTypeMetadata governorPhysicalTypeMetadata = getPhysicalTypeMetadata(entity);
    if (governorPhysicalTypeMetadata != null) {
      final ClassOrInterfaceTypeDetails governorTypeDetails =
          governorPhysicalTypeMetadata.getMemberHoldingTypeDetails();
      final AnnotationMetadata jpaAnnotation = getJpaAnnotation(governorTypeDetails);
      if (jpaAnnotation != null) {
        final AnnotationAttributeValue<?> identifierTypeAttribute =
            jpaAnnotation.getAttribute(new JavaSymbolName(IDENTIFIER_TYPE));
        if (identifierTypeAttribute != null) {
          // The identifierType attribute exists, so get its value
          final JavaType identifierType = (JavaType) identifierTypeAttribute.getValue();
          if (identifierType != null && !JdkJavaType.isPartOfJavaLang(identifierType)) {
            return identifierType;
          }
        }
      }
    }

    // The JPA annotation's "identifierType" attribute does not exist or is
    // not a simple type, so return a default
    return new JavaType(entity.getFullyQualifiedTypeName() + PRIMARY_KEY_SUFFIX);
  }
  /** @return a test for the merge method, if available and requested (may return null) */
  private MethodMetadataBuilder getMergeMethodTest(
      final MemberTypeAdditions mergeMethod,
      final MemberTypeAdditions findMethod,
      final MemberTypeAdditions flushMethod,
      final MethodMetadata versionAccessorMethod,
      final MethodMetadata identifierAccessorMethod) {
    if (!annotationValues.isMerge()
        || mergeMethod == null
        || versionAccessorMethod == null
        || findMethod == null
        || identifierAccessorMethod == null) {
      // User does not want this method, or core dependencies are missing
      return null;
    }

    // Prepare method signature
    JavaSymbolName methodName =
        new JavaSymbolName("test" + StringUtils.capitalize(mergeMethod.getMethodName()) + "Update");
    if (governorHasMethod(methodName)) {
      return null;
    }

    JavaType versionType = versionAccessorMethod.getReturnType();
    builder
        .getImportRegistrationResolver()
        .addImports(identifierAccessorMethod.getReturnType(), versionType);

    List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
    annotations.add(new AnnotationMetadataBuilder(TEST));

    String entityName = annotationValues.getEntity().getSimpleTypeName();

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine(
        entityName
            + " obj = dod."
            + dataOnDemandMetadata.getRandomPersistentEntityMethod().getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to initialize correctly\", obj);");
    bodyBuilder.appendFormalLine(
        identifierAccessorMethod.getReturnType().getSimpleTypeName()
            + " id = obj."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "();");
    bodyBuilder.appendFormalLine(
        "Assert.assertNotNull(\"Data on demand for '"
            + entityName
            + "' failed to provide an identifier\", id);");
    bodyBuilder.appendFormalLine("obj = " + findMethod.getMethodCall() + ";");
    bodyBuilder.appendFormalLine(
        "boolean modified =  dod."
            + dataOnDemandMetadata.getModifyMethod().getMethodName().getSymbolName()
            + "(obj);");

    bodyBuilder.appendFormalLine(
        versionAccessorMethod.getReturnType().getSimpleTypeName()
            + " currentVersion = obj."
            + versionAccessorMethod.getMethodName().getSymbolName()
            + "();");

    String castStr = entityHasSuperclass ? "(" + entityName + ")" : "";
    bodyBuilder.appendFormalLine(
        entityName + " merged = " + castStr + mergeMethod.getMethodCall() + ";");

    if (flushMethod != null) {
      bodyBuilder.appendFormalLine(flushMethod.getMethodCall() + ";");
      flushMethod.copyAdditionsTo(builder, governorTypeDetails);
    }

    bodyBuilder.appendFormalLine(
        "Assert.assertEquals(\"Identifier of merged object not the same as identifier of original object\", merged."
            + identifierAccessorMethod.getMethodName().getSymbolName()
            + "(), id);");
    if (JdkJavaType.isDateField(versionType)) {
      bodyBuilder.appendFormalLine(
          "Assert.assertTrue(\"Version for '"
              + entityName
              + "' failed to increment on merge and flush directive\", (currentVersion != null && obj."
              + versionAccessorMethod.getMethodName().getSymbolName()
              + "().after(currentVersion)) || !modified);");
    } else {
      bodyBuilder.appendFormalLine(
          "Assert.assertTrue(\"Version for '"
              + entityName
              + "' failed to increment on merge and flush directive\", (currentVersion != null && obj."
              + versionAccessorMethod.getMethodName().getSymbolName()
              + "() > currentVersion) || !modified);");
    }
    mergeMethod.copyAdditionsTo(builder, governorTypeDetails);
    findMethod.copyAdditionsTo(builder, governorTypeDetails);

    MethodMetadataBuilder methodBuilder =
        new MethodMetadataBuilder(
            getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, bodyBuilder);
    methodBuilder.setAnnotations(annotations);
    return methodBuilder;
  }