# ResultSet als Parameter an andere Klasse übergeben



## Stratos22 (12. Nov 2021)

Hallo zusammen!
Ich bin Programmieranfänger und habe die Aufgabe ein kleines Spiel mit Login aus einer Datenbank zu programmieren. Der Datenbankzugriff funktioniert soweit. Wenn das DAO einen Eintrag mit passendem Namen und Passwort findet, soll es ein Objekt der Klasse Spieler(name,passwort) erstellen. Aber irgendwie komm ich nicht drauf, wie ich das hinbekomme. Ich wäre um jede Art der Hilfe dankbar!

Hier der Code meiner Spieler-Klasse:

[CODE lang="java" title="Spieler"]public class Spieler{//dem Spieler-Objekt wird eine ArrayList mit allen Spielern aus der Datenbank mitgegeben
    private String name;
    private String passwort;

    public Spieler(String name, String passwort) {
        this.name = name;
        this.passwort = passwort;   
    }


}//Ende Klasse[/CODE]


Hier der Code meiner SQL_DAO -Klasse:

[CODE lang="java" title="SQL_DAO"]import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

//DIESE KLASSE HOLT SICH DAS VON DB-CONNECTOR ERZEUGTE CONNECTOR-OBJEKT UND BIETET SQL-ABFRAGEMETHODEN, DIE DER CONTROLLER NUTZEN KANN

public class SQL_DAO {

    private Connection meineConnection;
    //private Spieler spieler;

    public SQL_DAO() {
        meineConnection = DB_Connector.holeEinzigesDBObjekt();
    }

    //TODO Hier folgen die SQL-Befehle in Methoden, die der Controller verwenden kann
    //getSpielerFromDB bekommt 2 Parameter von den getter-Methoden der LoginGUi mit und setzt, nachdem eine SQLQuery durchgeführt wurde next() auf true oder false.
    public Spieler getSpielerFromDB(String Name, String Passwort) throws SQLException {//----------------------------------> was ist hier los?
        String query = "SELECT * FROM VierGewinntLogin WHERE Name=" + "'" + Name + "'" + "AND Passwort=" + "'" + Passwort + "'";
        //SELECT 'Name','Passwort' FROM 'users' WHERE 'Name' = ? AND 'Passwort' = ?


        //Das Statement-Objekt braucht man für die Datenbankabfrage
        Statement stmt = meineConnection.createStatement();

        //Die Methode executeQuery(query) erzeugt ein Objekt rs der Klasse ResultSet, das die Ergebisse der DB-Abfrage enthält
        ResultSet rs = stmt.executeQuery(query);
        Spieler spieler = new Spieler(Name, Passwort);
        spieler.add(rs.getString(2), rs.getString(3));

        //rs ist wie eine Tabelle, diese kann eben wie eine Tabelle durchlaufen werden. Der Befehl dafür ist next().
        //Eine if-Bedingung reicht, da ja eh nur ein Spieler gesucht wird. Falls dieser gefunden wurde ist next() == true
        if(rs.next()==true) {
            System.out.println("Login erfolgreich");
            return spieler;

            //Daten aus rs herausholen und Spieler erzeugen und returnen;
            //return Spieler();
        }else {
            System.out.println("Login gescheitert");
        }
        rs.close();
        stmt.close();
        return null;

    }//Ende Methode getSpielerFromDB
}//Ende Klasse[/CODE]


----------



## kneitzel (12. Nov 2021)

Also ich muss gestehen, dass ich so noch nicht ganz schlau aus Deinem Code und dem, was Du da schreibst, werde.

Du hast eine Klasse Spieler, die bisher nur Name und Passwort als Attribute enthält. Daher ist die frage, was Du da an add Aufrufen hast.
Im Kommentar steht etwas von wegen ArrayList mit allen Spielern. Aber wieso? Kannst Du diesen Aufbau ggf. etwas weiter erläutern?
Und auch, was da überhaupt in der Tabelle Spieler genau stehen soll...

Bezüglich Code: 
a) Java Naming Conventions: Bitte Parameter immer klein starten. Klassen werden in Upper Camel Case Geschrieben, also keine _ oder so.

b) Eine SQL Query bitte NIE so bauen! Ich habe sonst das Passwort `'; DROP TABLE VierGewinntLogin;` - Dazu gibt es das PreparedStatement - im Kommentar findet man ja auch schon, wie da so ein Query aussehen würde.

c) Das ResultSet ist wie eine Liste zu sehen, mit der Du mit next() immer von einem Element zum nächsten kommst. Am Anfang stehst Du aber noch bei keinem Element, daher ist ein Zugriff auf Werte nicht möglich. Diese rs.GetString sind da also schlicht erst einmal falsch.

d) Nutze try-with-resources )oder packe die close in ein finally incl. fangen möglicher Exceptions). Die close() werden derzeit nur ausgeführt, wenn der Login nicht erfolgreich war.

e) Und die Bezeichner würde ich auch einmal überdenken. Hole einziges DB Objekt - das liefert eine Connection? In der DAO Klasse, die Daten von der DB holt: Da muss man in der Methode nicht mehr schreiben, dass es von der DB geholt wird. getSpieler würde da auch reichen. 

Das wären so meine ersten Anregungen zu dem Code in der Hoffnung, dass es hilfreich ist.


----------



## Stratos22 (14. Nov 2021)

Erstmal ein großes Dankeschön für deine ausführliche Antwort und deine Zeit!!
Und auch noch gleich eine Entschuldigung von mir, dass das Text so wenig transparent war. Da waren noch einige Kommentarleichen von älteren Gedankengängen drin, die ich hätte vorher herauslöschen müssen. Wenn ich schon um Hilfe bitte, sollte ich da wohl schon drauf achten.
Dein Kommentar hat mich aber dennoch weiter gebracht. Ich habe die Klasse jetzt etwas abgeändert und sie scheint jetzt mehr oder weniger zu funktionieren (Probleme hab ich jetzt in einer anderen Klasse, da will ich aber selbst noch etwas rumprobieren, ob ich es hinbekomme).

Mein Ziel ist es, dass wenn die Klasse SQL_DAO eine passende Kombination aus Name und Passwort in der DB findet (Argumente werden aus einem Objekt der Klasse LoginGUI geholt), ein Spieler-Objekt erzeugt wird, dass die Attribute name und passwort hat. Das Attribut name kann ich dann später verwenden, um im Spielfenster anzuzeigen, welcher Spieler gerade dran ist.
Die Schwierigkeit, an der ich gerade hänge ist, dass 2 Login-Fenster hintereinander kommen sollten. Login des 1. Spielers und Login des 2. Spielers. Es soll jeweils ein Spieler-Objekt erzeugt werden. Aber so richtig krieg ich das noch nicht hin.

Ich hab jetzt mal die SQL_DAO bereinigt und stell sie nochmal rein. Ich hoffe die Klasse passt jetzt in etwa so, dass sie meine Anforderungen erfüllen kann...

[CODE lang="java" title="SQL_DAO - verändert"]import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

//DIESE KLASSE HOLT SICH DAS VON DB-CONNECTOR ERZEUGTE CONNECTOR-OBJEKT UND BIETET SQL-ABFRAGEMETHODEN, DIE DER CONTROLLER NUTZEN KANN

public class SQL_DAO {

    private Connection meineConnection;

    public SQL_DAO() {
        meineConnection = DB_Connector.holeEinzigesDBObjekt();
    }

    //getSpielerFromDB bekommt 2 Parameter von den getter-Methoden der LoginGUI mit und setzt, prüft ob diese Parameter so in der Datenbank stehen und gibt, wenn dem so ist, ein Objekt der Klasse spieler mit eben diesen Parametern zurück
    public Spieler getSpielerFromDB(String name, String passwort) throws SQLException {
        String query = "SELECT * FROM VierGewinntLogin WHERE Name=" + "'" + name + "'" + "AND Passwort=" + "'" + passwort + "'";
        //SELECT 'Name','Passwort' FROM 'users' WHERE 'Name' = ? AND 'Passwort' = ? --> wäre aus Sicherheitsgründen besser, wird aber hier nicht genutzt


        //Das Statement-Objekt braucht man für die Datenbankabfrage
        Statement stmt = meineConnection.createStatement();

        //Die Methode executeQuery(query) erzeugt ein Objekt rs der Klasse ResultSet, das die Ergebisse der DB-Abfrage enthält
        ResultSet rs = stmt.executeQuery(query);

        //rs ist wie eine Tabelle, diese kann eben wie eine Tabelle durchlaufen werden. Der Befehl dafür ist next().
        //Eine if-Bedingung reicht, da ja eh nur ein Spieler gesucht wird. Falls dieser gefunden wurde ist next() == true

        rs.next();//Der Zeiger springt in die einzige Zeile des ResultSet-Objekts---------------------------> müsste man das nicht eigentlich reinschreiben? Warum hat das letztens funktioniert?
        if(rs.next()==true) {
            System.out.println("Login erfolgreich");
            name = rs.getString(2);//der Eintrag in der zweiten Spalte der DB (in der ersten Spalte steht die ID) wird in der Variable name gespeichert
            passwort = rs.getString(3);//der Eintrag in der dritten Spalte der DB wird in der Variable passwort gespeichert
            Spieler spieler = new Spieler(name, passwort);//ein neues Spieler-Objekt namens spieler wird mit den Argumenten aus den Variablen name und passwort (also die beiden getString-Methoden) erzeugt
            return spieler;//das erzeugte spieler-Objekt wird zurückgegeben
        }else {
            System.out.println("Login gescheitert");//andernfalls soll ausgegeben werden, dass der Login gescheitert ist
        }
        rs.close();
        stmt.close();
        return null;
    }//Ende Methode getSpielerFromDB
}//Ende Klasse[/CODE]

Zu deinen Anmerkungen:
a) Werde ich in Zukunft beachten, ich lass sie jetzt mal so, damit ich mir nicht wieder irgendwas zerschieße ^^
b) Das Problem ist mir bewusst, da ich aber wohl nie Softwareentwickler werde, nicht ganz so tragisch
c) Ich hab jetzt die Methode rs.next() aufgerufen, bevor ich ihren boolean abfrage. Das müsste dann so passen, denke ich?
d) Das kapier ich nicht ganz. Ich werde mich einlesen. Danke!
e) Die Bezeichnung wurde in diesem Fall tatsächlich von unserem Betreuer so gewählt. Wenn ich so darüber nachdenke, ist das tatsächlich Quatsch.

Das hat auf alle Fälle geholfen, danke nochmal!
Vielleicht darf ich ja hier nochmal schreiben, wenn ich irgendwo längere Zeit nicht weiterkomme. 😅


----------



## Neumi5694 (13. Dez 2021)

```
if(rs.next()==true) {...}
```
Ersetz solche Ausdrücke durch Ausdrücke wie

```
if(rs.next()) {...}
```
Zu d)
Eine try-with-Resource ist für sog. AutoCloseables gedacht. Ein Objekt wird im Header initialisiert und wird nach Beenden des Blocks automatisch geschlossen mit allem, was dazugehört, auch wenn während der Laufzeit ein Fehler auftritt.
Du kannst in einem Header übrigens - falls eh nur ein Zugriff stattfindet - von Connection bis ResultSet alles gleichzeitig initialisieren und musst nicht schachteln.
try (Connection conn = ...; Statement statement = ...; ResultSet resultSet = ...) {...}
Die werden dann alle nach dem Codeblock geschlossen, selbst wenn darin ein return stattfindet.


----------

