# Struktur eines Datenbankinterface?



## netz-rack.c (13. Mrz 2007)

Hallo,

ich habe mal gelernt, ich solle meinen Code überdenken, wenn ich merke das ich den selben Code mehrmals schreibe.

Daher kommen mir bei meinem aktuellem Design doch so einige Zweifel:

Zum Problem: Ich soll eine Oberfläche für eine Datenbank schreiben, die den Werkbestand uns sie zugehörigen Fotos in unserem Museum erfasst. Da ich hier poste arbeite ich ja ganz offensichtlich mit Java und stelle die Verbindung zur PostgreSQL-Datenbank per JDBC her.

Ich habe also begonnen, die einzelenen Ansichten mit Logik zu füllen, dabei merke ich, dass ich immer wieder die Datenbankverbindung initialisiere und abfragen ausführe. Dazu ist aber fast immer der selbe Code notwendig (zumindest bis zu dem Punkt, an dem ich die Abfrageergebnisse auf Objekte ummünze)
Kann man diesen Code nicht irgendwie in eine Klasse auslagern, die die eine Verbindung hält, und darüber immer wieder neue Abfragen auwsführt, vielleicht sogar mit einem FIFO-Zwischenspeicher, der einen Forschrittsbalken oder irgendeine andere Rückmeldung an den Benutzer gibt?

Anscheinend habe ich hier vom Programmdesign bzw. von der Objektorien her ein Denkproblem.

MfG
Carsten


Ach, bevor ich's vergesse und wer meckert, hier noch kurz ein Beispiel für den Teil der sich wiederholt:
(Es sind zwar meißtens SELECT-Abfragen und keine Updates, ich hatte aber gerade diesen Teil (Stichwortzuweisung) geöffnet)


```
try {
			Class.forName(DB_DRV);
		} catch (ClassNotFoundException e) {
			JOptionPane.showMessageDialog(this.getRootPane(),
				    "Konnte Datenbanktreiber nicht finden!!",
				    "Datenbank-Fehler",
				    JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
			return;
		}
		
		try {
			Connection c = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
			
			Statement s = c.createStatement();
			
			try {
				s.executeUpdate("INSERT INTO \"BDB-1.0\".stichwort(wort) VALUES ('"+stwName+"')");
			} catch (org.postgresql.util.PSQLException e) {
				JOptionPane.showMessageDialog(this.getRootPane(),
					    e.getLocalizedMessage(),
					    "Datenbank-Fehler",
					    JOptionPane.ERROR_MESSAGE);
				e.printStackTrace();
			}
		
			s.executeUpdate("INSERT INTO " +
					"\"BDB-1.0\".verschlagwortung(werk_id, stichw_id) " +
						"SELECT " +
							"werk_id, stichw_id " +
						"FROM " +
							"\"BDB-1.0\".werk, " +
							"\"BDB-1.0\".stichwort " +
						"WHERE " +
							"werk_id="+ werk_id +
							" AND " +
							"wort='" + stwName + "'");
		
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(this.getRootPane(),
				    e.getLocalizedMessage(),
				    "Datenbank-Fehler",
				    JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
			return;
		}
```


----------



## SlaterB (13. Mrz 2007)

soviel Code hast du ja gar nicht mal, 

da sind zum einen die Fehlermeldungen, was wenig mit JDBC zu tun hat, eher mit Swing,
die könntest du kürzen zu einer Operation a la 
showDBError(e);

der Rest ist gar nicht so übel, eine Anfrage als SQL geht mit geringen Aufwand kaum kürzer,
du könntest das alles in eine Logikklasse auslagern:

try { 
  logicObject.doXyUpdate(Parameter);
} catch (ClassNotFoundException e) { 
} catch (SQLException e) { 
...

hast dann natürlich den Aufwand mit den Parametern..


--------

den Treiber solltest du eh nur einmal beim Programmstart laden,
dann kann es danach ClassNotFoundException nicht mehr geben

--------

auf SQLFehler anders zu reagieren als in der GUI SQLException abzufangen wäre auch zu empfehlen,
ist aber wieder etwas aufwendiger
(Statusfeld oder eigene Exception)


----------



## netz-rack.c (13. Mrz 2007)

Naja, das Hauptproblem ist ja eher der massive Codeblock, wenn ich die Daten aus der Datenbank ins GUI schreibe, also in dem Beispiel hinter dem UPDATE.

Mein gefühl sagt mir ich sollte diese Logik von dem GUI trennen. in einem Fall habe ich das auch schon mit einer Extraklasse gemacht, die den Swingworker benutzt um das ganze auch aus dem AWT-Thread rauszuhalten.
Was imho gerade bei der bearbeitung von BLOBs ganz gut ist, da die JAI-Operationen immer so lange brauchen.

Ich hätte nur gern eine algemeine Lösung, bei der ich solche Anfragen einreihen könnte.

Demnächst bekomme ich ganze Listen von Bildern mit Informationen die alle verarbeitet werden wollen. In dem Fall würde das erste Internal Frame ja bei hoher Belastung (Binary-Stram verarbeiten) ja die ganze Anwendung lam legen. Ich würde also gerne das Ganze JDBC/Netzwer/JAI-Zeug aus dem AWT-Thread rausbekommen, wenn es geht aber so, das ich trozdem noch Rückmeldungen an den User geben kann. (z.b. über setProgress() im SwingWorker)

Ich bin in dem Zusammenhang gerade über Hibernate gestolpert, lohnt es sich, dass ich es mir mal ansehe? Oder ist es zu komplex um es in (relativ) kurzer Zeit benutzen zu können? (bin eher Java-Autodidakt troz einm halben Jahr Unterricht damals in der Schule :### )


MfG
Carsten


----------



## Guest (13. Mrz 2007)

Wie wäre es mit sowas dieser Art. Schreibe eine abstrakte Klasse, die 
solche Verwendung ermöglicht.
Die eigentlichen Aufrufe der Methoden kannst du dann in einen SwingWorker
vepacken und schon hast du eine saubere Trennung zwischen Persistenz- und
GUI-Logik.
Oder vergiss das ganze und verwende einen vernünftigen OR-Mapper. 

```
public final class BookDB extends AbstractDB<Book>
{
   /**
   * Privater Konstruktor.
   * Nicht instanzierbare Util-Klasse mit lauter static-Methoden.
   */
   private BookDB() {}

   /**
   * Callback von AbstractDB. Wird für jede Zeile des ResultSets aufgerufen.
   * ...
   */
   protected static Book getValue(ResultSet rs) throws SQLException;
   {
      Book result = new Book();

      // DBUtil ist eine Hilfsklasse, die das Lesen von Parametern vereinfacht/verkürzt
      //
      // z.B. Lesen von Int aus ResutSet
      //
      // public static Integer getInteger(ResultSet rs, String fieldName) throws SQLException
      // {
      //    int intValue = rs.getInt(fieldName);
      //    return !rs.wasNull()? Integer.valueOf(intValue) : null;
      // }

      result.setISBN(DBUtil.getString(rs, "isbn"));
      result.setAuthor(DBUtil.getString(rs, "author"));
      result.setTitle(DBUtil.getString(rs, "title"));
      return result;
   }

   public static List<Book> getBooksByAuthor(String author) throws DAOException
   {
      try
      {
         // getValueList ist eine generische Methode in AbstractDB und bedient sich
         // der überschriebenen Methode getValue(ResultSet rs), um die einzelnen
         // Zeilen in Book-Instanzen zu konvertieren
         // Signatur: protected static List<T> getValueList(String sqlStatement, Object ... args) throws SQLException
         return getValueList(
            "SELECT isbn, author, title FROM Book WHERE author = ?",
            author
         );
      }
      catch(SQLException e)
      {
         Logger.error(e);
         // hier evtl. noch eine Fehlermeldung setzen, damit der User nicht mit SQL-Meldungen
         // schockiert wird.
         // z.B. new DAOException(String.format("Fehler beim Lesen von Büchern anhand des Authornamens (%1$s).", author), e);
         throw new DAOException(e);
      }
   }

   public static Book getBookByISBN(String isbn) throws DAOException
   {
      try
      {
         // getValue ist eine generische Methode in AbstractDB und bedient sich
         // der überschriebenen Methode getValue(ResultSet rs), um das Ergebnis
         // der Abfrage in eine Book-Instanzen zu konvertieren.
         // Signatur: protected static T getValue(String sqlStatement, Object ... args) throws SQLException
         return getValue(
            "SELECT isbn, author, title FROM Book WHERE isbn = ?",
            isbn
         );
      }
      catch(SQLException e)
      {
         Logger.error(e);
         throw new DAOException(e);
      }
   }

}
```


----------



## netz-rack.c (13. Mrz 2007)

Und ein solcher O/R-Mapper lagert ide Arbeit dan auch vernünftig in externe Threads aus?

Wenn Hibernate jetzt noch die möglichkeiten für Fortschrittsanzeige oder ähnliches bietet und nicht zu komplex ist, so währe es die Ideale Lösung!!?


----------



## KSG9|sebastian (13. Mrz 2007)

Hibernate? Ne Fortschrittsanzeige?
Weiß du was Hibernate ist? Hibernate ist für die Persistenz und das Mapping Datenbank <-> Java Objekte zuständig. Nicht mehr, nicht weniger. Es tut im Endeffekt nichts anderes wie deine Methode welche einen SQL-Query abfeuert und aus dem Resultat ein Objekt baut. Nur das du eben nichtsmehr mit JDBC zu tun hast und Hibernate 100000x komplexer ist als meine Beschreibung 
Auch mit Hibernate wird dir nichts anderes übrig bleiben als die Performancelastige Arbeit auszulagern


----------



## netz-rack.c (14. Mrz 2007)

hmn, ... OK
(Bei der Fortschrittsanzeige hast du mich vmtl. falsch verstanden, mir ging es darum, ob Hibernate Auskunft über den Status/Fortschritt des mapping-Vorgangs gibt)

Wenn ich jetzt also das MVC-Designmuster als Idee benutze, so ist Hibernate bzw die von ihm hergestellten Objekte nur das Modell??

Währe es demnach also der leistungsfähigste Weg, wenn der Controler Hibernate (wenn es geht in einem eigenen Thread) startet und die Objekte dann in das GUI (View) schreibt?


----------



## Guest (21. Mrz 2007)

Hibernate ist nicht das Model. Das Model bedient sich Hibernate, um an Daten ran 
zu kommen.


----------

