# Hibernate -> SQL verwenden und keine Entity



## PHANTOMIAS (13. Jul 2010)

Hallo an alle!

Ich habe ein Problem: Ich nutze Hibernate und habe normalerweise ein Objekt das annotiert ist, bspw. ein Address-Objekt.
Bei diesem sage ich dann über die Schnittstelle

```
public List<Address> getAllAddresses();
```
Und bekomme dann eine Liste an Adress-Objekten zurück.

In der DAO-Implementierung tue ich nämlich:

```
public List<Address> getAllAddresses() {
	SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
	Session sess = sessionFactory.getCurrentSession();
	Transaction tx = sess.beginTransaction();
	@SuppressWarnings("unchecked")
	List<Address> addresses= sess.createQuery("from Address").list();
	tx.commit();
	return addresses;
}
```
Das war bisher mein normaler Weg.

Nun aber zu meinem Problem: Ich muss eine selbstgeschriebene SQL-Abfrage an die Datenbank weitergeben und es liefert mir etwas zurück, das sich aus mehreren Tabellen berechnet. Über die Objekte das zu managen, wäre viel zu umständlich, da müsste ich mir fast die ganze Datenbank erst in Objekte einlesen um darüber dann das zusammenzubauen was mit SQL in 7-8 Zeilen geht.

Zurückbekommen tue ich so etwas:

```
TAG            DAUER
23.01.2010        31
24.01.2010        18
25.01.2010        20
26.01.2010        27
```
Das mapped aber auf kein Objekt, sondern die Klasse müsste ich dann anlegen:

```
class TagDauer {
 ... Eigenschaften TAG und DAUER ... setter/getter
}
```
Ich rufe den Service über eine Flex-Anwendung auf, dann würde ich auf der Frontend-Seite auch diese Klasse bauen, dass die dann miteinander "vernetzt" sind.

Aber geht das so überhaupt? Wie setzt man so etwas um?

Danke vielmals im voraus für eure Hilfe, Gruss PHANTOMIAS


----------



## SlaterB (13. Jul 2010)

siehe meine Auflistung hier
http://www.java-forum.org/data-tier...blem-get-set-lesendem-zugriff.html#post656992


----------



## Marcinek (13. Jul 2010)

this.openSession().createSQLQuery(arg0, arg1, arg2)

Damit kann man einen nativen SQL mit Order und dann noch die Klasse angeben, in das die Werte aus dem Query reingemappt werden sollen.


----------



## PHANTOMIAS (13. Jul 2010)

Ich habe es so gemacht:

```
public List<Chart> getAllCharts() {
	SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
	Session sess = sessionFactory.getCurrentSession();
	Transaction tx = sess.beginTransaction();
	@SuppressWarnings("unchecked")
	List<Chart> charts = (List<Chart>) sess.createSQLQuery("select ...").addEntity(Chart.class).list();
	tx.commit();
	return charts;
}
```
Nun erhalte ich jedoch den Fehler:


> org.hibernate.MappingException : Unknown entity: com.myDomain.myProject.data.domain.Chart



Lasse ich das "addEntity(Chart.class)", so kommt es über den Tomcat zum Frontend zwar von den Werten her an, es ist aber keine Klasse "Chart".

An was kann das liegen, dass ich diese Meldung erhalte? Wie bereits erwähnt, annotiert ist diese Klasse nicht.

Gruss PHANTOMIAS

Update: Das Problem ist wohl, dass Chart.class nicht mit @Entity annotiert ist?!


----------



## SlaterB (13. Jul 2010)

> An was kann das liegen, dass ich diese Meldung erhalte? Wie bereits erwähnt, annotiert ist diese Klasse nicht.

du stellst die Frage, du lieferst die Antwort, genau daran liegt es, Entity ist auch ein Fachbegriff in Hibernate

wie in meinem Link schon aufgeführt geht stattdessen
"select new com.myDomain.myProject.data.domain.Chart(x,y,z) from .. ";
sofern ein passender Konstruktor vorhanden ist, 
mit HQL geht das in jedem Fall, da dürfte die Query auch sauberer zu formulieren sein, SQL geht bestimmt genauso


----------



## PHANTOMIAS (13. Jul 2010)

Ich habe es umgesetzt, jetzt erhalte ich jedoch:


> org.hibernate.exception.SQLGrammarException : could not execute query




```
List<Chart> charts = sess.createSQLQuery(
	"select new com.myDomain.myProject.data.domain.Chart ('2010-07-06' AS 'd', sum(DUR) AS 'r', 0 AS 'o') " +
	" from " +
	" (select distinct a.id as ID, a.dur as DUR from b_c" +
	" inner join c on b_c.c_id = c.id " +
	" inner join a on c.id = a.c_id " +
	" inner join a_aud on a.id = a_aud.id " +
	" inner join d on a_aud.rev = d.rev " +
	" where b_c.b_id = 30 and " +
	" a_aud.stat != 1 and " +
	" DATE(FROM_UNIXTIME(revtime / 1000)) = DATE('2010-07-06')) " +
	" AS SubSelectTable").list();
```
Was stimmt an dieser Abfrage nicht? Führe ich sie auf der Konsole aus, geht sie, wbei ich tendiere zu sagen, dass es an den AS als Parameter liegen könnte? Nur ohne die AS funktioniert das Subselect nicht.

Gruss PHANTOMIAS


----------



## SlaterB (13. Jul 2010)

'could not execute query' sollte immer noch eine dahinerliegende Exception haben, die genauer erklärt was los ist,
fängst du die Exception ab? dann schau nach getCause()

jetzt wo ich das SQL sehe, glaube ich wieder weniger, dass 
'select new com.myDomain.myProject.data.domain.Chart' funktioniert,
aber fast zu einfach um es hinzuschreiben: einfach testen..

wieso eigentlich AS 'd' usw. im select, das macht doch keinen Unterschied?
meinst du die oder AS SubSelectTable mit 'Nur ohne die AS funktioniert das Subselect nicht.'?

das wahrscheinlichste und bei mir der häufigste Fehler ist ein falscher Konstruktor, nicht passende Parametertypen,
versuche erstmal nur die Attribute abzufragen ohne Rahmenklasse, schau dir an was da rauskommt (String statt Date usw.),
danach eins nach dem anderen im Konstruktor, gerne auch mal nur mit Object als Parameter


----------



## PHANTOMIAS (14. Jul 2010)

Debugge ich auf Serverseite, erhalte ich:


> SCHWERWIEGEND: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.myDomain.myProject.data.domain.Chart ('2010-07-06' AS 'd', sum(DUR)' at line 1



Lasse ich die AS im select weg, geht es auch, aber gleiche Fehlermeldung.

Und auch alle anderen Modifikationen funktionieren nicht!

Auch so eine einfache Abfrage klappt nicht, es wird der gleiche Fehler wie oben geworfen. Also klappt da grundlegend das Konstrukt nicht?! Weil ein einfachere Abfrage geht ja fast nicht mehr 

```
public List<Chart> getAllCharts() {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session sess = sessionFactory.getCurrentSession();
    Transaction tx = sess.beginTransaction();
    @SuppressWarnings("unchecked")
    List<Chart> charts = sess.createSQLQuery("select new com.myDomain.myProject.data.domain.Chart(myTable.id) from myTable where testColumn = 10").list();
    tx.commit();
    return charts;
}
```

Gruss PHANTOMIAS


----------



## Marcinek (14. Jul 2010)

Mir erscheint das sehr logisch.

Das, was da in der SQL steht ist kein HQL! 

Daher wird das Statement so auf der DB abgelegt und daher kann ich mir nicht vorerstellen, dass er mit einem statement: select "NEW BBLA BLA (hier kommt die id)" überhaupt was anfangen kann.

Wenn es mysql ist dann kann ich dir sagen, dass das so nicht klappt.


----------



## PHANTOMIAS (14. Jul 2010)

Okay, also meine Abfrage in HQL umwandeln, dann klappt es?

Kann ich weiterhin meine Methode so verwenden und das schreiben:

```
List<Chart> charts = sess.createSQLQuery(ALLES_IN_HQL).list();
```

Danke + Gruß PHANTOMIAS


----------



## Marcinek (14. Jul 2010)

Eher nicht.

Hier muss ein gültiges SQL rein.

Bei deinen letzten Versuchen vermisse ich ein wenig das VO, in das er mappen soll.

Wie wäre es, wenn du aus deiner Anfrage eine View baust und diese normal über Hibernate mappst?

Gruß,

Marcinek


----------



## PHANTOMIAS (14. Jul 2010)

Mein VO ist die Chart-Klasse.
Das ist ja nur das Backend! Die Frontend-Anbindung ist auf Flex-Seite und nicht auf der Java-Seite, deswegen weiß ich nicht was du meinst, dass du ein wenig das VO vermisst in dem es mappen soll.

Was meinst du mit View bauen und dann normal über Hibernate mappen? Ich habe ja keine Entity-Entsprechung in einer Datenbanktabelle.
Und wie man das sinnvoll macht, das habe ich hier ja angefragt 

Danke und Gruß PHANTOMIAS


----------



## Marcinek (14. Jul 2010)

Also was ich mit view meine:

In der Datenbank: Create View blabla as 
 "select new com.myDomain.myProject.data.domain.Chart ('2010-07-06' AS 'd', sum(DUR) AS 'r', 0 AS 'o') " +
    " from " +
    " (select distinct a.id as ID, a.dur as DUR from b_c" +
    " inner join c on b_c.c_id = c.id " +
    " inner join a on c.id = a.c_id " +
    " inner join a_aud on a.id = a_aud.id " +
    " inner join d on a_aud.rev = d.rev " +
    " where b_c.b_id = 30 and " +
    " a_aud.stat != 1 and " +
    " DATE(FROM_UNIXTIME(revtime / 1000)) = DATE('2010-07-06')) " +
    " AS SubSelectTable"

Anschließend erzeugst du dein VO und annotierst es korrekt.

Dann musst du das VO Hibernate bekannt machen, was über die configuration geht. (add Annotated class)

Und dann machst du ein ganz normalen select mit hibernate.


----------



## PHANTOMIAS (14. Jul 2010)

Sorry, dafür bin ich noch zu ungeübt.

Beim View muss das Datum zur Laufzeit eingesetzt werden, ebenso die Zahl bei 

```
where b_c.b_id = 30
```

Die beiden Anweisungen:

```
'2010-07-06' AS 'd'
0 AS 'o'
```
sind nur da, dass ich das Objekt vollständig habe.

Also wie erzeuge ich dann die View? Und wie annotiere ich die Klasse? Doch nicht mit Entity, dann wird ja eine Datenbanktabelle angelegt, das will ich ja nicht.

Ich habe in hibernate.cfg.xml:
[XML]<mapping class="com.myDomain.myProject.data.domain.Chart"/>[/XML]

---

In der Zwischenzeit habe ich mal alternativ das notiert:

```
Query query = sess.createSQLQuery(
				"select sum(DUR)" +
    " from " +
    " (select distinct a.id as ID, a.dur as DUR from b_c" +
    " inner join c on b_c.c_id = c.id " +
    " inner join a on c.id = a.c_id " +
    " inner join a_aud on a.id = a_aud.id " +
    " inner join d on a_aud.rev = d.rev " +
    " where b_c.b_id = 30 and " +
    " a_aud.stat != 1 and " +
    " DATE(FROM_UNIXTIME(revtime / 1000)) = DATE('2010-07-06')) " +
    " AS SubSelectTable");
		List list = query.list();
		List<Chart> charts = new ArrayList<Chart>();
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
		tx.commit();

		return list;
```
In der Zeile

```
System.out.println(list.get(i));
```
kriege ich das Ergebnis zurück.

Aber wie kriege ich das nun in ein Chart-Objekt rein?
Denn eigentlich müsste ich früher ansetzen, denn ich brauch keine Liste, sondern einfach nur das Resultat da es nur eine Ergebnis-Reihe ist und keine Liste.

Danke vielmals und Gruss PHANTOMIAS


----------



## Marcinek (14. Jul 2010)

Naja es kommt immer eine Liste zurück den eine query hat als ergebnis immer eine Menge 

Was kommt da bei dem println raus?

Kannst du TS3 kommen, denn ich denke, dass das Problem eher trivial ist, aber hier via tippen man die Intention nicht korrekt rüber bringen kann.

Gruß,

Marcinek


----------



## PHANTOMIAS (14. Jul 2010)

Ich habe was Gutes gefunden:

```
List resultWithAliasedBean = s.createSQLQuery(
  "SELECT st.name as studentName, co.description as courseDescription " +
  "FROM Enrolment e " +
  "INNER JOIN Student st on e.studentId=st.studentId " +
  "INNER JOIN Course co on e.courseCode=co.courseCode")
  .addScalar("studentName")
  .addScalar("courseDescription")
  .setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
  .list();

StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);
```
Das scheint zu klappen, bis dass in meiner Abfrage die Zahl 25 herauskommt und ich die gerne als double hätte. Es liefert es mir aber als Bigdecimal zurück.


> SCHWERWIEGEND: IllegalArgumentException in class: com.myDomain.myProject.data.domain.Chart, setter method of property: r
> 14.07.2010 11:50:27 org.slf4j.impl.JCLLoggerAdapter error
> SCHWERWIEGEND: expected type: double, actual value: java.math.BigDecimal


Wenn ich das noch umgehen kann, dann wäre ich für den ersten Schritt völlig zufrieden. Aber nicht dass ich als Parameter in der setR-Methode ein BigDecimal als Parameter will anstelle von double, andernfalls muss ich wieder mit der Flex-Schnittstelle kämpfen, also es muss ein double "reingehen".


----------



## SlaterB (14. Jul 2010)

.addScalar("x", Hibernate.DOUBLE)


----------



## PHANTOMIAS (14. Jul 2010)

Das hat geholfen, ist jedoch leider deprecated mit "Hibernate.DOUBLE".
Ich habe zwar auch "Lösungen" gelesen, nämlich hier:
https://forum.hibernate.org/viewtopic.php?f=1&t=1004907&view=previous
aber ich kann sie nicht bei mir anwenden, da ich die Meldung bei 

```
private static final StringType STRING = new StringType();
```
erhalte, dass StringType nicht existiert... Klar, woher soll es das auch geben?

Muss ich dazu etwas importieren?

Danke + Gruss PHANTOMIAS


----------



## SlaterB (14. Jul 2010)

> Klar, woher soll es das auch geben?

von Hibernate, wenn nicht da, dann evtl. zu alte Version

> Muss ich dazu etwas importieren?

na sicher,
StringType (Hibernate API Documentation)


----------



## PHANTOMIAS (14. Jul 2010)

Okay, vielen Dank, somit hat es funktioniert!


----------

