# Klasse welche Mysql Daten zurück gibt



## herbuin (13. Okt 2011)

Hallo,

ich habe ein Problem mit der Java/Mysql Programmierung, höchstwahrscheinlich ein Denkfehler, vielleicht kann mir ja jemand weiterhelfen! Ich habe schon seit Stunden nach einer Antwort gesucht, allerdings, falls es eine gibt(und dessen bin ich mir sicher) habe ich wohl nicht die richtigen Keywords benutzt...

Ich hatte die Idee eine Klasse zu schreiben die sich ausschließlich darum kümmert Mysql Abfragen auszuführen und diese zurück zu geben(da ich in meinem Programm sehr oft Mysql Abfragen haben werde und den Code auf ein minimum reduzieren möchte), die dann z.B. so angesprochen werden kann:


```
mysqlabfrage(String Mysqlstatement)
```

Zurück geben sollte sie dann die Daten aus der Mysql Abfrage. Allerdings bin ich dabei auf ein Problem gestoßen - wie soll ich die Daten zurück geben, die meistens aus mehr als einer Zeile bestehen? 

Ich dachte daran die Daten in einem Multidimensionalen Array zu speichern und dann dieses zurück zu geben als return Wert - ist das ein geeigneter Weg oder gibt es einen besseren? 

Oder sollte diese Klasse 2 Methoden haben, eine welche die Abfrage ausführt, und eine weitere die nur dafür zuständig ist jeweils eine Zeile der Abfrage zurück zu geben (als  mit .next(), und als Return Format String?)

Oder sollte ich den gesamten Weg lieber sein lassen und die Mysqlabfrage in derjenigen Klasse direkt ausführen, die sie braucht? 

Danke schon mal!


----------



## turtle (13. Okt 2011)

Ich halte das Absenden von SQL-Befehle mit JDBC an eine DB für aufwändig. Dein Ansatz legt nahe, dass hier ein String angegeben werden soll, der dann durch die DB bearbeitet wird.

Neben dem Problem des Rückgabewertes besteht meiner Meinung auch die Schwierigkeit, das viele SQl_Befehl nur mit Parametern funktionieren. Zum Beispiel

```
select vorname, alter, geburtsjahr from person where name=<Parameter>
```
In diesem Beispiel muss also der Name als Parameter angeben werden können. Es sind sicherlich auch Beispiele denkbar bei denen mehrere Parameter unterschiedlichen Typs angegeben werden müssen. Weiterhin musst Du darauf aufpassen dass Du dir keine SQL-Injection-Problem einhandelst.

Daher rate ich immer von der direkten Nutzung von JDBC ab. Du solltest einmal über den Einsatz eines ORM-Tools (Hibermate, JPA, myBATIS) nachdenken. Da ich ja ein myBATIS-Fan bin, würde ich hierzu raten.

In myBATIS kannst Du die Rückgabe-Werte (mehrere Zeilen) sehr einfach per Map abrufen.


----------



## nillehammer (13. Okt 2011)

herbuin hat gesagt.:
			
		

> Oder sollte diese Klasse 2 Methoden haben, eine welche die Abfrage ausführt, und eine weitere die nur dafür zuständig ist jeweils eine Zeile der Abfrage zurück zu geben (als mit .next(), und als Return Format String?)


Du bekommst bei jdbc als Ergebnis einer Abfrage ein ResultSet, dass alle Zeilen der Antwort enthält. Dieses stellt Dir Methoden zum durchiterieren (.next()) zur Verfügung. Insofern brauchst Du das nicht selbst zu implementieren auch ein multidimensionales Array brauchst Du nicht.


			
				herbuin hat gesagt.:
			
		

> Oder sollte ich den gesamten Weg lieber sein lassen und die Mysqlabfrage in derjenigen Klasse direkt ausführen, die sie braucht?


Nein. Der von Dir eingeschlagene Weg, Datenabfragen in einer eigenen Klasse zu kapseln ist schon richtig. Solche Klassen nennt man übrigens DAOs (Data Access Objects).


----------



## herbuin (13. Okt 2011)

Erstmal Danke für die Antwort!

Du meinst also dass das Problem der Return Art wirklich ein Problem ist (ich war mir nicht so sicher ob das stimmt was ich vermutet habe, deswegen hake ich lieber mal nach  )?

Das Problem mit den Parametern verstehe ich nicht so ganz - die Klasse sollte ja einen String akzeptieren, der eine SQL Abfrage enthält (aber das habe ich wohl vergessen zu schreiben), also


```
mysqlabfrage("SELECT vorname, ALTER, geburtsjahr FROM person WHERE name=<Parameter>");
```

müsste doch funktionieren? 

Das mit der MYSQL Injection sollte kein Problem sein, da dies kein öffentliches Programm wird - aber Danke für den Hinweis, ich glaub zwecks SQL Injection solle ich etwas nachlesen, schließlich wird nicht alles was ich coden werde privat sein.

Bis jetzt wusste ich nicht einmal dass es ORM-Tools gibt - Danke für den Tip, ich schau mir gerade schonmal mybatis an. Vor allem das sehr einfach daran klingt verlockend!


----------



## herbuin (13. Okt 2011)

nillehammer hat gesagt.:


> Du bekommst bei jdbc als Ergebnis einer Abfrage ein ResultSet, dass alle Zeilen der Antwort enthält. Dieses stellt Dir Methoden zum durchiterieren (.next()) zur Verfügung. Insofern brauchst Du das nicht selbst zu implementieren auch ein multidimensionales Array brauchst Du nicht.



Das verstehe ich jetzt nicht so ganz - kann ich denn das gesamte Resultset an eine andere Klasse übergeben, per return? sprich:


```
Statement s = conn.createStatement ();
   s.executeQuery ("SELECT id, name, category FROM animal");
   ResultSet rs = s.getResultSet ();
return rs;
```

Dann würde ich ja quasi das ResultSet Objekt zurück geben, und müsste es von einer anderen Klasse auch mit .next() etc ansprechen können?



nillehammer hat gesagt.:


> Nein. Der von Dir eingeschlagene Weg, Datenabfragen in einer eigenen Klasse zu kapseln ist schon richtig. Solche Klassen nennt man übrigens DAOs (Data Access Objects).



Schön, ein Keyword zum Suchen, danke!


----------



## nillehammer (13. Okt 2011)

herbuin hat gesagt.:
			
		

> Dann würde ich ja quasi das ResultSet Objekt zurück geben, und müsste es von einer anderen Klasse auch mit .next() etc ansprechen können?


Theoretisch ginge das. Aber an dieser Stelle hast Du schon Recht, dass man wenig gewinnt, wenn man das Resultset einfach weiterreicht. Hier sollte man im DAO schon direkt darüber iterieren, es in ein JDBC-unabhängiges Format umwandeln (z.B. ein Array oder noch besser eine Liste mit Deinen Datenobjekten) und das dann zurück geben.

P.S. Und wo wir gerade bei ORM-Frameworks sind, die Dir das direkte Arbeiten mit JDBC wegkapseln. Es gibt dazu einen Standard JPA (Java Persistence Architecture) und sehr mächtige Implementierungen dieses Standards z.B. Hibernate oder Eclipse Link. Aber mach ruhig erstmal mit Deinem Weg weiter. Obwohl ich sonst immer schreibe "Frameworks benutzen!", finde ich es bei JDBC nicht schlecht, es zu Lernzwecken mal direkt zu programmieren. Da werden einem viele Grundlagen klar, die einem die spätere Nutzung von ORM sehr vereinfachen.


----------



## turtle (13. Okt 2011)

Zu myBATIS habe ich mal ein kurzes Blog geschrieben: http://www.java-forum.org/blogs/turtle/162-mybatis.html


----------



## herbuin (13. Okt 2011)

nillehammer hat gesagt.:


> Theoretisch ginge das. Aber an dieser Stelle hast Du schon Recht, dass man wenig gewinnt, wenn man das Resultset einfach weiterreicht.



Ich hab das jetzt testhalber mal versucht, allerdings bin ich nicht weit gekommen - wahrscheinlich weil ich nicht den richtigen return Typ in der methode spezifiziert habe - aber welcher wäre das denn?

Hier der Code:



```
public readdb (String statement){
        try {
    Connection conn;
    Statement stmt;
    ResultSet res;
    
    //Connector/J_Treiber laden
    Class.forName("com.mysql.jdbc.Driver").newInstance();
    
    //Verbindungsaufbau zu MySQL
    conn = DriverManager.getConnection(
                    pref.getDatabaseLocation() + pref.getDatabasename(), pref.getDBUser(), pref.getDBPassword());
// Select Abfrage ausführen
    stmt = conn.createStatement();
    res = stmt.executeQuery(
            statement);
    ResultSet rs= stmt.getResultSet ();
   

    res.close();
    return rs;
    
}
    catch(Exception e) {
    System.out.println("Error: " + e.toString());
}
```

Und dann müsste ich das zurückgegebene Objekt mit .next() verarbeiten können.


----------



## herbuin (13. Okt 2011)

Und der Gewinner heißt... ResultSet. Sorry hat ein bisschen gebraucht.  Nur bekomme ich jetzt den Error dass ein return statement fehlt - obwohl ich return rs; zurück gebe? :bahnhof:


----------



## turtle (13. Okt 2011)

Ich will ja nicht unnötig meckern, aber ;-)

Dein Code weist bereits gravierende Probleme auf, was die Verwaltung der Ressourcen angeht. Wird in der Routine durch executeQuery() eine Exception geworfen, wird das ResultSet und das Statement nicht geschlossen.

Daher mein Hinweis, dass die korrekte Handhabung von JDBC nicht Ohne ist und besser mit einem ausgereiften Framework durchgeführt werden sollte.


----------



## herbuin (13. Okt 2011)

@turtle Mecker soviel Du willst, dann lern ich wenigstens etwas. 
Ja Du hast schon recht dass der Ansatz nicht der beste ist, aber ich würds gern erstmal zum laufen kriegen (und daraus lernen wieso das nicht klappt) bevor ich mich mit einem anderen Lösungsansatz beschäftige, sonst bleib ich doof. 

Magst Du mir kurz nen Tip geben wieso Netbeans immer noch meckert dass es es immer noch kein return statement gibt, und ob ich die Ressourcen jetzt korrekt wieder freigegeben habe?

Hier nochmal der Code, dieses mal (hoffentlich richtig?) updated mit Schließen der Mysql Verbindung:


```
public ResultSet readdb(String statement) {
        try {
            Connection conn;
            Statement stmt;
            ResultSet res;

            //Connector/J_Treiber laden
            Class.forName("com.mysql.jdbc.Driver").newInstance();

            //Verbindungsaufbau zu MySQL
            Preferences pref = new Preferences();
            conn = DriverManager.getConnection(
                    pref.getDatabaseLocation() + pref.getDatabasename(), pref.getDBUser(), pref.getDBPassword());
// Select Abfrage ausführen
            stmt = conn.createStatement();
            res = stmt.executeQuery(
                    statement);
            ResultSet rs = stmt.getResultSet();

            //Ergebnisse verarbeiten
    /*while (res.next()){
            String name = res.getString("name");
            String pups = res.getString("pups");
            System.out.println("ID: " + name + "   Name: " + pups);
            }*/
            res.close();
            return rs;

        } catch (Exception e) {
            System.out.println("Error: " + e.toString());

        }
```


----------



## Gast2 (13. Okt 2011)

> Magst Du mir kurz nen Tip geben wieso Netbeans immer noch meckert dass es es immer noch kein return statement gibt, und ob ich die Ressourcen jetzt korrekt wieder freigegeben habe?


Im Fall einer Exception wird nichts zurückgegeben.
Falls executeQuery() eine Exception wirft dann werden Statement und Connection nicht geschlossen. Das schließen der Ressourcen löst du am besten im finally block, der wird in jedem fall aufgerufen.


----------



## turtle (13. Okt 2011)

Ich hab echt nur kurz versucht, aber gibt vielleicht ein paar Hinweise:

```
public ResultSet readdb(String statement) {
		ResultSet res = null;
		;
		try {
			// Verbindungsaufbau zu MySQL
			Connection conn = DriverManager.getConnection("", "", "");
			Statement stmt = null;
			try {
				stmt = conn.createStatement();
				try {
					res = stmt.executeQuery(statement);
				} finally {
					if (res != null) res.close();
				}
			} finally {
				if (stmt != null) stmt.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return res;
	}
```
Aber könnte sein, dass da echt noch Ressourcen-Leaks drin sind. Das letzte Mal plain JDBC habe ich vor Jahren das Letzte Mal geschrieben (und wie man am Code sieht, zu Recht).


----------



## turtle (13. Okt 2011)

Und hier mal Pseudo-Code für myBATIS

```
SqlSession sqlSession = sqlSessionFactory.openSession();
			try {
				List<Map> listMap = sqlSession
						.selectList("selectStatement");

				for (Map map: listMap) {
					Object object = map.get("SPALTE");
                                }
			} finally {
				sqlSession.close();
			}
```
und Du kannst über die Map auf alle Spalten aus dem ResultSet zugreifen. myBATIS hat alle Spalten des ResultSets in eine Map überführt.Ich finde was Einfacheres gibbet es nicht.


----------



## herbuin (13. Okt 2011)

EikeB hat gesagt.:


> Im Fall einer Exception wird nichts zurückgegeben.
> Falls executeQuery() eine Exception wirft dann werden Statement und Connection nicht geschlossen. Das schließen der Ressourcen löst du am besten im finally block, der wird in jedem fall aufgerufen.



Finally Block? Du meinst nach dem Ende der Try/catch anweisung aber noch innerhalb der Methode? oder nach der Methode vor dem Ende der Klasse? Ich hab beides probiert, aber schließen lässt sich die Verbindung dann nicht mehr, außer es ist innerhalb des entsprechenden try blocks?

Bezüglich dem Error "missing return statement" sollte ich vielleicht noch hinzufügen dass er für folgende Zeile gilt:


```
public ResultSet readdb(String statement)
```

Leute, sorry für die ganzen blöden Fragen. So wies aussieht bin ich noch gaaanz am Anfang meiner Java Karriere... :autsch:


----------



## herbuin (13. Okt 2011)

turtle hat gesagt.:


> Ich hab echt nur kurz versucht, aber gibt vielleicht ein paar Hinweise:
> 
> ```
> public ResultSet readdb(String statement) {
> ...




Sieht ja komplett anders aus als mein Code, danke schon mal für die Mühe! Jetzt versteh ich auch was mit final block gemeint war... So ich werd jetzt erstmal meine und Deine Version vergleichen und etwas rumprobieren! :toll:


----------



## herbuin (13. Okt 2011)

OOOOkkkkk... Ich bin gerade so happy, denn dank euerer Hilfe (und vor allem turtle's Codeversion) scheint alles zu funktionieren. Falls jemand mal ein ähnliches Problem haben sollte, hier der Code:

databaseIO1.java:

```
package administrative;

import java.sql.*;

public class databaseIO1 {

    public ResultSet res = null;
    Statement stmt = null;

    public ResultSet readdb(String statement) {
       
        

        try {
            // Verbindungsaufbau zu MySQL
            //Preferences Objekt enthält Zugangsdaten
            Preferences pref = new Preferences();
            Connection conn = DriverManager.getConnection(pref.getDatabaseLocation() + pref.getDatabasename(), pref.getDBUser(), pref.getDBPassword());
            
            try {
                stmt = conn.createStatement();
                try {
                    res = stmt.executeQuery(statement);
                } finally {
                    /*if (res != null) res.close(); Verbindungen werden durch Methode closedb geschlossen, die extra aufgerufen werden muss */
                }
            } finally {
                /*if (stmt != null) stmt.close(); Verbindungen werden durch Methode closedb geschlossen, die extra aufgerufen werden muss */
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return res;
    }

    public void closedb() {
        try {
            res.close();
            stmt.close();
        }
        catch(SQLException e){e.printStackTrace();}
    }

}
```


```
package administrative;
import java.sql.*;


public class TestWriteReadDatabase {
    public static void main(String[] args){
    databaseIO1 dbio = new databaseIO1();
   try {
    ResultSet test;
            test = dbio.readdb("SELECT name, vorname FROM testtabelle");
    
            while (test.next()){
            String name = test.getString("name");
            String vorname = test.getString("vorname");
            System.out.println("Name: " + name + "   Vorname: " + vorname);
            }
    }
   catch (SQLException e) {
			e.printStackTrace();
    }
   finally{dbio.closedb();}
}
}
```

Zwecks dem Schließen der Verbindung, scheint es so als müsste nur ResultSet geschlossen werden, und das Schließen von Statement ist überflüssig? (Zumindest ist nur das Schließen von ResultSet in meinem Mysql5 Buch beschrieben)

DANKE! :toll:


----------



## nillehammer (13. Okt 2011)

herbuin hat gesagt.:
			
		

> Zwecks dem Schließen der Verbindung, scheint es so als müsste nur ResultSet geschlossen werden, und das Schließen von Statement ist überflüssig? (Zumindest ist nur das Schließen von ResultSet in meinem Mysql5 Buch beschrieben)


Laut der API-Dokumentation von ResultSet (ResultSet (Java Platform SE 6)) wird genau anders herum ein Schuh draus. Wenn das Statement geschlossen wird, wird das damit erzeugte ResultSet automatisch mit geschlosssen. Ich schließ aber zur Sicherheit immer alles selbst.


----------



## Gast2 (13. Okt 2011)

@turtle
Das kann man schöner schreiben:

```
public ResultSet readdb(final String statement) {
    	Connection con = null;
        Statement stmt = null;
        ResultSet res = null;

        try {
            // Verbindungsaufbau zu MySQL
            con = DriverManager.getConnection("", "", "");
            stmt = conn.createStatement();

            res = stmt.executeQuery(statement);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
        	IOUtil.safeClose(rs);
        	IOUtil.safeClose(stmt);
        	IOUtil.safeClose(con);
        }
        return res;
    }
```

In IOUtil gibts dann Methoden nach dem Schema:

```
public static void safeClose(final ResultSet rs) {
    	if (rs != null) {
    		try {
				rs.close();
			} catch (SQLException e) { /** nothing to do here.. */ }
    	}
    }
```


----------

