Hibernate Naming Strategie für Oracle die zweite

Vor einiger Zeit habe ich eine einfache Naming Strategie vorgestellt, die sicherstellt, dass die generierten Namen sich an die etwas geizigen Beschränkungen von Oracle hält. Ich habe die Starategie noch einmal ein wenig überarbeitet. Sie verteilt jetzt großzügiger Underscores, so dass sie mehr Stellen hat, an denen sie einen Namen kürzen kann.

Natürlich gilt wieder: Benutzung auf eigene Gefahr.

public class OracleNamingStrategy
        extends DefaultComponentSafeNamingStrategy
{

  protected static String addUnderscores(String name)
  {
    if (name == null) return null;
    StringBuffer buf = new StringBuffer(
            name.replace('.', '_')
    );
    for (int i = 1; i < buf.length() - 1; i++)
    {
      if (Character.isLowerCase(buf.charAt(i - 1)) &&
          Character.isUpperCase(buf.charAt(i))
      // && Character.isLowerCase(buf.charAt(i + 1))
      )
      {
        buf.insert(i++, '_');
      }
    }
    return buf.toString().toLowerCase();
  }

  @Override
  public String collectionTableName(String ownerEntity,
      String ownerEntityTable, String associatedEntity,
      String associatedEntityTable, String propertyName)
  {
    return abbreviateName(super.collectionTableName(
        addUnderscores(ownerEntity),
        addUnderscores(ownerEntityTable),
        addUnderscores(associatedEntity),
        addUnderscores(associatedEntityTable),
        addUnderscores(propertyName)));
  }

  @Override
  public String foreignKeyColumnName(String propertyName,
      String propertyEntityName, String propertyTableName,
      String referencedColumnName)
  {
    return abbreviateName(super.foreignKeyColumnName(
        addUnderscores(propertyName),
        addUnderscores(propertyEntityName),
        addUnderscores(propertyTableName),
        addUnderscores(referencedColumnName)));
  }

  @Override
  public String logicalCollectionColumnName(
      String columnName,
      String propertyName, String referencedColumn)
  {
    return abbreviateName(super.logicalCollectionColumnName(
        addUnderscores(columnName),
        addUnderscores(propertyName),
        addUnderscores(referencedColumn)));
  }

  @Override
  public String logicalCollectionTableName(String tableName,
      String ownerEntityTable,
      String associatedEntityTable, String propertyName)
  {
    return abbreviateName(super.logicalCollectionTableName(
        addUnderscores(tableName),
        addUnderscores(ownerEntityTable),
        addUnderscores(associatedEntityTable),
        addUnderscores(propertyName)));
  }

  @Override
  public String logicalColumnName(String columnName,
        String propertyName)
  {
    return abbreviateName(super.logicalColumnName(
        addUnderscores(columnName),
        addUnderscores(propertyName)));
  }

  @Override
  public String propertyToColumnName(String propertyName)
  {
    return abbreviateName(super.propertyToColumnName(
          addUnderscores(propertyName)));
  }

  private static final int MAX_LENGTH = 30;

  public static String abbreviateName(String someName)
  {
    if (someName.length() <= MAX_LENGTH) return someName;

    String[] tokens = splitName(someName);
    shortenName(someName, tokens);

    return assembleResults(tokens);
  }

  private static String[] splitName(String someName)
  {
    StringTokenizer toki = new StringTokenizer(someName, "_");
    String[] tokens = new String[toki.countTokens()];
    int i = 0;
    while (toki.hasMoreTokens())
    {
      tokens[i] = toki.nextToken();
      i++;
    }
    return tokens;
  }

  private static void shortenName(
       String someName, String[] tokens)
  {
    int currentLength = someName.length();
    while (currentLength > MAX_LENGTH)
    {
      int tokenIndex = getIndexOfLongest(tokens);
      String oldToken = tokens[tokenIndex];
      tokens[tokenIndex] = abbreviate(oldToken);
      currentLength -=
         oldToken.length() - tokens[tokenIndex].length();
    }
  }

  private static String assembleResults(String[] tokens)
  {
    StringBuilder result = new StringBuilder(tokens[0]);
    for (int j = 1; j < tokens.length; j++)
    {
      result.append("_").append(tokens[j]);
    }
    return result.toString();
  }

  private static String abbreviate(String token)
  {
    final String VOWELS = "AEIOUaeiou";
    boolean vowelFound = false;
    for (int i = token.length() - 1; i >= 0; i--)
    {
      if (!vowelFound)
        vowelFound = VOWELS.contains(
            String.valueOf(token.charAt(i)));
      else if (!VOWELS.contains(
            String.valueOf(token.charAt(i))))
        return token.substring(0, i + 1);
    }
    return "";
  }

  private static int getIndexOfLongest(String[] tokens)
  {
    int maxLength = 0;
    int index = -1;
    for (int i = 0; i < tokens.length; i++)
    {
      String string = tokens[i];
      if (maxLength < string.length())
      {
        maxLength = string.length();
        index = i;
      }
    }
    return index;
  }

  @Override
  public String classToTableName(String aClassName)
  {

    return abbreviateName(super.classToTableName(addUnderscores(aClassName)));
  }
}


Share:
  • DZone
  • Digg
  • del.icio.us
  • Reddit
  • Facebook
  • Twitter
This entry was written by Jens Schauder , posted on Saturday March 29 2008at 11:03 pm , filed under Java, Oracle . Bookmark the permalink . Post a comment below or leave a trackback: Trackback URL.

3 Responses to “Hibernate Naming Strategie für Oracle die zweite”

  • Ziombel says:

    English please :P

  • You might be interested in the entry announcing the first release of the Hibernate NamingStrategy for Oracle:

    http://blog.schauderhaft.de/2009/03/07/oracle-naming-strategy-for-hibernate-released-as-a-proper-open-source-project/

    And of course the Open Source Project

    http://code.google.com/p/hibernate-naming-strategy-for-oracle/

  • Dominik S. says:

    Nochmals Nachtrag (bezieht sich auf meinen Post im anderen Blogeintrag zum Thema): Für mich stimmt’s wenn ich die Methode isMultipleUpperToLower in addUnderscores rausnehme. Ich habe das Ganze übrigens auf NHibernate und C# übertragen:

    
        public class OracleNamingStrategy : INamingStrategy
        {
            public string ClassToTableName(string className)
            {
                return AbbreviateName(TableName(StringHelper.Unqualify(className)));
            }
    
            public string PropertyToColumnName(string propertyName)
            {
                return AbbreviateName(AddUnderscores(propertyName));
            }
    
            public string TableName(string tableName)
            {
                return AbbreviateName(AddUnderscores(tableName));
            }
    
            public string ColumnName(string columnName)
            {
                return AbbreviateName(AddUnderscores(columnName));
            }
    
            public string PropertyToTableName(string className, string propertyName)
            {
                string tableName = "Lt";
                tableName += className.Substring(className.LastIndexOf(".")).Remove(0,1);
                tableName += propertyName.Substring(propertyName.LastIndexOf(".")).Remove(0,1);
                return AbbreviateName(AddUnderscores(tableName));
            }
    
            public string LogicalColumnName(string columnName, string propertyName)
            {
                return ColumnName(columnName) + '_' + PropertyToColumnName(propertyName);
            }
    
            private static string AddUnderscores(string name)
            {
                if (name == null)
                    return null;
                StringBuilder buf = new StringBuilder(name.Replace('.', '_'));
                for (int i = 1; i < buf.Length - 1; i++)
                {
                    if ((IsLowerToUpper(buf, i)))
                    {
                        buf.Insert(i++, '_');
                    }
                }
                return buf.ToString().ToLower();
            }
    
            private static bool IsMultipleUpperToLower(StringBuilder buf, int i)
            {
                return i > 1 && char.IsUpper(buf[i - 1])
                        && char.IsUpper(buf[i - 2])
                        && char.IsLower(buf[i]);
            }
    
            private static bool IsLowerToUpper(StringBuilder buf, int i)
            {
                return char.IsLower(buf[i - 1])
                        && char.IsUpper(buf[i]);
            }
    
            private static int MAX_LENGTH = 30;
    
            public static string AbbreviateName(string someName)
            {
                if (someName.Length <= MAX_LENGTH)
                    return someName;
    
                string[] tokens = SplitName(someName);
                ShortenName(someName, tokens);
    
                return AssembleResults(tokens);
            }
    
            private static string[] SplitName(string someName)
            {
                string[] tokens = someName.Split(char.Parse("_"));
                return tokens;
            }
    
            private static void ShortenName(string someName, string[] tokens)
            {
                int currentLength = someName.Length;
                while (currentLength > MAX_LENGTH)
                {
                    int tokenIndex = GetIndexOfLongest(tokens);
                    string oldToken = tokens[tokenIndex];
                    tokens[tokenIndex] = Abbreviate(oldToken);
                    currentLength -= oldToken.Length - tokens[tokenIndex].Length;
                }
            }
    
            private static string AssembleResults(string[] tokens)
            {
                StringBuilder result = new StringBuilder(tokens[0]);
                for (int j = 1; j < tokens.Length; j++)
                {
                    result.Append("_").Append(tokens[j]);
                }
                return result.ToString();
            }
    
            private static string Abbreviate(string token)
            {
                string VOWELS = "AEIOUaeiou";
                bool vowelFound = false;
                for (int i = token.Length - 1; i >= 0; i--)
                {
                    if (!vowelFound)
                        vowelFound = VOWELS.Contains(token[i].ToString());
                    else if (!VOWELS.Contains(token[i].ToString()))
                        return token.Substring(0, i + 1);
                }
                return "";
            }
    
            private static int GetIndexOfLongest(string[] tokens)
            {
                int maxLength = 0;
                int index = -1;
                for (int i = 0; i < tokens.Length; i++)
                {
                    string tString = tokens[i];
                    if (maxLength < tString.Length)
                    {
                        maxLength = tString.Length;
                        index = i;
                    }
                }
                return index;
            }
    
        }
    

    Es hat darin noch eine kleine Modifikation: Collection tables werden bei uns mit einem vorangestellten “lt_” benannt.

    Hoffe, das nützt jemandem.

    – English summary:

    I’ve translated the NamingStrategy for Oracle to NHibernate and C#. Small additions:
    - I’m not using the IsMultipleUpperToLower() method in AddUnderscores() because I’m not happy with the result when there’s a bunch of consecutive uppercase characters present.
    - We want our collection tables to be called lt_* so the above code reflects that.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>