In dem Projekt in dem ich aktuell arbeite, generieren wir unser Datenbank praktisch vollständig per Hibernate Annotations. Was die Benennung von Tabellen und Spalten angeht, verlassen wir uns dabei praktisch vollständig auf die Standards von Hibernate, die recht brauchbar sind. Eigentlich gibt es nur zwei echte Probleme und eine kleinere Unanehmlichkeit:
- Oracle unterstützt nur Spaltennamen und Tabellennamen bis zu einer Länge von 30 Zeichen (Ende der 70er war dies vermutlich up-to-date oder sogar fortschrittlich).
- Werden mehrere gleichartige Objekte 'Embedded' in einer Entität so gibt es einen Namenskonflikt.
- Standardmäßig geht die Gliederung eines Namens durch CamelCasing verloren, wenn er in Tabellen und Spaltennamen umgewandelt werden. (z.B. wird aus tollerAttributName einfach TOLLERATTRIBUTNAME. Viele würden TOLLER_ATTRIBUT_NAME bevorzugen.
Für Problem 2. und 3. gibt es eine fertige Lösung: DefaultComponentSafeNamingStrategy, die einfach vor der Erzeugung der SessionFactory in der Konfiguration gesetzt wird:
Configuration config = new AnnotationConfiguration().configure();
config.setNamingStrategy(new DefaultComponentSafeNamingStrategy());
sessionFactory = config.buildSessionFactory();
Auf der Suche nach dieser Lösung bin ich auch bei ein paar Seiten vorbeigekommen, bei denen Namenskonvention mit Hilfe einer Namingstrategy umgesetzt werden.
we extended the ImprovedNamingStrategy further to handle pluralization of tablenames
Das verletzt zwar das Prinzip: Don't repeat yourself, denn niemand würde wohl erwarten, dass die CUSTOMER Tabelle nur einen Kunden enthält, aber wer's mag. Immer noch besser als Code Obfuscation
class: tr.PopulationCenter à table: T_PPLTN_CNTR
property: upperPopulationCenter à column: UPPR_PPLTN_CNTR
Ganz Abseits von der länglichen Diskussion über Codeconventionen, so ist die Idee, sie per NamingStrategy umzusetzen, doch definitiv eine Gute. Ich habe mein neu gewonnenes Wissen erstmal ausprobiert um eine Namingstrategy zu konstruieren, die Tabellen und Spaltennamen auf 30 Zeichen kürzt, hoffentlich ohne dabei völlig unleserlich zu werden. Der Algorithmus wandelt Namen wie Auftragsbestaetigung_Rueckantwort_lineitem um in Auftragsb_Rueckantw_lineitem. Der Algorithmus sucht nach dem längsten Teilstück und kürzt es um die letzen Vokale inklusive eventuell nachfolgender Konsonanten. Dies wird wiederholt, bis der Name maximal noch 30 Zeichen lang ist.
Auftragsbestaetigung aus dem obigen Beispiel wird also der Reihe nach Auftragsbestaetig, Auftragsbestaet, Auftragsbest und schließlich Auftragsbest
import java.util.StringTokenizer;
import org.hibernate.cfg.DefaultComponentSafeNamingStrategy;
public class OracleNamingStrategy
extends DefaultComponentSafeNamingStrategy
{@Override
public String collectionTableName(String ownerEntity,
String ownerEntityTable, String associatedEntity,
String associatedEntityTable, String propertyName)
{
return abbreviateName(super.collectionTableName(
ownerEntity,
ownerEntityTable, associatedEntity,
associatedEntityTable, propertyName));
}@Override
public String foreignKeyColumnName(String propertyName,
String propertyEntityName, String propertyTableName,
String referencedColumnName)
{
return abbreviateName(super.foreignKeyColumnName(
propertyName,
propertyEntityName, propertyTableName,
referencedColumnName));
}@Override
public String logicalCollectionColumnName(
String columnName,
String propertyName,
String referencedColumn)
{
return abbreviateName(super.logicalCollectionColumnName(
columnName,
propertyName, referencedColumn));
}@Override
public String logicalCollectionTableName(String tableName,
String ownerEntityTable, String associatedEntityTable,
String propertyName)
{
return abbreviateName(super.logicalCollectionTableName(
tableName,
ownerEntityTable, associatedEntityTable,
propertyName));
}@Override
public String logicalColumnName(String columnName,
String propertyName)
{
return abbreviateName(super.logicalColumnName(
columnName, propertyName));
}@Override
public String propertyToColumnName(String propertyName)
{
return abbreviateName(
super.propertyToColumnName(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;
}
}
Das ganze ist im Halbschlaf entstanden und weder gründlich getestet noch intensiv genutzt worden. Benutzung also auf eigene Gefahr. Sollte es sich aber als praktisch erweisen, so wie es ist, oder in abgewandelter Form so würde ich mich sehr über einen entsprechenden Kommentar freuen
Talks
Wan't to meet me in person to tell me how stupid I am? You can find me at the following events: