<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:georss="http://www.georss.org/georss"> <channel><title>freakcommander &#187; Klasse</title> <atom:link href="http://www.freakcommander.de/tag/klasse/feed/" rel="self" type="application/rss+xml" /><link>http://www.freakcommander.de</link> <description>Kann alles. Weiß alles. Macht alles.</description> <lastBuildDate>Mon, 30 Jan 2012 12:31:09 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Java: Erstellen großer CSV-Dateien zum Datenbank-Import</title><link>http://www.freakcommander.de/2905/computer/java/java-erstellen-grosser-csv-dateien-zum-datenbank-import/</link> <comments>http://www.freakcommander.de/2905/computer/java/java-erstellen-grosser-csv-dateien-zum-datenbank-import/#comments</comments> <pubDate>Tue, 27 Jul 2010 17:05:49 +0000</pubDate> <dc:creator>crille</dc:creator> <category><![CDATA[Java]]></category> <category><![CDATA[CSV]]></category> <category><![CDATA[Datenmenge]]></category> <category><![CDATA[Geschwindigkeit]]></category> <category><![CDATA[Klasse]]></category> <category><![CDATA[MySQL]]></category> <guid
isPermaLink="false">http://www.freakcommander.de/?p=2905</guid> <description><![CDATA[Anstatt Millionen von INSERT oder UPDATE-Befehlen an die Datenbank zu schicken, um eine Tabelle zu aktualisieren, bietet es sich bei großen Datenmengen an, eine CSV-Datei zu erstellen, welche dann über LOAD DATA INFILE mit extrem hoher Geschwindigkeit eingelesen wird. Erstellen von CSV-Dateien mit Java In Java bietet sich zum Erstellen von CSV-Dateien die Klasse Super [...]]]></description> <content:encoded><![CDATA[<p>Anstatt Millionen von INSERT oder UPDATE-Befehlen an die Datenbank zu schicken, um eine Tabelle zu aktualisieren, bietet es sich bei großen Datenmengen an, eine CSV-Datei zu erstellen, welche dann über <a
href="http://dev.mysql.com/doc/refman/5.1/de/load-data.html">LOAD DATA INFILE</a> mit extrem hoher Geschwindigkeit eingelesen wird.</p><h2>Erstellen von CSV-Dateien mit Java</h2><p>In Java bietet sich zum Erstellen von <a
href="http://de.wikipedia.org/wiki/CSV_%28Dateiformat%29">CSV-Dateien</a> die Klasse <a
href="http://supercsv.sourceforge.net/">Super CSV</a> an. Um eine Datei zu erzeugen, benötigt man als erstes einen CSVMapWriter:</p><pre class="brush: java">CsvMapWriter writer = new CsvMapWriter(new OutputStreamWriter(new FileOutputStream(&quot;C:\meineDatei.csv&quot;, true),&quot;UTF-8&quot;), CsvPreference.EXCEL_PREFERENCE);</pre><p>Anstatt mit einem OutputStreamWriter zu arbeiten, kann man auch einen FileWriter verwenden. Da man bei letzterem aber kein Encoding (&#8220;UTF-8&#8243;) angeben kann und in der Datenbank die Zeichen UTF-8-kodiert sind, müssen wir den Stream verwenden.<br
/> Interessant ist der zweite Parameter des FileOutputStream, der angibt, dass die Daten, die eingefügt werden, angehängt werden und nicht vorher gelöscht werden.<br
/> Die Einstellung CsvPreference.EXCEL_PREFERENCE legt fest, welche Trennzeichen in der CSV-Datei verwendet werden &#8211; wenn man andere Trennzeichen verwenden möchte, muss der später behandelte LOAD DATA INFILE-Befehl angepasst werden.</p><p>Als nächstes werden die Textfelder für die CSV-Datei definiert:</p><pre class="brush: java">final String[] header = new String[] { &quot;id&quot;, &quot;name&quot;, &quot;adressfeld&quot; };</pre><p>Unsere Datensätze bestehen also aus 3 Feldern.</p><p>Um die CSV-Datei mit Daten zu füllen sind folgende Zeilen notwendig:</p><pre class="brush: java">final HashMap&lt;String, ? super Object&gt; data = new HashMap&lt;String, Object&gt;();
data.put(header[0], this.id);
data.put(header[1], this.name);
this.adressfeld = this.adressfeld.replace(&quot;\\&quot;, &quot;\\\\&quot;);
data.put(header[2], this.adressfeld);</pre><p>Die Daten werden in einer HashMap gespeichert und per data.put eingefügt. Was mich etwas überraschte war, dass man das Backslash maskieren muss, ansonsten geht es in der Verarbeitung verloren. Eigenartig, weil man diese Funktionalität sicherlich problemlos in Super CSV hätte integrieren können.</p><p>Abschließend werden die Daten dem CSVMapWriter übergeben und dieser wieder geschlossen:</p><pre class="brush: java">writer.write(data, header);
writer.close();</pre><h3>Und mehrere Datensätze?</h3><p>Obiges Beispiel ist für einen einzelnen Datensatz, der in einer CSV-Datei gespeichert wird. Wenn man mehrere Datensätze einfügen möchte, müssen sämtliche Schritte (Anlegen eines CSVMapWriter, Daten vorbereiten, Daten einfügen) für jeden Datensatz wiederholt werden.</p><p>Den CSVMapWriter 1x anzulegen und am Ende per .close zu schließen, funktioniert nicht &#8211; meine Versuche mit der Super CSV-Klasse haben gezeigt, dass dann Datensätze verloren gehen bzw. abgeschnitten werden.</p><h2>Einlesen der CSV mit LOAD DATA INFILE</h2><p>Um die so erstellte Datei mit <a
href="http://dev.mysql.com/doc/refman/5.1/de/load-data.html">LOAD DATA INFILE</a> in die Datenbank zu importieren, muss folgender Befehl eingetragen werden:</p><pre class="brush: sql">LOAD DATA [LOCAL] INFILE &#039;C:\\meineDatei.csv&#039;
[REPLACE]
INTO TABLE meine_tabelle
FIELDS TERMINATED BY &#039;,&#039;
OPTIONALLY ENCLOSED BY &#039;\&quot;&#039;
LINES TERMINATED BY &#039;\n&#039;
(id, name, adressfeld);</pre><p>Vorab: Der Befehl wird so nicht funktionieren, da die Bestandteile innerhalb der eckigen Klammern optional sind:</p><ul><li>LOCAL wird dann angegeben, wenn die CSV-Datei vom Clientprogramm auf dem Clienthost gelesen und an den MySQL-Server geschickt wird. Fehlt LOCAL, liegt die Datei auf dem MySQL-Server</li><li>REPLACE nutzt man, wenn die eingelesene Datei nicht nur neue Datensätze importiert, sondern auch bereits vorhandene überschreibt &#8211; also UPDATES durchführt</li></ul><p>Alle/Weitere Optionen zu dem LOAD DATA INFILE-Befehl gibt es im <a
href="http://dev.mysql.com/doc/refman/5.1/de/load-data.html">MySQL-Handbuch</a>.</p><h2>Performancevergleich</h2><p><a
href="http://www.freakcommander.de/2915/computer/java/performancevergleich-mysql-load-data-infile-vs-einzelne-sql-statements/">Ein Performancevergleich ist hier</a> zu finden.</p><p>Auch interessant:<ol><li><a
href='http://www.freakcommander.de/2915/computer/java/performancevergleich-mysql-load-data-infile-vs-einzelne-sql-statements/' rel='bookmark' title='Performancevergleich: Enzelne SQL-Statements vs. MySQL LOAD DATA INFILE vs. Prepared Statements'>Performancevergleich: Enzelne SQL-Statements vs. MySQL LOAD DATA INFILE vs. Prepared Statements</a></li><li><a
href='http://www.freakcommander.de/3289/computer/mysql-import-und-pdflush/' rel='bookmark' title='MySQL-Import und pdflush'>MySQL-Import und pdflush</a></li><li><a
href='http://www.freakcommander.de/2892/computer/java/java-falscher-resultset-getter-verursacht-memory-leak/' rel='bookmark' title='Java: Falscher ResultSet.getter verursacht Memory Leak'>Java: Falscher ResultSet.getter verursacht Memory Leak</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.freakcommander.de/2905/computer/java/java-erstellen-grosser-csv-dateien-zum-datenbank-import/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Java: Falscher ResultSet.getter verursacht Memory Leak</title><link>http://www.freakcommander.de/2892/computer/java/java-falscher-resultset-getter-verursacht-memory-leak/</link> <comments>http://www.freakcommander.de/2892/computer/java/java-falscher-resultset-getter-verursacht-memory-leak/#comments</comments> <pubDate>Thu, 22 Jul 2010 22:43:48 +0000</pubDate> <dc:creator>crille</dc:creator> <category><![CDATA[Java]]></category> <category><![CDATA[eclipse]]></category> <category><![CDATA[IDE]]></category> <category><![CDATA[Klasse]]></category> <category><![CDATA[Memory Leak]]></category> <category><![CDATA[notebook]]></category> <category><![CDATA[Speicherleck]]></category> <guid
isPermaLink="false">http://www.freakcommander.de/?p=2892</guid> <description><![CDATA[Ich bin ein Gänseblümchen im Sonnenschein und durch meine Blüte fließt die Sonne in mich rein Ich bin ein Gänseblümchen und mir wird ganz warm ich könnt die ganze Welt und dann mich selbst umarmen. Ich bin ein Gänseblümchen ohne Aggression Wut, Ärger &#8211; was bringt das schon? Durchatmen, Freakcommander! Momentan gehst du durch die [...]]]></description> <content:encoded><![CDATA[<blockquote><p>Ich bin ein Gänseblümchen im Sonnenschein<br
/> und durch meine Blüte fließt die Sonne in mich rein<br
/> Ich bin ein Gänseblümchen und mir wird ganz warm<br
/> ich könnt die ganze Welt und dann mich selbst umarmen.<br
/> Ich bin ein Gänseblümchen ohne Aggression<br
/> Wut, Ärger &#8211; was bringt das schon?</p></blockquote><p><a
href="http://www.freakcommander.de/wp-content/uploads/2010/07/java.png"><img
class="alignright size-full wp-image-2893" title="Java" src="http://www.freakcommander.de/wp-content/uploads/2010/07/java.png" alt="java Java: Falscher ResultSet.getter verursacht Memory Leak" width="100" height="189" /></a>Durchatmen, Freakcommander! <a
href="http://www.freakcommander.de/2888/computer/java/liefert-mich-ein-sofort/">Momentan</a> gehst du durch die harte Schule einer dir immer noch relativ unbekannten Sprache. Da fällt man mal hin und muss wieder aufstehen, ansonsten wird das nichts.</p><h2>Die Vorgeschichte</h2><p>Aber man muss zugegeben, dass der heutige Fehler nicht ganz so leicht zu finden war:</p><ul><li>Ich habe die <em>Klasse Host</em> umgeschrieben. Vorher hat sie einfach per Methode <em>.toDB()</em> ihre Attribute in die Datenbank geschrieben. Nach dem Umschreiben soll sich die Klasse nur dann in die Datenbank schreiben, wenn sie entweder noch nicht in der DB ist oder ein Attribut sich verändert hat und die DB somit aktualisiert werden muss.<br
/> <strong>Grund für das Umschreiben</strong>: Innerhalb eines Programmaufrufs werden hintereinander Millionen Instanzen erzeugt und geguckt, ob sie sich verändert haben. Wenn das der Fall ist, muss der Eintrag auch in der DB aktualisiert werden. Da aber UPDATE oder INSERT-Befehle teurer sind und nur ein sehr kleiner Teil der Millionen Einträge sich auch wirklich verändern, macht eine Überprüfung, ob ein UPDATE oder INSERT erfolgen muss, definitiv Sinn.</li><li>Wie bin ich vorgegangen?<ol><li>Eine Methode <em>.getFromDB</em>, die die &#8220;alten&#8221; Daten aus der DB liest</li><li>Eine Methode <em>.equals</em>, die eine Instanz des Objekts mit einer anderen Instanz vergleicht und TRUE zurück gibt, wenn die Attribute gleich sind</li><li>Die alte Methode <em>.toDB()</em> dementsprechend angepasst. Einziges Manko war, dass ich in dieser Methode eine Instanz des eigenen Objektes anlegen musste &#8211; diese angelegte Instanz des eigenen Objekts bekommt mittels <em>.getFromDB</em>() die alten Daten verabreicht und wird dann mit <em>.equals</em> verglichen.</li></ol></li><li>Das Anpassen des Quellcodes war schnell erledigt. Das große Problem war nun allerdings, dass die Anwendung jetzt ein Speicherleck hatte. Der benutzte Speicher wuchs und wuchs und endete schließlich in einer Exception.</li><li>Anstatt mich direkt mit dem Code zu beschäftigen und durch auskommentieren und System.out.println-Meldungen dem Problem auf die Schliche zu kommen, beschäftigte ich mich mit der Literaturrecherche zu Memory Leaks, Speicherlecks, Garbage Collector und <a
href="http://www.eclipse.org/mat/">Eclipse Memory Analyzern</a> oder <a
href="https://visualvm.dev.java.net/">VisualVM</a>, einem Tool, das eine Realtime Heap-Usage Analyse verspricht.</li><li>Beim <a
href="http://www.eclipse.org/mat/">Eclipse Memory Analyzer</a> funktionierte das Generieren der .hprof-Datei nicht, die man braucht, um den Analyzer einzusetzen.</li><li>Und die <a
href="https://visualvm.dev.java.net/">VisualVM</a> ist sicherlich ein tolles Tool, aber mein uralt Notebook, welches zu wenig RAM und CPU-Leistung hat, arbeitet so langsam mit der VisualVM, dass beim Start schon der Java-Prozess mit meiner Anwendung, die den Memory Leak hat, verschwunden war. So konnte ich leider keinen Memory-Monitorer einschalten oder den Heap dumpen.. No chance.</li><li>Und nach stundenlanger Recherche und antesten etlicher Tools, um einem Speicherleck in Java zu begegnen, bekam ich schon wieder dieses Gefühl: Wie kann es sein, dass eine Programmiersprache, die millionenfach eingesetzt wird, kein einfach zu installierendes Tool hat, welches automatisch die Daten sammelt, um einem dann vorzuschlagen, welche Klassen oder Methoden man sich nochmal genauer anschauen sollte, um das Speicherleck zu schließen. Wenn in Foren ein Programmierer Probleme mit einem Tool hat, wird ihm einfach ein anderes Tool empfohlen, welches ähnlich ist und bei ihm laufen sollte. Na ja, ich hatte keinen Bock mehr mich durch irgendwelche Seiten und Tools zu klicken.</li><li>Das Problem war an irgendeiner Stelle in meinem Code, also finde ich einfach die Stelle und behebe das Problem.</li><li>Nachdem ich sichergestellt hatte, dass sämtliche Statements, PreparedStatements, ResultSets und Connections nach getaner Arbeit auch wieder geschlossen wurden und das Problem nach wie vor auftrat, kümmerte ich mich um die Instanz, die innerhalb der <em>.toDB()-</em>Methode erzeugt wird. Vielleicht werden ganz viele Instanzen erzeugt, die der Garbage Collector aus irgendwelchen Gründen nicht löschen kann. Aber auch daran lag es nicht.</li><li>Schließlich ging ich dazu über ganze Funktionen und Programmteile auszukommentieren. Folgender Programmcode verursachte das Speicherleck</li></ul><h2>Der Fehler</h2><p>In der Methode <em>.getFromDB</em>(), die einfach nur per DB-Query Daten einliest, gab es folgende 3 Zeilen Code, die Grund für das Memory Leak waren:</p><pre class="brush: java">this.setM_swap(rs.getInt(16));
this.setD_total(rs.getInt(17));
this.setD_free(rs.getInt(18));</pre><p>Aus dem ResultSet der DB-Abfrage werden per Setter-Methoden die Attribute gesetzt. Das Problem? Da die Java-Datentypen und die DB-Felder für m_swap, d_total und d_free mit Integer bzw. int nicht ausreichend lang waren, habe ich sie nachträglich auf long bzw. Bigint (DB) geändert. Dabei habe ich auch die setter wie <em>setM_swap()</em> so geändert, dass sie als Parameter long erwarten.  Normalerweise schreit ja gleich das IDE los, wenn ein Typ nicht passt, aber Java findet (und in diesem Fall völlig zu Recht), dass Methoden, die long als Parameter erwarten, auch mit int gefüttert werden können und hat deswegen nicht gemeckert.</p><p>Das Memory Leak kann ich mir nur so erklären, dass mit rs.getInt auf eine Spalte zugegriffen wurde, welche in der Datenbank als BigInteger definiert ist.</p><p>Richtigerweise hätte man mit <em>rs.getLong()</em> arbeiten müssen, dann gibt es auch keine Probleme mit dem Speicher. ;)</p><p>Auch interessant:<ol><li><a
href='http://www.freakcommander.de/2905/computer/java/java-erstellen-grosser-csv-dateien-zum-datenbank-import/' rel='bookmark' title='Java: Erstellen großer CSV-Dateien zum Datenbank-Import'>Java: Erstellen großer CSV-Dateien zum Datenbank-Import</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.freakcommander.de/2892/computer/java/java-falscher-resultset-getter-verursacht-memory-leak/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Frage an die PHP OOP-Experten: Typisierung von Klassenattributen</title><link>http://www.freakcommander.de/2585/computer/frage-an-die-php-oop-experten-attribut-als-instanz-einer-anderen-klasse/</link> <comments>http://www.freakcommander.de/2585/computer/frage-an-die-php-oop-experten-attribut-als-instanz-einer-anderen-klasse/#comments</comments> <pubDate>Sun, 31 Jan 2010 12:28:01 +0000</pubDate> <dc:creator>crille</dc:creator> <category><![CDATA[Computer]]></category> <category><![CDATA[IDE]]></category> <category><![CDATA[Klasse]]></category> <category><![CDATA[NetBeans]]></category> <category><![CDATA[oop]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[Typisierung]]></category> <guid
isPermaLink="false">http://www.freakcommander.de/?p=2585</guid> <description><![CDATA[Die Überschrift hört sich komplizierter an als sie ist. Veranschaulichen wir das Problem mal an einem Beispiel. Ich habe eine Klasse Erde, Mars, Venus etc. Diese Klassen sehen so aus: class Erde{ var $name; var $groesse; var $avgEntfernungZurSonne; var $bevoelkerung; ... function __construct(...) { ... } function eineFkt(...) { ... } } Nun habe ich [...]]]></description> <content:encoded><![CDATA[<p>Die Überschrift hört sich komplizierter an als sie ist. Veranschaulichen wir das Problem mal an einem Beispiel. Ich habe eine Klasse Erde, Mars, Venus etc. Diese Klassen sehen so aus:</p><pre class="brush: php">
class Erde{
var $name;
var $groesse;
var $avgEntfernungZurSonne;
var $bevoelkerung;
...
function __construct(...) {
...
}
function eineFkt(...) {
...
}
}
</pre><p>Nun habe ich noch eine Klasse Sonnensystem. Und diese Klasse soll als Attribute die einzelnen Planeten beinhalten, also:</p><pre class="brush: php">
class Sonnensystem{
var $erde;
var $venus;
var $mars;
...
function __construct($erde, $venus,...) {
$this-&gt;erde = $erde;
$this-&gt;venus = $venus;
...
}
function gebReihenfolgeDerPlanetenAbSonne() {
$arrayEntfernung =
($this-&gt;erde-&gt;name =&gt; $this-&gt;erde-&gt;avgEntfernungZurSonne,
$this-&gt;venus-&gt;name =&gt; $this-&gt;venus-&gt;avgEntfernungZurSonne,
...) ;
...
}
}
</pre><p>Das Problem ist jetzt, dass NetBeans in Sonnensystem::gebReihenfolgeDerPlanetenAbSonne() nicht weiß, von welchem Typ die Variablen $this-&gt;erde und $this-&gt;venus sind. Denn die werden ja nirgendwo genauer in der Klasse Sonnensystem spezifiziert. Folglich kann mir NetBeans auch nicht vorschlagen, welche Attribute/Methoden ich bei $this-&gt;erde aufrufen möchte. Das finde ich allerdings extrem schwach, denn genau deswegen setze ich ja ein IDE ein.</p><p>Mir sind zwar bei Recherchen Schlagwörter wie z.B. <a
href="http://php.net/manual/de/language.oop5.typehinting.php">Typehinting</a> über den Weg gelaufen, aber das könnte ich ja nur im Konstruktor einsetzen und dann wäre nur dort sicher gestellt, dass $erde eine Instanz der Klasse Erde ist und hilft folglich nicht wirklich weiter.</p><p>Also, mal konkretisiert: Wie kann ich in PHP festlegen, dass ein Attribut von einem bestimmten Typ ist. So zumindest nicht:</p><pre class="brush: php">
class Sonnensystem{
var Erde $erde;
...
</pre><p><strong>Update + Update + Update + Update</strong><br
/> Damit die IDE NetBeans weiß, von welchem Typ ein Klassen-Attribut ist, muss man PHPDoc-Kommentare verwenden:</p><pre class="brush: php">
class Sonnensystem{
/**
* @var Erde
**/
private $erde;
...
</pre><p>Felix schlug dann noch vor, das Attribut auf &#8220;private&#8221; zu setzen und dann im Konstruktor mit TypeHinting zu arbeiten. Das funktionierte bei mir in NetBeans allerdings nicht. Da scheint es also Unterschiede zwischen den IDEs zu geben.</p><p>Auch interessant:<ol><li><a
href='http://www.freakcommander.de/2603/computer/php-foreach-variablen-typ-definieren/' rel='bookmark' title='PHP: Foreach Variablen Typ definieren'>PHP: Foreach Variablen Typ definieren</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.freakcommander.de/2585/computer/frage-an-die-php-oop-experten-attribut-als-instanz-einer-anderen-klasse/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
