Hibernate Entity aus Query ermitteln

Hallo,

ist es möglich aus einer Hibernate Query bzw. dem HQL-String die Entity zu ermitteln. Ich könnte den HQL String zu Fuß parsen, aber vielleicht geht es besser. Oder es geht generell besser.

Szenario:

Aus der Datenbank werden via Hibernate ConfigDaten gelesen. Typischer select “from Configuration where configname = ?”.
Ich möchte um Datenbankzugriffe zu minimieren diese Daten cachen. Und zwar nicht beim Aufrufer, sondern in der Datenbankzugriffsklasse, welche ein Wrapper um die Hibernatefunktionalität ist.

Idee: wenn der Aufrufer obigen String absetzen, möchte ich erkennen dass die Tabelle Configuration und geg. das Feld configname gemeint ist und dann alles aus dieser Tabelle einmalig abholen.
Und alle selects auf die Tabelle “umleiten”.

Gruß Thomas

Willst du wirklich von Hand “cachen”, eine HQL-Query Parsen und dann versuchen, eine gecachte Version an den Client liefern??

Kannst du keine vorhandenen Lösungen verwenden?

http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-cache

So wie ich es verstehe - man korrgiere mich - cachen diese Lösungen nicht alles, sondern nur schon gelaufene Abfragen.

Z.B. wird “from Configuration where configname = name1” gecached und Configuration 1 geliefert.
Erst wenn “from Configuration where configname = name2” aufgerufen wird, wird Configuration 2 aus der DB gelesen.
Configuration 2 ist damit jünger als Configuration 1.

Das hast du falsch verstanden :wink:

Gecacht und verfolgt werden vor allem Objektzustände, in der Session (1st Level Cache) und in der SessionFactory (2nd Level Cache, auch Shared Cache genannt, istbei Hibernate per default deaktiviert, bei EclipseLink aktiviert).

Würde mir an diner Stelle alles abschminken was damit zu tun hat selber Caches zu verwalten und mich auf das ORM, hier Hibernate, konzentrieren.

Hast du denn schon gemessen dass diese Abfragen zu lange dauern oder optimierst du in Blaue hinein und misst danach nichtmal das Ergebnis?
Letzteres ist kein Optimieren, sondern nur „Code verschlechtern“.

auch wenn ich nicht ganz so kritisch hinsichtlich solcher Versuche bin scheint eine derart allgemeine Stelle falsch,
damit schaltest du ganze Hibernate-Funktionen ab, hast kein respektables Persistenz-Framework mehr vorliegen sondern ein bizarres Sonderkonstrukt,

so ein Cache ist eher was für innerhalb der Anwendung, und auch dann nur für explizite Aufrufe a la loadConfiguration(name), dort statt HQL evtl. Cache einbeziehen,

wenn dagegen derselbe Anwender eine neue Configuration anlegt oder editiert, und Hibernate deswegen intern auch irgendwelche Ladevorgänge braucht, sind diese nicht gefährdet,
wobei das nicht unbedingt HQL sein wird, vielleicht wäre das duch ‘Abfangen von allen HQL-Queries’, wie immer das geht, nicht gefährdet

Es geht um Configdaten, die aus der DB gelesen werden und mehrmals (im Extremfall mehrere Mio am Tag) gebraucht werden.
Momentan optimiere ich noch nicht. Ich klopf mal Möglichkeiten ab.

wie steht es denn zu normaleren Vorgehen innerhalb der Anwendung, schon eine simple Map, einmal geladen, notfalls gar statisch,

bei solchen Daten ist auch individueller Code pro Datenklasse nicht zu schade, selbst wenn 3x ähnliches vorhanden,
oder eine allgemeine Methode kann immer noch programmiert und dann individuell aufgerufen werden

class IchHabeDieAhnungImProgrammMichFragenAlle {
  Config getConfig(name) {
    if (configMap == null) {
      ... // 10 Zeilen Code oder Aufruf Methode buildMap(Klassenname, Feldname);
    }
    return configMap.get(name);
  }
}

versprichst du dir konkrete Vorteile von allgemeinerer Lösung?


wobei ich ‚Datenbankzugriffsklasse, welche ein Wrapper um die Hibernatefunktionalität‘ bisher glaube ich falsch gelesen hatte,
das klingt ja gar nicht so hoch, die Klasse IchHabeDieAhnungImProgrammMichFragenAlle könnte man strenggenommen auch so bezeichnen,
innerhalb der Anwendung Mittelfeld-Position? :wink:

in dem Fall werfe ich noch den allgemeinen Gedanken ein, HQL nicht völlig frei als String aufzubauen sondern ähnlich Criteria zusammenzustellen,
als Rückfall immer noch mit Hinzufügen von String für bestimmte Konstrukte, die in Criteria auch gar nicht möglich sind,
aber für die normalen Querys

new QueryBuilder().from("Config").where("name",wert);

usw.,
so hättest du jedenfalls leicht den Klassennamen und weiteres, falls nötig

warum dann nicht gleich Criteria ist berechtigte Frage, welches aber auch nicht den Klassennamen gleich rausrückt, denke ich,

Hallo Slater,

ich möchte diese IchHabeDieAhnungImProgrammMichFragenAlle Klasse gerne allgemein gültiger machen:

Die Architektur sieht momentan - vereinfacht und historisch gewachsen - folgendermaßen aus:

Ein ServiceKlasse bekommt Nachrichten. Um diese Nachrichten zur verarbeiten sind Konfigurationen von nöten, die zentral in der DB gepflegt werden. Welche Konfiguration genommen werden muss, ist abhängig von dem Nachrichteninhalt. Daher müssten bei jeder Nachricht die DB abgefragt werden. Das kann auch mal mehr werden.

Um mit der Datenbank zu kommunizieren bekommen die ServiceKlassen eine DataSource injiziert. Das ist eine eigene abstrakte Klasse mit - mittlerweile - HQL-Zugrifffunktionen. Von dieser abstrakte Klasse, gibt es nur (noch) eine Implementierung, DataSourceHibernate. Die - oh wunder - mit Hibernate arbeitet. Historisch gesehen war es ein viel krampfigerer Zugriff, weswegen diese zusätzliche Abstraktion eingeführt wurde um dann besser den Schwenk machen zu können.

Ein einfaches Caching finde (z.B. via Map) in einigen ServiceKlassen statt. Auf verschiedenster Art und Weise. Z.B. beim Start alles auslesen. Nach Bedarf. Usw.

Die Idee war nun, dieses Caching zu verlagern. In eine DataSource-Klasse ala DataSourceHibernateConfigCached. So dass die ServiceKlasse a) das Caching, das auch komplex sein kann (timer, täglich 0:00 Uhr, bei Neustart) gar nicht bemerkt und b) nicht selbst implementieren muss.

ich weiß nicht ganz wo das hinsichtlich meinem Punkt hinführt, an welcher Stelle es Gemeinsamkeiten oder Widersprüche noch gibt,
aber noch kurz von einer anderen Sache angerissen:

Code a la session.createQuery("from Config where name = ..") mit Parameter-Übergabe usw. sollte es maximal 1x im Programm geben,
eben eine Hilfsmethode getConfig(name), auch ganz ohne Caching für wichtige Datenklassen sinnvoll,
Objektorientierung statt beliebiger schwer nachvollziehbarer verstreuter String-Queries

es muss nicht nur ein einziges Objekt sein, das man danach fragen kann, aber besser nur eine Klasse, evtl. mehrere Objekte davon im Umlauf,
andere spezifischere ServiceKlassen könnten weiterleiten, das ist auch noch recht human

class ServiceXy {
  Config getConfig(name) {
     return commonService.getConfig(name); // eigenes CommonService-Objekt, nicht unbedingt programmglobal, eh an Session gebunden
  }
}

falls man dem Aufrufer nicht zumuten will, auch den CommonService zu kennen/ zu holen

falls du eine solche Struktur bereits hast, oder gut findest und in Betracht ziehst, dann wäre jedenfalls diese einzige
Codestelle im ganzen Programm, die die Query session.createQuery("from Config where name = ..") hat,
geeignet für eine individuelle Map zum Cache der Daten,

da muss nicht unbedingt eine generische Lösung beim Abfangen beliebiger HQL-Querys kommen,
die eine getConfig()-Methode im Programm ist auch für normale Vorgänge offen

so, dass nochmal als kleine Wiederholung meines Punktes,
geht natürlich auch anders :wink:

Diese getConfig() Methoden gibt es für einiges häufig benötigtes schon. Z.B. getTransferConfig() für I/O-Verbindungsdaten.

Nur leider nicht dafür.

Die obigen DataSource-Klassen sind Teil des Frameworks, welches von vielen fachliche Projekten benutzt wird. Die konkrete Config, die Anlass ist darüber nachzudenken, ist eine Mappingtabelle die nur für exakt eine Anwendung benötigt wird.

Wenn damit die Anwendung konfiguriert wird, dann ist es vielleicht sinnvoll, die Konfiguration gar nicht in die Datenbank zu packen, sondern beispielsweise mit Commons Configuration aus einer Datei zu laden. Dort hast du sehr gute Performance und du kannst die Konfigurationsdatei zur Laufzeit der Anwendung ändern und sie wird automatisch nach einer konfigurierbaren Zeit neu geladen.
Achso, mit Comons Configuration kannst du die Konfiguration wenn es unbedingt sein muss auch aus der Datenbank laden lassen (auch wenn ich das nicht unbedingt für den richtigen Ort einer Anwendungskonfiguration halte, sofern es sich dabei nicht um eine mehrmandantenfähige Anwendung und dementsprechend mandantenabhängige Konfiguration handelt).

Einen zusätzlichen Cache über hibernate zu legen, halte ich auch nicht für zielführend.

Die folgende Aussage habe ich auch nicht verstanden:

Wozu brauchst du einen Wrapper um hibernate herum? Wenn du einen Wrapper möchtest, um von hibernate weg zu können, dann würde ich an deiner Stelle einfach JPA benutzen. So kannst du dann relativ problemlos die Implementierung austauschen.

Hätte ich gerne gemacht.
Ist aber Anforderung. ;-(

Ok. Ich nehm das mal so mit.

[QUOTE=cmrudolph;96695]
Wozu brauchst du einen Wrapper um hibernate herum? Wenn du einen Wrapper möchtest, um von hibernate weg zu können, dann würde ich an deiner Stelle einfach JPA benutzen. So kannst du dann relativ problemlos die Implementierung austauschen.[/QUOTE]

Ich darf mal Wikipedia zitieren:
„Die Java Persistence API wurde als Projekt der JSR 220 Expert Group entwickelt und im Mai 2006 erstmals veröffentlicht.“

Die Kernbestandteile der Software entstanden noch mit Java 1.4.
Der Wrapper wurde eingeführt, um leicht zu Hibernate hinzukommen. Weil die damalige Lösung/Anforderung Schrumpel war, der nur durch politische Spielchen in die Anwendung kam, aber ich und ein paar andere Entwickler, leicht drumrumwollten. Was macht man bei Scheiss-Anforderungen? Man hält eine Fassade aufrecht und geht heimlich andere sinnvolle Wege … :wink: Die mögen dann nicht so sinnvoll erscheinen, wie richtig sinnvolle Wege, sind aber immerhin kein richtiger Schrumpel.

[QUOTE=ThomasT]Hätte ich gerne gemacht.
Ist aber Anforderung. ;-( [/QUOTE]
Commons Configuration kann Konfigurationen auch in einer Datenbank abspeichern. Das wäre dann zumindest vom Rest der Persistenz unabhängig aber bei Datenbankbackups mit dabei.

[QUOTE=ThomasT;96788]“Die Java Persistence API wurde als Projekt der JSR 220 Expert Group entwickelt und im Mai 2006 erstmals veröffentlicht.”

Die Kernbestandteile der Software entstanden noch mit Java 1.4.
Der Wrapper wurde eingeführt, um leicht zu Hibernate hinzukommen.[/QUOTE]
JPA 2.1 ist am 23. Mai 2013 released worden. Mittlerweile ist JPA so flexibel, dass man eigentlich alles damit hinbekommt. Einen Direktzugriff auf Hibernate braucht man außer beim Start bei der Konfiguration nicht.

Es geht um Configdaten, die aus der DB gelesen werden und mehrmals (im Extremfall mehrere Mio am Tag) gebraucht werden.

Stellt sich die Frage, wie oft und schnell sich diese Configdaten überhaupt ändern? Millionen mal die „gleichen“ Daten abzufragen ist tatsächlich Mist.

Passiert was schlimmes, wenn jemand „veraltete“ Configdaten vorhält?

Weil die damalige Lösung/Anforderung Schrumpel war, der nur durch politische Spielchen in die Anwendung kam, aber ich und ein paar andere Entwickler, leicht drumrumwollten.

Also ich würde die Config-daten einfach in einer Map speichern (genau wie SlaterB das vorschlägt). Für solche Daten extra mit Caches, Hibernate-Wrapper und den Atomraketen zu schrumpeln ist doch wahrscheinlich etwas übertrieben.

Wie siehts mit JPAaus?

Tja, da gibt es eine Annotation Cacheable (Java EE 6 )

Hängt aber von Provider ab, wie und ob (und wie) das implementiert ist. Schau mal nach, ob Hibernate das umsetzt…