# Datenbankverbindung zu langsam beim Insert?



## MrTobi (12. Dez 2011)

Hallo liebes Forum,
ich habe mal eine Frage ich importiere von 20 - 60 verschiedenen Exceldatein Daten (im Durchschnitt ca. 4500
Zeilen pro Datei)

Mir fällt auf, dass mein Programm alle Daten richtig ließt aber beim schreiben in die DB Daten verloren gehen.
Nun wollte ich euch fragen ob ihr eine Idee habt wie man dies umgehen kann da ich immer das Gefühl habe wenn ich debugge (und somit alles künstlich verlangsamt wird) werden alle Daten pro Datei korrekt importiert.

Nun wollte ich wissen wie ich mein Programm so ändern muss das es eben alle Daten richtig inserted.

Dazu folgende Idee: eine künstliche Pause nach x inserts. (Ich habs mit Thread.Sleep(2000) versucht, aber irgendwie liegt das ganze Programm dann still und zwar länger wie 2 Millisekunden. Und will einfach nix mehr machen.

Generell bin ich immer dankbar für Verbesserungsvorschläge falls euch irgendwas nicht gefällt in den Klassen.

Abfolge meines Programmes:
1.alle daten aus aktuellem Sheet lesen und in Liste schreiben (alle Daten sind vorhanden)
2.alle Objekte in der Liste in die DB schreiben (hier gehen die Daten verlohren)
3. Nächste Datei vornehmen


Hier meine Datenbankklasse:


```
package IOHandler;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import Logic.DataHolder;
/**
 *
 * Handles the connection to the database access 
 *
 * Important: Singelton Pattern is used to make sure only one connection 
 * 
 * to the database is established. 
 */
public class DataBaseConnector {

	/**
	 * local Variable
	 */
	private static DataBaseConnector instance;
	private Connection myConn;
	
	
	public Connection getConnection() {
		return myConn;
	}

	/**
	 * Constructor for the class
	 * 
	 * @throws ClassNotFoundException 
	 * 
	 * @throws SQLException 
	 */
	private DataBaseConnector() {
		
		PathReader myPathReader = new PathReader();
		String idDatabasepath = myPathReader.getXmlElements("path.xml", "Database").get(0);
		
		try {
			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		} catch (ClassNotFoundException e) {
			
			e.printStackTrace();
		}
		String sUsr = "";
		String sPw = "";
		String sDbUrl = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ="
				+ idDatabasepath +";READONLY=false;}";

			try {
				myConn = DriverManager.getConnection(sDbUrl, sUsr, sPw);
			} catch (SQLException e) {
				
				e.printStackTrace();
			}
	}

	/**
	 * Make sure singleton is realized
	 * 
	 * @return the connection
	 * 
	 */
	public synchronized static DataBaseConnector getInstance(){
		if (instance == null) {
			instance = new DataBaseConnector();
		}
		return instance;
	}
	
	/**
	 * 
	 * @param currentHolder
	 * 
	 * @throws SQLException
	 */
	public void insertDataHolder(DataHolder currentHolder) throws SQLException{
		
		String query = "INSERT INTO tbl_Excelalle("+"MLFB,"+"Geraet,"+"GeraetAS,"+"ArtikelNr,"+"ASZahl,"+"EinfuegeDatum)"+"VALUES(?,?,?,?,?,?)";
		
				PreparedStatement prstmt = myConn.prepareStatement(query);

				if(currentHolder.getsMLFB()!=null){
					prstmt.setString(1,currentHolder.getsMLFB());
				}else{
					prstmt.setString(1,null);
				}
				
				if(currentHolder.getsGeraet()!=null){
					prstmt.setString(2,currentHolder.getsGeraet());
				}else{
					prstmt.setString(2, null);
				}
				
				if(currentHolder.getiGeraetAS()!=null){
					prstmt.setString(3, currentHolder.getiGeraetAS());
				}else{
					prstmt.setString(3, null);
				}
				
				if(currentHolder.getsArtikelNr()!=null){
					prstmt.setString(4, currentHolder.getsArtikelNr());
				}else{
					prstmt.setString(4, null);
				}
				
				if(currentHolder.getiAS()!=null){
					prstmt.setString(5, currentHolder.getiAS());
				}else{
					prstmt.setString(5, null);
				}
				
				prstmt.setDate(6, new java.sql.Date(System.currentTimeMillis()));
				
				prstmt.executeUpdate();
				
	}
	
	/**
	 * 
	 * @throws SQLException
	 */
	public void closeConn() throws SQLException{
		myConn.close();
	}
}
```

Aufgerufen wird die insertDataHolder() in einer for each Schleife:


```
for (DataHolder inHolder : insertList) {
								try {
									/**
									 * Insert the DataHolder in the database
									 */
									myConnection.insertDataHolder(inHolder);
									isfullimported = true;
								} catch (Exception e) {
									System.out.println("Fehler bei insert von: " +inHolder.toString());
									failImports.add(inHolder);
									isfullimported = false;
								}
							}
```

Vielen Dank schonmal an euch, ich hoffe jemand hat eine zündene Idee 

Grüße
MrTobi


----------



## turtle (12. Dez 2011)

Ich vermute, dass keine Fehlermeldungen kommen, weil Du die sonst erwähnt hättest, oder?

Sonst sehe ich keine grösseren Probleme im Code. Deine Connection läuft natürlich im Auto-Commit-Mode und bedeutet, dass jedes SQL-Statement einzeln committed oder rollbacked wird.

Dieses kann eigentlich nicht das Problem verursachen, um es aber definitiv auszuschliessen, würde ich das mal ändern und alle Insert-Befehle in EINER Transaktion laufen lassen, die Du dann zum Abschluss committest. Dieses sollte auch die Performance etwas erhöhen, obwohl ich da bei Access meine Zweifel habe ;-)

Auf alle Fälle ist es keine Option, das eigene Programm künstlich zu verlangsamen. Dein Hinweis, dass das Programm bei einem Sleep länger braucht als angegeben, ist ein Hinweis, dass die DB anscheinend mehr Locks hält als vermutet, daher mein Vorschlag mit den Transaktionen.

Sonst habe ich auch keinen richtigen Vorschlag, ausser mehr Informationen in der Schleife auszugeben.


----------



## maki (12. Dez 2011)

Ein paar grundsätzliche Anmerkungen:
- die JDBC/ODBC Brücke war nie für den produktiven Einsatz gedacht sondern nur als Demo
- Access ist keine "richtige" Datenbank
- executeUpdate sollte die Anzahl der eingefügten Datensätze zurückgeben, damit könnte der Code feststellen ob auch alles inserted wurde


----------



## MrTobi (12. Dez 2011)

Ersteinmal danke für die schnellen Antworten.

Das Access keine richtige DB ist, ist mir bewusst leider ist dies eine Anforderung und ich kann daran nichts rütteln  

Das die JDBC/ODBC Brücke nie für den produktiven Einsatz gedacht war, sondern nur als Demo wusste ich erlich gesagt nicht. Gibt es denn eine "richtige" Alternative für die Anbindung an eine Access DB?

Ich würd das gern testen was turtle vorgeschlagen hat und alle Inserts in einer Transaktion durchführen, erlich gesagt weiß ich nur nicht so richtig wie ich das implementieren soll.
Ich meine klar die komplette Liste anstatt jedes Objekt einzeln übergeben aber wie bastelt man dann das Update zusammen?

Also in diesem Fall:


```
public void insertDataHolder(List<DataHolder> insertList) throws SQLException{

...

}
```

Ich hab auch nochmal getestet also es sieht wirklich danach aus als kommt er eben ab dem x-ten Datensatz nicht mehr hinterher und lässt sie praktisch fallen bis er wieder "Luft" hat und insertet fleißig weiter , es sind im Schnitt 700-800 Datensätze die ich verliere.

Grüße
MrTobi


----------



## maki (12. Dez 2011)

Versuche mal nicht jeden Datensatz einzeln einzufügen, probier es mal mit Batches:
http://docs.oracle.com/javase/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame6.html

Achte dabei auf die Batchgröße, d.h. anstatt jeden Datensatz einzufügen solltest du auch nicht versuchen alles auf einmal einzufügen.

Bezüglich JDBC/ODBC Treiber, es gibt einige (imho kommerzielle)  JDBC Treiber, einfach mal suchen.


----------



## turtle (12. Dez 2011)

Nach dem Verbindungsaufbau schaltest Du autoCommit aus:

```
con.setAutoCommit(false);
```
Dadurch wird auch die erste Transaktion (oder nach jedem weiteren commit eine weitere Transaktion,gestartet.

Und nach einer geeigneten Anzahl von Inserts committest Du die Transaktion via

```
con.commit();
```

Wenn ein Fehler auftritt kannst Du natürlich auch 

```
con.rollback();
```
ausführen.


----------



## MrTobi (12. Dez 2011)

Super! Vielen Dank
ich habe nun den Code geändert und es funktioniert. Witzigerweise sogar ohne "Zwischencommit" er fügt nun alle Zeilen ein selbst wenn es 4000 auf einmal sind. Frag mich echt wieso der das mit dem einzelcommit nicht gepackt hat.

Danke an alle!

Grüße
MrTobi


----------

