brettwooldridge / SansOrm

A "No-ORM" sane SQL ←→ Java object mapping library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Inconsistent behaviour for Id fields that also have an AttributeConverter

tijmenr opened this issue · comments

For entity classes whose Id fields also have a Converter attribute, the behaviour of SansOrm is buggy (or at least inconsitent):

  • When persisting an entity, e.g. via OrmElf->OrmWriter.writeObject(), the actual id value written to the database is determined via Introspected.get(target, FieldColumnInfo), which, as expected, applies the AttributeConverter.toDatabaseColumn() (or Enum conversion).
  • When requesting that entity using OrmElf->OrmReader.getObjectById(), using (ofcourse) the "java-side" id value of the entity, it won't be found, since getObjectById() just uses the given value as bind parameter, without applying the appropriate toDatabaseColumn() converter first.

For OrmReader.getObjectById() specific, a quick and dirty fix could be:

   ...

   public static <T> T objectById(final Connection connection, final Class<T> clazz, final Object... args) throws SQLException
   {
      String where = getWhereIdClause(Introspector.getIntrospected(clazz), args);
      return objectFromClause(connection, clazz, where, args);
   }

   ...

   private static String getWhereIdClause(Introspected introspected, final Object... args) {
      final StringBuilder where = new StringBuilder();
      List<FieldColumnInfo> idFcInfos = introspected.getIdFcInfos();
      int i = 0;
      for (FieldColumnInfo fcInfo : idFcInfos) {
         where.append(fcInfo.getColumnName()).append("=? AND ");
         if (i < args.length) {
            // Maybe need to convert the argument, using same logic as Introspected.get()
            // Note: modifying the given args array!
            if (fcInfo.getConverter() != null) {
               args[i] = fcInfo.getConverter().convertToDatabaseColumn(args[i]);
            } else if (fcInfo.enumConstants != null && args[i] != null) {
               args[i] = (fcInfo.enumType == EnumType.ORDINAL ? ((Enum<?>) args[i]).ordinal() : ((Enum<?>) args[i]).name());
            }
         }
         i++;
      }
      // the where clause can be length of zero if we are loading an object that is presumed to
      // be the only row in the table and therefore has no id.
      if (where.length() > 0) {
      ...

However:

  • The same issue also pops up in other places, for example in OrmWriter (setParamsExecute, deleteObject, updateObject), or maybe Introspected.getActualIds, so a more structural solution seems appropriate.
  • It would more or less duplicate existing conversion code currently in Introspected.get; maybe it's better to create a conversion function in that class.

Any ideas on this?

This sounds like a reasonable approach. Pull requests welcome.

As I understand the JPA specification such behaviour would be less advisable: "The Convert annotation should not be used to specify conversion of the following: Id attributes (including the attributes of embedded ids and derived identities), version attributes, relationship attributes, and attributes explicitly annotated (or designated via XML) as Enumerated or Temporal. Applications that specify such conversions will not be portable." See JSR 338: JavaTM Persistence API, Version 2.2.