# update sperren bei client/server anwendung



## Guest (21. Mai 2007)

Hallo,
ich möchte eine Client/Server Anwendung programmieren, bei der einzelne Daten vom Client über den Server in eine MySQL Datenbank eingetragen werden.
Die Kommunikation zwischen Client und Server wollte ich mittels serialisierten Objekten machen, die ich übers Netzwerk übertrage.

Mein Problem:

Der Client soll zB neue Mitarbeiter in die MySQL Datenbank eintragen können. Dabei wollte ich abfragen, ob der hinzuzufügende Mitarbeiter bereits in der Datenbank eingtragen ist (um Duplikate zu vermeiden). Was ist aber, wenn ich die Abfrage starte, ob es den Mitarbeiter gibt, und in der zwischenzeit trägt ein anderer Client diesen Mitarbeiter ein? Also kann ich die Datenbank für eine gewisse Zeit sperren? Ich glaube es gibt einen Lock Mechanismus für MySQL Tabellen, allerdings weis ich nicht ob das das richtige ist.
Wie wird sowas normalerweise gelöst?


----------



## Wildcard (21. Mai 2007)

*verschoben*


----------



## kleiner_held (21. Mai 2007)

Das macht die Datenbank für dich, solange du ein einer Transaktion arbeitest (also z.B. in einer JDBC Connection AutoCommit auf false hast)
Das ganz Konzept ist doch ein wenig zu umfangreich um es in einem Post zu erklären, deswegen verweise ich mal auf:
de.wikipedia.org/wiki/Transaktion_(Informatik)


----------



## Guest (22. Mai 2007)

```
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.BatchUpdateException;
import java.sql.SQLException;

public class MySQL {

	public void doTransaction(){
	int[] updateCounts = new int[10];

	try
	{
		Class.forName("com.mysql.jdbc.Driver");
		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/testTransaction",
         			"xxx", "xxx");
	
		try
		{
			conn.setAutoCommit( false );
       

			Statement stm = conn.createStatement();
			stm.addBatch( "INSERT INTO transaction (Name)VALUES ('xxx')" );
			stm.addBatch( "INSERT INTO transaction (Name) VALUES ('xd')");
                
			updateCounts = stm.executeBatch();
			System.out.println("Counts: "+updateCounts);
			stm.close();
			//conn.commit();
			conn.close();

       } 
		catch ( BatchUpdateException bue )
		{
			conn.rollback();
			System.out.println("BatchUpdateException");
			bue.printStackTrace();
		}

	}
	catch(SQLException sqle)
    {
       System.out.println("SQL Fehler");
    }
    catch(ClassNotFoundException cnfe)
    {
    	cnfe.printStackTrace();
       System.out.println("Die Klasse wurde nicht gefunden");
    }
}

}
```

mit obenstehenden code kann ich die Daten in die Datenbank eintragen. Ich habe die DB-Felder auf UNIQUE stehen, sodass keine Duplikate zulässsig sind. Wenn ich nun 2 neue Namen in die DB eintragen will, geht alles problemlos. Wenn aber ein Name schon vorhanden ist, erhalte ich die BatchUpdateExeption mit dem Hinweis 'Duplicate Key'. Das Problem ist aber, dass er den 2. Namen, der noch nicht in der DB steht, dort einträgt. Also geht doch das rollback nicht? Oder?


----------



## kleiner_held (22. Mai 2007)

Du meinst in deinem Beispiel wird *xd* eingetragen und für *xxx* kommt eine BatchUpdateException?


----------



## Guest (23. Mai 2007)

Ja, wenn xxx bereits in der DB steht und xd nicht.


----------



## Guest (24. Mai 2007)

Ich hab hier ein 2. Beispiel für Transaktionen, das bei mir irgendwie auch nicht funktioniert.


```
import java.sql.*;
     
public class TransactionPairs {

	public static void main(String args[]) {
		  
		String url = "jdbc:mysql://localhost/testTransaction";
		Connection con = null;
		Statement stmt;
		PreparedStatement updateSales;
		PreparedStatement updateTotal;
		String updateString = "update COFFEES " +
						"set SALES = ? where COF_NAME like ?";
	
		String updateStatement = "update COFFEES " +
				"set TOTAL = TOTAL + ? where COF_NAME like ?";
		String query = "select COF_NAME, SALES, TOTAL from COFFEES";
	
		try {
			Class.forName("com.mysql.jdbc.Driver");

		} catch(java.lang.ClassNotFoundException e) {
			System.err.print("ClassNotFoundException: "); 
			System.err.println(e.getMessage());
		}

		try {
	
			con = DriverManager.getConnection(url, 
									 "xxx", "xxx");
	
			updateSales = con.prepareStatement(updateString);
			updateTotal = con.prepareStatement(updateStatement);
			int [] salesForWeek = {190, 150, 60, 155, 90};
			String [] coffees = {"Colombian", "French_Roast", 
								"Espresso", "Colombian_Decaf",
								"French_Roast_Decaf"};
			int len = coffees.length;
			con.setAutoCommit(false);
			for (int i = 0; i < len; i++) {
				updateSales.setInt(1, salesForWeek[i]);
				updateSales.setString(2, coffees[i]);
				updateSales.executeUpdate();
	
				updateTotal.setInt(1, salesForWeek[i]);
				updateTotal.setString(2, coffees[i]);
				updateTotal.executeUpdate();
				con.commit();
			}
	
			con.setAutoCommit(true);
	
			updateSales.close();
			updateTotal.close();
	
			stmt = con.createStatement();							
			ResultSet rs = stmt.executeQuery(query);
				
			while (rs.next()) {
				String c = rs.getString("COF_NAME");
				int s = rs.getInt("SALES");
				int t = rs.getInt("TOTAL");
				System.out.println(c + "     " +  s + "    " + t);
			}
	
			stmt.close();
			con.close();
	
		} catch(SQLException ex) {
			System.err.println("SQLException: " + ex.getMessage());
			if (con != null) {
				try {
					System.err.print("Transaction is being ");
					System.err.println("rolled back");
					con.rollback();
				} catch(SQLException excep) {
					System.err.print("SQLException: ");
					System.err.println(excep.getMessage());
				}
			}
		}	
	}
}
```

Wenn ich anstelle von 'set TOTAL' -> 'set TOTAL1' angebe, damit eine SQL_Exception kommt, wird das erste Prepared Statement trotzdem ausgeführt ('190' wird in das Feld 'SALES' eingetragen). Es steht zwar in der Exception: Transaction is being rolled back, das erste Statement wird aber nicht zurückgenommen, das zweite wird ja sowieso nicht eingetragen, da hier ja eine Exception ausgelöst wird.

Das ist doch nicht richtig so, oder? Könnte vieleicht jemand eines der beiden Scripte ausprobieren und schauen ob's bei euch geht? 
Als DB verwende ich MySQL 5.0.22, OS Ubuntu 6.06


----------



## Guest (28. Mai 2007)

habs gelöst: der Tabellentyp 'InnoDB' muss eingestellt werden, da 'MyISAM' anscheinend keine Transaktionen ausführen kann.

Aber zu meinem eigentlichen Problem: wie kann ich verhindern, dass eine Tabelle von einem anderen Client upgedatet wird, während ich mit meinem Client ein update mache?

Ich kann natürlich einen Datensatz in die Tabelle einfügen und, sollte das Insert nicht funktionieren weil der Datensatz schon vorhanden ist, das Einfügen mittels Transaktion rückgängig machen. Aber ist das eine saubere Lösung? Ist es nicht besser die Tabelle für den Zeitpunkt des Updates exklusiv für meinen Client zu öffnen bzw für andere Clients zu sperren?


----------

