MySQL Eine Instanz von eigenem DBWrapper -> Fehler durch mehrfachbenutzung möglich?

serjoscha

Mitglied
Hallo zusammen.

Ich habe mir für ein Java Programm einen MySQL Wrapper geschrieben, damit es schlichtweg einfacher für mich ist im Programm Datenbankverbindungen aufzubauen und Querys abzusetzen.

Jetzt ist es so, dass die Daten für die DB aus x Threads kommen können.

Das Programm analysiert kurz gesagt hot plug devices (usb festplatten und sticks) und startet sobald es mitbekommt, dass ein neues Gerät angeschlossen wurde ein Thread das seinerseits Änderungen auf genau diesem gerade angeschlossenen Laufwerk überwacht und die gefunden Daten dann in die
DB Speichert (und das auch fortan tut, bis die Platte getrennt wird).

Und nun zum Problem:

Die Daten in der DB sind zum Teil (nicht komplett!) völlig wirr. Ich war nicht in der Lage herauszufinden, wann genau das passiert, wann also die Daten Integrität über die Wupper geht. Aber ich mutmaße,
dass das passiert, wenn 2 Threads gleichzeitig meinen DB Wrapper (Objekt) benutzen *von dem es nur eine Instanz gibt*

Mit wirren Daten meine ich, dass *Teile* des Inhalts einer Festplatte in der DB der falschen Festplatte (dafür gibts Einträge in der DB) zugeordnet werden.

Aber letztendlich macht das Programm nicht mehr als rekursiv über den Inhalt der Platte zu iterieren und alles in die DB zu speichern. Mehr passiert da nicht. An der Stelle kann also eigentlich kein wirr-warr entstehen.

Kann es also sein, dass die "gleichzeitige" Benutzung des DB Wrappers aus 2 verschiedenen Threads heraus Ressource Probleme verursacht die Daten völlig wirr und über Kreuz in die DB schreiben?

Hier der DB Wrapper:

Java:
package misc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;

import com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException;

public class MySQL_Wrapper {

	private Connection con;
	
	private String user, pass, host, db;
	
	private boolean connected;
	
	public MySQL_Wrapper() {
		// Explicit set vars to null
		this.user = null;
		this.pass = null;
		this.host = null;
		this.db = null;

		this.loadDriver();
	}
	
	public MySQL_Wrapper(String user, String pass, String host, String db) {
		this.user = user;
		this.pass = pass;
		this.host = host;
		this.db = db;
		
		this.loadDriver();
		
		this.connect();
	}
	
	private void loadDriver() {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			Logger.getLogger("global").info("KRITISCHER FEHLER: Konnte MySQL Driver nicht laden!\n" + e.getMessage());
		}
	}
	
	/**
	 * tells the wrapper to connect to the DB
	 * @param user
	 * @param pass
	 * @param host
	 * @param db
	 * @return a boolean which tells wether the connection was successfull or not
	 */
	public boolean connect(String user, String pass, String host, String db) {
		try {
			this.con = DriverManager.getConnection( "jdbc:mysql://" + host + "/" + db, user, pass);
			connected = true;
			return true;
		} catch (SQLException e) {
			connected = false;
			Logger.getLogger("global").info("KRITISCHER FEHLER: Konnte nicht zur Datenbank verbinden!\n" + e.getMessage());
			return false;
			//System.exit(0); // new: dont exit for it seems the program looses connection from time to time
		}
	}
	
	public boolean connect() { // delegates
		return this.connect(this.user, this.pass,this.host, this.db);
	}
	
	public void setConnectionData(String user, String pass, String host, String db){
		this.user = user;
		this.pass = pass;
		this.host = host;
		this.db = db;
	}
	
	public void disconnect() {
		try {
			this.con.close();
		} catch (SQLException e) {
			Logger.getLogger("global").info("Konnte Verbindung zum MySQL-Server nicht trennen.\n" + e.getMessage());
		}
	}
	
	/**
	 * Nicht geeignet um Daten aus der Datenbank zu lesen
	 * @param qry Die Query
	 * @return affected rows
	 */
	public int sendQry(String qry) {
		if(this.con == null)
			Logger.getLogger("global").info("KRITISCHER FEHLER: Du hast eine Query agesetzt ohne vorher eine Verbindung zur DB aufgebaut zu haben");
		
		/**
		 * Note: Copying the Entries into the Archive Table causes a "Duplicate Entry"-Exception every time...
		 */
		try {
			Statement stmt = this.con.createStatement();
			qry = this.escapeQuery(qry); // escape
			int affected = stmt.executeUpdate(qry);
			return affected;
		}
		catch (MySQLIntegrityConstraintViolationException e) {
			//System.out.println(e.getMessage());
		} catch (SQLException e) {
			Logger.getLogger("global").info("KRITISCHER FEHLER: Die Query enthält Fehler!\n" + e.getMessage() + "\n" + e.getSQLState());
		}
		return 0; // im Fehlerfall
	}
	
	/**
	 * Query, die ein Result set zurückgibt... Die benutzung ist etwas hacky umgesetzt
	 * @param qry
	 * @param anyVal völlig egal! Kann alles sein, true oder false oder sonst was... der Wert ist völlig egal und wird nur benutzt um eine andere Methoden Signatur zu erhalten
	 * @return ResultSet der Daten die gefunden worden
	 */
	public ResultSet sendQry(String qry, Object anyVal) {
		if(this.con == null)
			Logger.getLogger("global").info("KRITISCHER FEHLER: Du hast eine Query agesetzt ohne vorher eine Verbindung zur DB aufgebaut zu haben");
		try {
			Statement stmt = this.con.createStatement();
			qry = this.escapeQuery(qry); // escape
			return stmt.executeQuery(qry);
		}
		catch (MySQLIntegrityConstraintViolationException e) {
		} catch (SQLException e) {
			Logger.getLogger("global").info("KRITISCHER FEHLER: Die Query enthält Fehler!\n" + e.getMessage() + "\n" + e.getSQLState());
		}
		return null; // !!!
	}
	
	private String escapeQuery(String baseString) {
		// Currently only escape backspaces -> \ to \\
		return baseString.replace("\\", "\\\\");
	}
	
	public boolean isConnected() {
		return connected;
	}

}

Danke im Voraus!

Liebe Grüße, Serjoscha

ps. ich nehme auch gerne Tipps für eine Alternative zu meinem eigenen DB Wrapper entgegen
 
Das Connection-handling würde ich global machen. Ansonsten kann ein Thred einfach die Connection killen und dann wars das für die anderen Threads.

Du kannst auch jedem Thread eine Instanz vom Wrapper anbieten, dann kommen sie sich nicht in die Query, jedenfalls, was den Wrapper betrifft.

* loadDriver muss Du nur einmal in der App machen
* anyVal wird gar nicht verwendet
 
Zuletzt bearbeitet:
Das Problem ist wohl, dass der Rechner zwischenzeitlich immer mal in den Standby fährt und dann die Verbindung einen Timout bekommt. Ich vermute dass der mySQL connector NOPs sendet um die Verbindung am Leben zu halten, was er dann aber nicht kann wenn der Rechner im s3 ist.
Wenn der Rechner dann wieder da ist und das Programm versucht Daten in die DB zu schreiben kracht es mit einer nicht mehr validen Verbindung, was dazu führt, dass ich für jedes Mal wenn Daten geschrieben werden sollen eine Verbindung aufbaue und sie wieder trenne, wenn die Daten geschrieben worden sind.

Pseudecode:

Überwachungs Thread meldet dem Controller Änderungen:

Controller:

- Verbinde zur DB
- Parse Daten
- Persistiere Daten
- Trenne Verbindung

Ich vermute dass genau das zu einem Problem führt wenn 2 Threads sich gleichzeitig beim Controller melden. Der läuft seinerseits nämlich nicht im Thread.

Wird die Listener Implementation dann eigentlich ge'queue'd?

Danke für deine Antwort!
 

Neue Themen


Zurück
Oben