-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 
Author Message
 Post subject: Dynamische Konfiguration
PostPosted: Fri Apr 01, 2011 2:02 pm 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Hallo,

leider konnte ich zu meinem Problem bisher nichts hilfreiches finden. Eventuell liegt es auch daran, dass ich es nicht richtig benennen kann, um danach zu suchen. Ich arbeite zurzeit an einem Projekt mit Struts 2 und Hibernate. Ziel ist es, dass über eine XML-Datei nur Tabellennamen übergeben werden und alles andere dynamisch generiert wird. Ich möchte am Ende eine Weboberflaeche haben, die die Daten über ein CRUD-Interrface verwalten kann. Man kann es sich vorstellen wie ein Datenbank-Tool, das einem das Schema und die Inhalte von Tabellen anzeigt. Diese müssen ja auch dynamisch anhand der Metadaten der Datenbank generiert werden.

Leider denke ich, dass dies mit Hibernate nicht möglich ist, aber ich würde gerne noch einmal um Rat fragen, da es auch sein kann, dass ich wichtige Funktionen übersehen habe. Das was ich bisher rausfinden konnte ist, dass es dynamische Models gibt. Um diese aber verwenden zu können, müssen trotzdem zusaetzliche XML-Konfigurationen durchgeführt werden. Außerdem scheint dieser Bereich von Hibernate noch experimentell zu sein.

Ich hoffe, ich konnte das Problem verstaendlich ausdrücken. Hoffentlich kann mir jemand einen Hinweis geben, ob mein Vorhaben mit Hibernate möglich ist.

Gruß
JudGer


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Mon Apr 04, 2011 3:04 am 
Senior
Senior

Joined: Tue Oct 28, 2008 10:39 am
Posts: 196
Moin,
du kannst dynamisch Tabellen mappen, ich habe sowas auch mal gesucht und mir dann aus mehreren Quellen (die ich nicht mehr finde) was zusammengestrickt. Im Prinzip funktioniert das so, dass du eine abstrakte Entity hast, die nur eine ID hat und eine Map<String, Object>, die die restlichen Spalten aufnehmen soll. Mit dem Erzeugen der SessionFactory (vor buildSessionFactory()) kannst du dann noch in das Mapping eingreifen und zusätzliche Spalten in die Map einfügen und diese auf Tabellenspalten mappen.

Wenn du Interesse hast, kann ich mal schauen, ob ich irgendwo die Sourcen von meinem Test damals noch finde. Ich habs damals dann irgendwann unter den Tisch fallen lassen, weil ich dringenderes zu tun hatte. Mein Versuch beschränkte sich darauf, dass ich eine beliebige (mir aber in ihrer Struktur bekannte) Tabelle so nachgebildet habe, dass das Mapping erst zur Laufzeit erzeugt wird. Du müsstest das quasi noch selbst aus den Metadaten rausfinden. Beziehungen zwischen Tabellen hatte ich nicht versucht.


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Mon Apr 04, 2011 6:47 am 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Huhu,

vielen Dank schonmal für deinen Hinweis. Ist das zufällig das hier? Wäre klasse, wenn du die Zeit finden kannst, ein kleines Beispiel zu posten. Hört sich jedenfalls so an, als ob es genau auf mein Problem passen würde :)

Danke


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Mon Apr 04, 2011 7:07 am 
Senior
Senior

Joined: Tue Oct 28, 2008 10:39 am
Posts: 196
Ich habe mal ein wenig gegraben...

Wir haben eine Entity. Das ist die einzige Klasse, die wir mappen!
Code:
package domain;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.Transient;

@Entity
public class AbstractEntity {

   @Id
   @Column(name = "ABSTRACT_ID")
   private Long id;

   @Transient
   private Map<String, Object> dynamicAttributes = null;

   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   @PostLoad
   public void postload() {
      if (dynamicAttributes == null || dynamicAttributes.isEmpty()) {
         dynamicAttributes = new HashMap<String, Object>();
      }
   }

   public Map<String, Object> getDynamicAttributes() {
      return dynamicAttributes;
   }

   public void setDynamicAttributes(Map<String, Object> dynamicAttributes) {
      this.dynamicAttributes = dynamicAttributes;
   }

   public void setDynamicProperty(String propertyName, Object value) {
      dynamicAttributes.put(propertyName, value);
   }

   @Override
   public String toString() {
      return "AbstractEntity [dynamicAttributes=" + dynamicAttributes
            + ", id=" + id + "]";
   }

}



Ich habe mich hier auf einen Long für die ID festgelegt, weil wir in unserem Modell nur generische, über eine Sequence erzeugte IDs haben.

Code:
package test;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.criterion.Restrictions;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;

import hibernate.HibernateUtil;
import domain.AbstractEntity;

public class TestAbstract {

   /**
    * @param args
    */
   @SuppressWarnings("unchecked")
   public static void main(String[] args) {
      AnnotationConfiguration cfg = HibernateUtil.getConfiguration();
      PersistentClass clazz = cfg
            .getClassMapping(domain.AbstractEntity.class
                  .getName());

      // Lege den Namen der neuen Tabelle fest
      String tablename = "TABLE_BLA";
      clazz.getIdentifier().getTable().setName(tablename);

      // Legt den Spaltennamen der ID-Spalte fest
      Property identifierProperty = clazz.getIdentifierProperty();
      Iterator<?> columnIterator = identifierProperty.getColumnIterator();
      while (columnIterator.hasNext()) {
         Column col = (Column) columnIterator.next();
         col.setName("FIRST_ID");
      }
      
      // Mappt die Map für die neuen Spalten
      Component component = new Component(clazz);
      component.setTable(clazz.getTable());

      Property dynamicProperty = new Property();
      dynamicProperty.setName("dynamicAttributes");
      dynamicProperty.setNodeName("dynamicAttributes");
      dynamicProperty.setValue(component);

      clazz.addProperty(dynamicProperty);

      // Legt eine neue Spalte an
      Column column = new Column("FIRST_NAME");
      column.setLength(50);
      clazz.getTable().addColumn(column);

      // Setzt den Typ der Spalte als String
      SimpleValue value = new SimpleValue();
      value.setTable(clazz.getTable());
      value.addColumn(column);
      value.setTypeName("string");

      // Mappt die eigentliche Property auf den Namen "name"
      Property property = new Property();
      property.setName("name");
      property.setNodeName("name");
      property.setValue(value);

      // Legt die neue Spalte als Paar in der Map ab
      component.addProperty(property);

      // Legt eine neue Spalte an
      Column column2 = new Column("ALTER_P");
      column2.setPrecision(2);
      column2.setLength(5);
      clazz.getTable().addColumn(column2);

      // Setzt den Typ der Spalte als String
      SimpleValue value2 = new SimpleValue();
      value2.setTable(clazz.getTable());
      value2.addColumn(column2);
      value2.setTypeName("double");

      // Mappt die eigentliche Property auf den Namen "alter"
      Property property2 = new Property();
      property2.setName("alter");
      property2.setNodeName("alter");
      property2.setValue(value2);

      component.addProperty(property2);

      SessionFactory sessionFactory = cfg.buildSessionFactory();

      Session session = sessionFactory.openSession();
      Transaction trans = session.beginTransaction();
      Criteria crit = session.createCriteria(AbstractEntity.class);
      //crit.add(Restrictions.gt("dynamicAttributes.alter", new Double(20)));
      List<AbstractEntity> list = crit.list();
      for (AbstractEntity entity : list) {
         System.out.println(entity);
         String old = entity.getDynamicProperty("name");
         String newValue = old + " neu";
         entity.setDynamicProperty("name", newValue);
         Double alter = entity.getDynamicProperty("alter");
         entity.setDynamicProperty("alter", new Double(
               alter.doubleValue()- 1d));
      }
      trans.commit();
      session.close();
   }

}



Da kommen dann Statements wie dieses bei rum:
select this_.FIRST_ID as FIRST1_0_0_, this_.FIRST_NAME as FIRST2_0_0_, this_.ALTER_P as ALTER3_0_0_ from TABLE_BLA this_ where this_.ALTER_P>?

Aus meiner Sicht funktionierts also bis hierher für einfache Datentypen wie String und Double, sowie für einfache PrimaryKeys. (Wie gesagt, alles immer auf unserer internes Modell abgestellt, bei dem bestimmte Voraussetzungen gegeben sind.)


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Mon Apr 04, 2011 9:10 am 
Senior
Senior

Joined: Tue Oct 28, 2008 10:39 am
Posts: 196
Es ist mir jetzt gelungen, eine Tabelle komplett ohne eigene Klasse zu mappen:

Code:
package test;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Mappings;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;

import hibernate.HibernateUtil;

public class TestMapping{

   @SuppressWarnings("unchecked")
   public static void main(String[] args) {
      AnnotationConfiguration cfg = HibernateUtil.getConfiguration();

      // ID-Spalte anlegen
      Column idColumn = new Column();
      idColumn.setName("ID1");

      // Tabelle anlegen und Spalte mappen
      Table tNew = new Table("TABELLE1");
      tNew.addColumn(idColumn);

      // Value erzeugen, Typ, Spalte und Tabelle festlegen
      SimpleValue idValue = new SimpleValue();
      idValue.setTable(tNew);
      idValue.addColumn(idColumn);
      idValue.setTypeName("long");

      // Property erzeugen, Name und Value zuweisen
      Property propId = new Property();
      propId.setName("id");
      propId.setNodeName("id");
      propId.setValue(idValue);

      // Class-Mapping
      RootClass rc = new RootClass();
      rc.setIdentifierProperty(propId);
      rc.setIdentifier(idValue);
      rc.setEntityName("Entity1");
      rc.setNodeName("Entity1");
      rc.setTable(tNew);

      // Primärschlüssel festlegen
      PrimaryKey pk = new PrimaryKey();
      pk.setName("ID1");

      // Primärschlüssel und Tabelle "bekannt machen"
      pk.setTable(tNew);
      tNew.setPrimaryKey(pk);

      // Mapping hinzufügen
      Mappings createMappings = cfg.createMappings();
      createMappings.addClass(rc);

      // Mapping abschliessen
      SessionFactory sessionFactory = cfg.buildSessionFactory();

      // Daten erzeugen, speichern, lesen und ausgeben
      Session session = sessionFactory.openSession();
      Transaction trans = session.beginTransaction();

      HashMap<String, Object> newVal = new HashMap<String, Object>();
      newVal.put("id", new Long(4712));

      session.save("Entity1", newVal);
      session.flush();

      Criteria crit = session.createCriteria("Entity1");
      List<HashMap> list = crit.list();
      for (HashMap o : list) {
         Set keySet = o.keySet();
         for (Object key : keySet) {
            System.out.println(key + "=>" + o.get(key));
         }
      }

      trans.commit();
      session.close();
   }
}



Die Tabelle1 existiert und besteht erstmal nur aus der Spalte ID1. Wie man weitere Spalten anhängen kann zeigt das obige Beispiel ja bereits...


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Mon Apr 04, 2011 11:22 am 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Super interessant deine Lösung, vielen Dank schonmal.

Bin im Moment dabei, das ganze auf das Projekt zu übertragen. Werde morgen mal eine Rückmeldung geben, wie das ganze geklappt hat, aber dein zweiter Post zeigt ja, dass es geht.

Hat mich bisher schon wirklich weiter gebracht.


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Tue Apr 05, 2011 4:43 am 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Also ich habe die Testklasse mal etwas angepasst an Hibernate 3.6 und an die Anforderung, Felder dynamisch hinzuzufügen (durch ein Array in dem Fall).

Ich hoffe, dass ich die Umstellungen korrekt vorgenommen habe, allerdings erhalte ich eine

Code:
Exception in thread "main" org.hibernate.MappingException: Unknown entity: TEST


Ist an meinem Code irgendetwas auffälliges, was ich evtl. die ganze Zeit nur übersehe?

Code:
package test;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.cfg.Mappings;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;

import java.util.*;

import test.HibernateUtil;

public class TestManager {

    public static void main(String[] args) {
        TestManager mgr = new TestManager();

        mgr.createAndStoreEvent();
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void createAndStoreEvent() {

        Table tab = new Table("TEST");
       
        String[] felder = new String[4];
        felder[0] = "ROWID";
        felder[1] = "NAME";
        felder[2] = "AGE";
        felder[3] = "BLA";
       
        Column col;
        SimpleValue sv;
        Property prop;
       
        Mappings map;
        map = HibernateUtil.getConfig().createMappings();
       
        RootClass rc;
        rc = new RootClass();
        rc.setEntityName("TEST");
        rc.setNodeName("TEST");
        rc.setTable(tab);
       
        map.addClass(rc);
       
        for(String feld : felder) {
            col = new Column(feld);
            tab.addColumn(col);
           
            sv = new SimpleValue(map);
            sv.setTable(tab);
            sv.addColumn(col);
            sv.setTypeName("string");
           
            prop = new Property();
            prop.setName(feld);
            prop.setNodeName(feld);
            prop.setValue(sv);
           
            if(feld.equals("ROWID")) {
                rc.setIdentifierProperty(prop);
                rc.setIdentifier(sv);
            }
        }
       
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
       
        Criteria crit = session.createCriteria("TEST");
        List<HashMap> list = crit.list();
        for(HashMap o : list) {
            Set keySet = o.keySet();
            for(Object key : keySet) {
                System.out.println(key + "=>" + o.get(key));
            }
        }
       
        session.getTransaction().commit();
        session.close();
    }

}


Nachtrag: In HibernateUtil.getSessionFactory() ist buildSessionFactory() enthalten


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Tue Apr 05, 2011 5:28 am 
Senior
Senior

Joined: Tue Oct 28, 2008 10:39 am
Posts: 196
Hallo,
ich habe den PrimaryKey hinzugefügt, außerdem fehlte das hinzufügen der Property zum Mapping. Folgender Code funktioniert jetzt bei mir:

Code:
package test;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Mappings;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;

import hibernate.HibernateUtil;

public class TestAbstract3 {

   public static void main(String[] args) {
      TestAbstract3 mgr = new TestAbstract3();
      mgr.createAndStoreEvent();
   }

   @SuppressWarnings( { "unchecked", "rawtypes" })
   private void createAndStoreEvent() {
      AnnotationConfiguration cfg = HibernateUtil.getConfiguration();
      Table tab = new Table("TABELLE1");

      String[] felder = new String[4];
      felder[0] = "ID1";
      felder[1] = "NAME";
      felder[2] = "AGE";
      felder[3] = "BLA";

      Mappings map;
      map = cfg.createMappings();

      RootClass rc;
      rc = new RootClass();
      rc.setEntityName("TEST");
      rc.setNodeName("TEST");
      rc.setTable(tab);

      for (String feld : felder) {
         Column col = new Column(feld);
         tab.addColumn(col);

         SimpleValue sv = new SimpleValue();
         sv.setTable(tab);
         sv.addColumn(col);
         sv.setTypeName("string");

         Property prop = new Property();
         prop.setName(feld);
         prop.setNodeName(feld);
         prop.setValue(sv);

         if (feld.equals("ID1")) {
            rc.setIdentifierProperty(prop);
            rc.setIdentifier(sv);
            // Primärschlüssel festlegen
            PrimaryKey pk = new PrimaryKey();
            pk.setName(feld);
            // Primärschlüssel und Tabelle "bekannt machen"
            pk.setTable(tab);
            tab.setPrimaryKey(pk);
         } else {

            rc.addProperty(prop);
         }
      }

      map.addClass(rc);

      // Mapping abschliessen
      SessionFactory sessionFactory = cfg.buildSessionFactory();
      Session session = sessionFactory.openSession();
      session.beginTransaction();

      Criteria crit = session.createCriteria("TEST");
      List<HashMap> list = crit.list();
      for (HashMap o : list) {
         Set keySet = o.keySet();
         for (Object key : keySet) {
            System.out.println(key + "=>" + o.get(key));
         }
      }

      session.getTransaction().commit();
      session.close();
   }

}


Ergänzung: Ich verwende Hibernate 3.5.6. Vielleicht macht das auch nen Unterschied.


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Tue Apr 05, 2011 5:57 am 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Daran hats gelegen, vielen Dank!

Da steht man ewig wie der Ochse vorm Berg und sieht das Wesentliche doch nicht.


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Wed Apr 06, 2011 6:45 am 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Eine Frage als Nachtrag wäre noch, wie man mit diesem Mapping Daten löschen kann. Ich arbeite nun mit HashMaps als Rückgabewert meiner get-Methoden und meine delete-Methode sieht folgendermaßen aus:

Code:
public void delete(HashMap o) {
        try {
            this.sess().beginTransaction();
            this.sess().delete(o);
            this.sess().getTransaction().commit();
        }
        catch (HibernateException ex) {
            handleException(ex);
        }
    }


Das klappt allerdings nicht und ich bekomme die Meldung

Code:
Unknown entity: java.util.HashMap


Ich nehme an, dass ich hier irgendwie an die erzeugte RootClass rankommen muss, ist das richtig?

Gruß

Nachtrag:

Es hat doch geklappt, habe die Methode folgendermaßen umgebaut:

Code:
public void delete(String id) {
        try {
            this.sess().beginTransaction();
            Query q = this.sess().createQuery("delete from " + this.table + " where rowidtochar(rowid) = '" + id + "'");
            q.executeUpdate();
            this.sess().getTransaction().commit();
        }
        catch (HibernateException ex) {
            handleException(ex);
        }
    }


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Wed Apr 06, 2011 7:04 am 
Senior
Senior

Joined: Tue Oct 28, 2008 10:39 am
Posts: 196
Du musst kein SQL basteln. session.delete(entityName, object) funktioniert bei mir.

Beispiel:
Code:
Criteria crit = session.createCriteria("TEST");
      List<HashMap> list = crit.list();
      for (HashMap o : list) {
         session.delete("TEST", o);
      }


Speichern geht ähnlich:

Code:
      HashMap<String, Object> newObject = new HashMap<String, Object>();
      newObject.put("BLA", "testBla");
      newObject.put("NAME", "testName");
      newObject.put("ID1", "12345");

      session.save("TEST", newObject);


Die ID muss ich noch manuell setzen, weil ich noch keine PK-Generierungs-Strategie umgesetzt habe. (Kann man auch nicht immer automatisiert aus der DB ermitteln. Für Trigger-gesteuerte, aus Sequenzen bezogene IDs müsste man im Oracle-Umfeld z.B. den Inhalt des Triggers auslesen und analysieren.)


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Wed Apr 06, 2011 10:36 am 
Newbie

Joined: Fri Apr 01, 2011 5:36 am
Posts: 8
Das klappt alles perfekt, vielen Dank schonmal.

2 letzte Fragen (ganz sicher ;-) )

1. Jetzt habe ich ein Henne-Ei-Problem. Ich muss das dynamische Mapping durchführen, bevor ich Hibernates SessionFactory erzeugt habe, richtig? Allerdings muss ich davor noch eine Metadaten-Abfrage machen, um die Spalten einer bekannten Tabelle zu erfahren. Hast du bei dem speziellen Problem vll noch eine Idee, wie ich an die JDBC-Connection komme, bevor ich cfg.buildSessionFactory() ausführe?

2. Kommst du zufällig aus dem Raum Frankfurt, damit ich dir für deine umfassende Hilfe mal ein Bierchen ausgeben kann?

Gruß


Top
 Profile  
 
 Post subject: Re: Dynamische Konfiguration
PostPosted: Thu Apr 07, 2011 4:02 am 
Senior
Senior

Joined: Tue Oct 28, 2008 10:39 am
Posts: 196
Moin,

1. Du könntest eine reine JDBC-Verbindung aufbauen, dann musst du aber die Verbindungsparameter doppelt pflegen. Du könntest aber auch beim ersten Abruf einer Verbindung zu den dynamisch gemappted Daten eine SessionFactory komplett erzeugen, bei der keine Mappings vorgenommen werden. Über diese setzt du reine SQL-Queries auf die Metadaten ab und erzeugt eine zweite SessionFactory und nimmst für diese "innere" Factory die dynamischen Mappings vor. Wenn du damit durch bist, schließt du einfach die erste SessionFactory wieder. Über die Zugriffe von außen auf deine HibernateUtil musst du dann nur sicherstellen, dass die erste Initialisierung sauber läuft und nur Sessions der "inneren" SessionFactory nach draussen gegeben werden.

2. Bad Ems, aber ich trinke nicht während der Arbeitszeit... ;-)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.