Donnerstag, 3. Mai 2012

Java: Entfernen bestehender Hibernate-IDs vor dem erneuten Speichern

In meinem derzeitigen Projekt existiert die Anforderung, ein Angebot zu clonen und die erzeugte Kopie in der Datenbank abzuspeichern. Da wir Hibernate benutzen, um die PKs zu erzeugen und verwalten, führt das komplette Clonen natürlich zu einer Exception beim Speichern, da die IDs bereits vergeben sind. Anstatt nun manuell durch alle Domänen-Klassen zu iterieren und die ID zu entfernen, habe ich mir eine kleine Methode geschrieben, die mit Hilfe von Reflection und Rekursion die Arbeit für mich erledigt. Die Voraussetzungen für das Funktionieren dieser Methode sind folgendermaßen:


  • Die Domänenklassen müssen alle von der Klasse BaseEntity erben (dort stehen bei uns bspw. Versionshinweise)
  • Der Primary Key darf nicht zusammengesetzt sein (die Methode ist allerdings leicht um diese Funktionalität erweiterbar)
  • Der Name des Primary-Key Feldes muss id lauten
  • Die getter/setter müssen dem Bean-Standard folgen
Hier ist nun die Methode; aus Gründen besserer Lesbarkeit habe ich die Exceptions einfach weitergeworfen:
  
 public static Object stripID(Object object) throws IllegalAccessException, IllegalArgumentException,
            SecurityException, InvocationTargetException, NoSuchMethodException {
        if(object instanceof Collection){
            Iterator it = ((Collection) object).iterator();
            while(it.hasNext()){
                Object obj = it.next();
                if(obj instanceof BaseEntity){
                    stripID(obj);
                } else{
                    break;
                }
            }
        } else if(object instanceof BaseEntity){
            Field[] fields = object.getClass().getDeclaredFields();
            for(Field field : fields){
                if(field.getName().equals("id")){
                    field.setAccessible(true);
                    field.set(object, null);
                    field.setAccessible(false);
                } else{
                    StringBuilder fieldName = new StringBuilder();
                    fieldName.append(field.getName());
                    fieldName.replace(0, 1, fieldName.substring(0, 1).toUpperCase());

                    try{
                            stripID(object.getClass().getMethod("get" + fieldName.toString()).invoke(object));
                    } catch (Exception e){
                        log.info("Method not found. Continuing with next field.");
                    }
                }
            }
        }
        return object;
    }

Erläuterungen

Zeilen 3-12: In diesem Code-Block wird geprüft, ob es sich bei dem übergebenen Objekt um eine Collection handelt; ist dies der Fall wird durch die einzelnen Einträge der Collection iteriert, nachgeschaut, ob es sich um eine Instanz der Klasse BaseEntity handelt und falls dies der Fall ist, die Methode mit dem Eintrag aus der Collection rekursiv aufgerufen.
Zeile 13: Falls es sich nicht um eine Collection handelt, wird geprüftt, ob das übergebene Objekt eine Instanz der Klasse BaseEntity ist. Ist dies nicht der Fall, wird die Methode verlassen.
Zeile 14: Alle Felder des übergebenen Objekts werden ausgelesen.
Zeilen 15-32: Es wird durch alle Felder iteriert. Lautet der Name des Feldes id, wird der Inhalt auf null gesetzt, lautet der Name anders, wird der erste Buchstabe des Feldnamens in einen Großbuchstaben umgewandelt, ein get vor den Feldnamen gesetzt und per Reflection die so erzeugte get-Methode aufgerufen und das Objekt per Rekursion stripID übergeben. Sollte der getter nicht existieren oder das Konstrukt nicht instanziierbar sein, wird die Exception gefangen, eine Nachricht ausgegeben und mit dem nächsten Feld fortgefahren.

Wenn die Rekursion beendet ist, hat man den ganzen Objektbaum durchlaufen und bekommt ein um die id bereinigtes Domänenmodell zurück.

Wie oben bereits angemerkt, lässt sich die Methode stripID leicht erweitern:
  • Durch ein weiteres if-statement können zusammengesetzte Primary Keys gelöscht werden.
  • Durch instanceof-Abfragen können die IDs bestimmter Objekte erhalten bleiben.
  • Arrays werden bislang nicht berücksichtigt.

Keine Kommentare:

Kommentar veröffentlichen