NAS

Java Architekturentwurf Importer

_pa

Cadet 3rd Year
Registriert
Dez. 2007
Beiträge
44
Hallo Zusammen,

ich soll hier ein Tool programmieren, dass mehrere Dateien importiert und in eine Datenbank schreibt.
Die Dateien sind grundsätzlich so aufgebaut

Code:
Datei1-Anfang
 DatensatzA
 DatensatzB
 DatensatzB
 .......
 DatensatzC
Datei2-Ende

Ich hab mir also eine AbstractDatensatz gebaut von der DatensatzA, DatensatzB... erben
Im Prinzip sind es reine Entities, sprich die Klassen bestehen nur aus Membern und zugehörigen gettern/settern.

Das importieren funktioniert auch alles wunderbar, beim einlesen der der Daten erstell ich die Datensatz Objekte und schmeiss sie in eine ArrayList<AbstractDatensatz>.

Das Konzept, welches ich mir zur Speicherung ausgedacht habe funktioniert nur leider nicht wie ich gedacht habe.
Idee war: Ich erstell ein Interface für meine Speicherkomponente (Persistor)
Code:
public interface IPersistor
{
	void persist(AbstractSatz satz) throws Exception;
}

Der Persistor weiß jetzt wie er mit den entsprechenden Datensatz Objekten umzugehen hat (DatensatzA in SQLite, DatensatzB in MySQL, DatensatzC in Textdatei usw).
Meine Idee war das durch überladen der persist Methode zu machen:

Code:
public class Persistor implements IPersistor
{
 public void persist(AbstractDatensatz satz) throws Exception 
 { //... }

 public void persist(DatensatzA satz) throws Exception 
 { //... }

 public void persist(DatensatzB satz) throws Exception 
 { //... }
 ....
}

Iteriere ich jetzt aber über die ArrayList<AbstractDatensatz> wird immer nur die public void persist(AbstractDatensatz satz) aufgerufen - was im Nachhinein betrachtet natürlich auch vollkommen richtig ist, mir jetzt aber nicht hilft ;)

Wie löse ich jetzt am elegantesten das Problem die Datensätze zu speichern?
Was ich auf keinen Fall will: Die DatensatzA, DatensatzB sollen nicht wissen wie sie gespeichert werden.

Ideen die ich hatte:
1) Ich caste (über Reflection oder wie auch immer) entweder beim iterieren über ArrayList<AbstractDatensatz> die Objekte zu DatensatzA, DatensatzB...oder ich mach das in der public void persist(AbstractDatensatz satz) in der konkretem Implementierung von Persistor.
Dann klappts auch wieder mit der Überladung, aber was mach ich wenn ein unbekanntes Objekt kommt? Dann knallts zur Laufzeit...

2) Ich speicher mir die DatensatzA, DatensatzB... Objekte direkt in ArrayListen mit dem richtigen Typ. Dann funktioniert das mit der Überladung wieder.
Nachteil: Ich hab N-ArrayListen und N-Foreach schleifen :( Sehr unschöne Lösung; muss viel angepasst werden wenn es neue Datensatz Typen gibt.

3) Ich verpasse der AbstractDatensatz eine public abstract void persist(IPersistor p); Methode und programmier die in den konkreten Implementationen so aus:
Code:
        @Override
	public void persist(IPersistor p)
	{
		try
		{
			p.persist(this);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
So hab ich zwar die Speicherung im Datensatz, aber trotzdem lose gekoppelt, weil die Implementierung im Persistor ist. Ist aber irgendwie Brainfuck das sich das Objekt selber an ein Objekt übergibt was ein Teil von ihm ist :freaky:

Vielleicht fällt euch ja nen guter Weg ein.
Danke schonmal im vorraus :)
 
_pa schrieb:
Das importieren funktioniert auch alles wunderbar, beim einlesen der der Daten erstell ich die Datensatz Objekte und schmeiss sie in eine ArrayList<AbstractDatensatz>

Weißt Du beim Einlesen, ob DatensatzA, DatensatzB etc. eingelesen werden?
 
also ich für meinen Teil würde in die Klasse AbstractDatensatz protected Referenzen zur Datenbank (oder zu dem Objekt, das für die Persistierung zuständig ist) reingeben und diese das IPersistor-Interface implementieren lassen, ohne die Rümpfe anzugeben. Das brauchts ja bei ner abstrakten Klasse nicht. Dann sollten die konkreten Datensätze wie bisher von dieser Klasse erben und müssen die Methode persist() implementieren.

Ist für meinen Geschmack mit Abstand die sauberste Methode. Dadurch brauchst du keine Typbestimmung über Reflection, kein instanceof und keine Casts. Außerdem kannst du nach Belieben neue Datensatztypen hinzufügen.

Wenn du die Klasse Persistor wirklich behalten möchtest, bleibt dir wohl nur die Möglichkeit übrig, in einer Methode zu prüfen, ob IPersistor nun DatenSatzA oder DatensatzB etc. ist. Dann könntest du für jeden Typ eine eigene Methode aufrufen und die Dinger persistieren lassen. Ist aber nicht so sauber, wenn du mich fragst.

Die abstrakte Superklasse könnte außerdem über eine Datensatz-Factory die Verantwortung übernehmen, zu bestimmen, was für ein Datensatztyp instanziiert werden soll, je nach Eingabe.

Dann hast du die einzelnen Klassen sauber entkoppelt und kannst diese Factory dann gleich auch noch als Facade, d.h. als Single Point of Entry für andere Klassen verwenden.
 
Zuletzt bearbeitet:
Bin mir ehrlich gesagt nicht sicher, aber würde es nicht reichen wenn du die "public void persist(AbstractDatensatz satz) throws Exception" Methode rausnehmen würdest?! Oder ganz nach hinten stellen?

Es werden doch eigentlich die Methoden von oben durchgegangen und nach der ersten passigen gesucht?! Oder erinner ich mich da grad falsch?!
 
Ich würd einen Wrapper schreiben der aus der Wurscht ein XMl macht.
JAXB Object Transformierung
JPA zum persistieren
Fertig
 
soares schrieb:
Weißt Du beim Einlesen, ob DatensatzA, DatensatzB etc. eingelesen werden?

Nein, also ja :) Also A ist immer dabei, aber B ist variable, genauso wie C,D,E,F,G,H.....

erebos schrieb:
Bin mir ehrlich gesagt nicht sicher, aber würde es nicht reichen wenn du die "public void persist(AbstractDatensatz satz) throws Exception" Methode rausnehmen würdest?! Oder ganz nach hinten stellen?

Es werden doch eigentlich die Methoden von oben durchgegangen und nach der ersten passigen gesucht?! Oder erinner ich mich da grad falsch?!

Da erinnerst du die halb falsch :)
Es wird generell nach der passensten gesucht, die Reihefolge wie sie im Code auftauchen ist aber vollkommen egal.

Funart schrieb:
Ich würd einen Wrapper schreiben der aus der Wurscht ein XMl macht.
JAXB Object Transformierung
JPA zum persistieren
Fertig

Das ist für das kleine Tool vielleicht nen bischen mit Kanon auf Spatzen geschossen, könnte aber prinzipiell gehen. Allerdings hat hier keiner Erfahrung mit JAXB und/oder JPA, deswegen würd ichs aufgrund des Aufwands/Zeit mal als Lösung (vorerst) ausschliessen.

Maekloev schrieb:
also ich für meinen Teil würde in die Klasse AbstractDatensatz protected Referenzen zur Datenbank (oder zu dem Objekt, das für die Persistierung zuständig ist) reingeben und diese das IPersistor-Interface implementieren lassen, ohne die Rümpfe anzugeben. Das brauchts ja bei ner abstrakten Klasse nicht. Dann sollten die konkreten Datensätze wie bisher von dieser Klasse erben und müssen die Methode persist() implementieren.

Ist für meinen Geschmack mit Abstand die sauberste Methode. Dadurch brauchst du keine Typbestimmung über Reflection, kein instanceof und keine Casts. Außerdem kannst du nach Belieben neue Datensatztypen hinzufügen.

[...]

Die abstrakte Superklasse könnte außerdem über eine Datensatz-Factory die Verantwortung übernehmen, zu bestimmen, was für ein Datensatztyp instanziiert werden soll, je nach Eingabe.

Dann hast du die einzelnen Klassen sauber entkoppelt und kannst diese Factory dann gleich auch noch als Facade, d.h. als Single Point of Entry für andere Klassen verwenden.

Das wäre ja dann quasi der Lösungsansatz #3
Halt ich bisher auch für am saubersten.
Das mit der Factory/Facade hört sich auch gut an. Werd ich mir mal ansehen.

Maekloev schrieb:
Wenn du die Klasse Persistor wirklich behalten möchtest, bleibt dir wohl nur die Möglichkeit übrig, in einer Methode zu prüfen, ob IPersistor nun DatenSatzA oder DatensatzB etc. ist. Dann könntest du für jeden Typ eine eigene Methode aufrufen und die Dinger persistieren lassen. Ist aber nicht so sauber, wenn du mich fragst.

Entweder versteh ich grade nicht was du meinst, oder du hast was durcheinander geschmissen, aber eine Klasse Datensatz implementiert nie das IPersistor Interface. In meinem Lösungsvorschlag #3 bekommt so ein Datensatz Objekt nur eine Instanz einer Persistor Klasse die IPersistor implementiert übergeben.
Aber ist auch egal :)
 
Du wirst auf jeden Fall eine Fallunterscheidung machen müssen, damit der richtige persist-Code aufgerufen werden kann. Du hast bereits jetzt eine solche Fallunterscheidung im Parser für die Datei, immerhin legt er ja die entsprechend richtigen Objekte an.

Was spricht dagegen als Ergebnis des Parsens anstatt einer ArrayList<AbstractSatz >, für jede Datensatzart eine eigene ArrayList abzulegen, also ArrayList<DatensatzA>, ArrayList<Datensatzb> usw.. Ich schlage allerdings vor, das Ergebnis des Parsens in ein eigenes Datenbean zusammenzufassen, das ist leichter zu erweitern und deine Parameterlisten werden nicht immer länger.

So iterierst du eben über jedes Array einzeln, da der Typ bekannt ist wird von Java auch die entsprechend korrekte persist Methode aufgerufen.
 
Das hat nix mit Kanone auf spatzen schiesen zu tun.
Es ist einfach die schnellste Lösung. Du brauchst keinen Tropfen Logik sondern nur ein wenig string replace action.
 
was du ja eigentlich haben möchtest wäre einfach eine unterscheidung beim persistieren.
wäre wohl die schnellste lösung, ohne gross was umzubauen.

PHP:
	public void persist(AbstractDatensatz abstractDatensatz) throws Exception {
		if (abstractDatensatz instanceof DatensatzA) {
			persist((DatensatzA) abstractDatensatz);
		} else if (abstractDatensatz instanceof DatensatzB) {
			persist((DatensatzB) abstractDatensatz);
		} else {
			throw new IllegalArgumentException("Unsupported Datensatz");
		}
	}

	protected void persist(DatensatzA satz) throws Exception {
		// do something
	}

	protected void persist(DatensatzB satz) throws Exception {
		// do something
	}

gruss
krizi
 
Wobei ich hier dann evtl vom Überladen abraten würde, und direkt
persistMySQL
persistDB2
persistXML
persistNirvana
usw...

nehmen würde. Und von "throws Exception" halte ich nicht wirklich viel... wenn dann schon lieber die konkreten Exceptions angeben... wobei ich RuntimeExceptions bevorzuge...
 
hallo _pa,
ich will dich ja nicht arbeitslos machen...
aber was spricht gegen die vorhadnen tools!?

Kettle von Pentaho wäre hier mindesten zu nennen,
was du bauen solst nennt sich im übrigen ETL-Tools.
 
Zurück
Oben