Gut und Böse
Diese Seite soll eine lose Sammlung von Tips sein, was man wie mit SRJRCFrames macht oder was man besser nicht machen sollte.
Mit die wichtigsten Gründe, SRJRCFrames einzusetzen ist die Flexibilität und Erweiterbarkeit. Dazu passt es natürlich überhaupt nicht, wenn Fabrikklassen mit ihren öffentlich statischen Methoden aus einer tollen Anwendung einen unflexiblen Moloch machen.
Deshalb ist der Dienste-Verwalter ApplicationServiceProvider
die EINZIGE Klasse, auf die statisch zugegriffen werden darf.
Die wichtigsten Anwendungsdienste werden bereits beim Start aus den Konfigurationsdaten erzeugt und registriert. Meist werden Dienste unter einer Schnittstellenklasse registriert, die sie implementieren.
Beispiel:
Um internationalisierte Texte ausgeben zu können, braucht es eine MessageSource. Diese kann wie folgt ermittelt werden:
MessageSource msgSrc = ApplicationServiceProvider.getService(MessageSource.class);
MessageSource ist ein
Interface
. Welche Implementierung hier tatsächlich zum Einsatz kommt ist unwichtig und hängt von der Konfiguration ab.
dynamische Anwendungsdienste
Der Dienste-Verwalter kann z.B. auch verwendet werden, um dynamische Dienste zu behandeln. Beispielsweise kann eine ladbare Anwendung einen Dienst registrieren, den dann eine andere ladbare Anwendung verwendet. Hierzu ein Beispeil aus VdrAssistant, wo der Aufnamehverwalter die Übersicht der Aufnahmen als Auswahl-Popup zur Verfügung stellt. Der Jobverwalter verwendet dieses Popup, um den Jobs Aufnahmen zuzuordnen.
Hier zuerst die Anmeldung des Dienstes vom Aufnahmeverwalter aus:
SelectionPopupFactory spf = ApplicationServiceProvider.getService(SelectionPopupFactory.class);
spf.registerSelectionPopupProvider(BonusItem.class, new RecSelectionPopupService());
Die SelectionPopupFactory
ist eine Fabrik, die ein Popup zu einer Entität liefert, über welches Instanzen dieser Entität ausgewählt und zugeordnet werden können.
Die Verwendung des Dienstes vom JobManager sieht wie folgt aus:
SelectionPopupFactory spf = ApplicationServiceProvider.getService(SelectionPopupFactory.class);
SelectionProvider spp = spf.getSelectionPopupProviderFor(BonusItem.class);
Entitäten sind Javabeans, die beispielsweise in einer SQL-Datenbank gespeichert werden können. In einer Anwendung mit grafischer Benutzerschnittstelle ist die Regel, dass die Attribute von Entitäten vom Anwender der grafischen Anwendung geändert werden - d.h. die Entitäten müssen in einem Editor präsentiert werden. Hier kommt JGoodies ins Spiel und deshalb entschied ich mich, JGoodies Model
Klasse als Basis für die Entitäten zu nehmen.
public class Person extends AbstractEntity {
public static final String FLD_NAME = "name";
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Soweit dürfte das Prinzip der Javabeans bekannt sein - jedes Attribut hat eine Lese- und eine Schreibfunktion. Die Superklasse AbstractEntity
fügt der Entität noch Verwaltungsattribute hinzu, die nur für Kontrollzwecke von Belang sind (mehr dazu später). Damit die Entität jedoch im Editor funktioniert, muss (zumindest) die Schreibfunktion noch erweitert werden.
public void setName(String name) {
String ov = this.name
this.name = name;
if (ov != null) {
if (name == null || ov.compareTo(name) != 0) setDirty(true);
} else if (ov != name) setDirty(true);
PropertyChangeEvent pce = new PropertyChangeEvent(this,
"name",
ov,
name);
firePropertyChangeEvent(pce);
}
Erst die Benachrichtigung bei einer Attributänderung führt dazu, dass die Automatismen von JGoodies bindings funktionieren und ein Formular autark werden kann. Bei der Schmutzerkennung habe ich vorgegriffen auf die Internas von SRJRCFrames. Eine Entität wird nur dann wirklich gespeichert, wenn sie geändert wurde (was neudeutsch schmutzig/dirty heißt).
Interne Attribute
WICHTIG: die internen Attribute sollten in der Anwendung als readonly angesehen werden!
- dirty
- ist ein virtuelles Attribute, d.h. dies wird nicht in die Datenbank gespeichert. Es dient dazu, zu erkennen, ob eine Entität gespeichert werden muss oder nicht.
- id
- Jede Entität hat eine Identifikationsnummer, die sie eindeutig macht. Ähnlich wie bei den Zahlentypen
Integer
, o.ä. gibt es keine unterschiedlichen Instanzen mit derselben Indentifikationsnummer. - uCreated
- Benutzerkennung des Benutzers, der den Datensatz angelegt hat. Bei transienten Entitäten kann dies
null
sein. Der Wert wird erst beim Speichern gefüllt. - uModified
- Benutzerkennung des Benutzers, der den Datensatz zuletzt gespeichert hat.
- dtCreated
- Zeitpunkt der Erzeugung des Datensatzes
- dtModified
- Zeitpunkt der letzten Änderung
- cModified
- Änderungszähler (wird für optimistische Sperren verwendet)
Die Datenbankzugriffe sind transparent, d.h. es ist in der Anwendung nicht ersichtlich, ob tatsächlich eine Datenbank involviert ist. Für die Anwendung gibt es nur Funktionen wie: Lesen oder Speichern. Der Rest wird intern vom Framework abgewickelt.
Wichtigste Schnittstelle zur Datenbank ist die Transaction
und die erhält man wieder von einem Anwendungsdienst, der TransactionFactory
.
if (taFactory == null)
taFactory = (TransactionFactory)
ApplicationServiceProvider.getService(
TransactionFactory.class);
Transaction ta = taFactory.createTransaction();
Eine Transaction
besteht aus einer oder mehreren Aktionen, den TransactionOperation
s. Diese können in beliebiger Anzahl einer Transaktion zugefügt werden. Beim Erstellen einer solchen Aktion und auch beim Hinzufügen zur Transaktion wird noch nichts ausgeführt. Erst wenn die gesamte Transaktion ausgeführt wird, werden auch die einzelnen Aktionen durchgeführt.
Eine Person könnte etwa so gespeichert werden:
Person instance = new Person();
Transaction ta = taFactory.createTransaction();
instance.setName("Mustermann");
ta.add(new TOSave<Person>(instance));
ta.execute();
Das Lesen von Personen ist genauso einfach ...
Transaction ta = taFactory.createTransaction();
TORead<Person> tor = new TORead<Person>(Person.class);
ta.add(tor);
ta.setRollbackOnly();
ta.execute();
List<Person> persons = tor.getResult();
Um z.B. eine Person zu finden, deren Namen bekannt ist, ...
Transaction ta = taFactory.createTransaction();
ConditionElement ceName = new EqualConditionElement(Person.FLD_NAME,
"Mustermann");
TORead<Person> tor = new TORead<Person>(Person.class,
ceName);
ta.add(tor);
ta.setRollbackOnly();
ta.execute();
List<Person> persons = tor.getResult();
Über die apidocs lassen sich weitere Varianten herausfinden. SRJRCFrames unterstützt teilweise gelesene Entitäten und natürlich auch das Ändern einzelner Attribute.