Datenbankzugriffe sind teurer als Speicherzugriffe, das ist nicht neu. Viele Frameworks wie Hibernate setzen deswegen auf einen Cache, um die Anzahl der Datenbankzugriffe zu minimieren und damit die Performance zu erhöhen. Das Caching passiert dabei meißt unter der Haube und lässt den Entwickler den Fokus auf die eigentlichen Arbeiten behalten.
Mit Google App Engine und Memcached und dem verteilten Cache ist Caching auch in der Cloud möglich. Doch auch wenn bei App Engine keine Persistenzlösung wie Hibernate oder EclipseLink möglich ist, kann man auf eine einfache Weise Caching nachrüsten.
Eine einfache DAO-Klasse
public class NodeDAO { public Node load(long id) { // code } public Node save(Node node) { if (node.getId() == 0) { insert(node); } else { update(node); } return node; } public void delete(Node node) { // code } private void insert(Node node) { // code } private void update(Node node) { // code } }
Relativ einfach, aber erfüllt den Zweck.
Zunächst erstellen wir ein Interface Indexed, damit die Lösung später für verschiedene Klassen möglich ist.
public interface Indexed { public long getId(); }
Und einen Simplen Cache (hier kann man später auf MemCached umrüsten)
public class SimpleCache { static HashMap cache; public static HashMap getInstance(){ if (cache==null){ cache = new HashMap(); } return cache; } }
Als Schlüssel soll hier Klassenname und ID fungieren
Noch ein paar kleine Interfaces für die 3 Cache- bzw Datenbankfunktionen
@Retention(RetentionPolicy.RUNTIME) public @interface SelectCachable {} @Retention(RetentionPolicy.RUNTIME) public @interface SaveCachable {} @Retention(RetentionPolicy.RUNTIME) public @interface DeleteCachable {}
Ein bisschen Guice-Magie
Starten wir mit dem SELECT oder GET – Befehl. Wenn wir einen Datensatz geladen haben, soll der im Cache verewigt werden. Ist er schon im Cache, soll keine Datenbankverbindung hergestellt werden. Wir benutzen den MethodInterceptor.
public class SelectCacher implements MethodInterceptor{ public Object invoke(MethodInvocation call) throws Throwable { Object[] args = call.getArguments(); Object result; if (args.length==1 && args[0] instanceof Long && SimpleCache.getInstance().containsKey(call.getMethod().getReturnType()+ "@" + args[0])){ // aus dem Cache holen result = SimpleCache.getInstance().get( call.getMethod().getReturnType()+ "@" + args[0]); }else{ // Aus der DB Holen result = call.proceed(); SimpleCache.getInstance().put( call.getMethod().getReturnType()+ "@" + args[0],(Indexed) result); } return result; } }
Auch der DELETE-Befehl ist simpel:
public class DeleteCacher implements MethodInterceptor { public Object invoke(MethodInvocation call) throws Throwable { Object[] args = call.getArguments(); Indexed indexer = (Indexed) args[0]; if (args.length==1 && SimpleCache.getInstance().containsKey(indexer.getClass()+ "@" + indexer.getId())){ SimpleCache.getInstance().remove( indexer.getClass()+ "@" + indexer.getId()); } return call.proceed(); } }
Zuletzt noch der PUT Befehl, hier muss man dazu sagen, dass aufgrund der ID entschieden wird, ob ein Insert oder Update geschieht, beim Insert wird bei @@IDENTITY die ID bestimmt und dem Objekt zugewiesen.
public Object invoke(MethodInvocation call) throws Throwable { Object result = call.proceed(); Indexed indexer = (Indexed) result; SimpleCache.getInstance().put(indexer.getClass() + "@" + indexer.getId(), indexer); return result; }
Nun müssen wir noch die Annotationen mit den Interceptors verbinden. Das passiert bei Guice so:
public class CacheModule extends AbstractModule{ protected void configure() { bindInterceptor(any(), annotatedWith(SelectCachable.class), new SelectCacher()); bindInterceptor(any(), annotatedWith(SaveCachable.class), new SaveCacher()); bindInterceptor(any(), annotatedWith(DeleteCachable.class), new DeleteCacher()); } }
Der finale Schritt, jetzt verbinden, oder besser gesagt injizieren, wir alles, was bisher erstellt wurde. Per Factory-Pattern erhalten wir wir nun eine Erweiterte Variante der Basisklasse. Per Annotationen bestimmen wir, welcher Methode welcher Interceptor zugewiesen wird.
public class NodeDAO { public static NodeDAO create() { Injector inj = Guice.createInjector(new CacheModule()); NodeDAO ret = inj.getInstance(NodeDAO.class); return ret; } @SelectCachable public Node load(int id) { // Code } @SaveCachable public Node save(Node node) { if (node.getId() == 0) { insert(node); } else { update(node); } return node; } @DeleteCachable public void delete(Node node) { // Code } private void insert(Node node) { // Code } private void update(Node node) { // Code } }
Mit Google Guice Funktionalitäten nachrüsten ist recht einfach. Die Lösung ist noch nicht ideal, aber ein guter Start, da alle Komponenten austauschbar sind.
Letztens habe ich ja schon ein bissel über Google und ihre AGBs geschrieben. Die Krone allerdings setzt sich Springsource auf.
In der letzten Zeit habe ich auch mehr mit Groovy und Grails beschägtigt. Springsource hat Groovy und Grails übernommen.
VMWare hat Springsource übernommen. Und die IDE mit der besten Grails und groovy Unterstützung soll zur Zeit die “SpringSource Tool Suite” sein. (Und fehlende IDE Unterstützung ist zZ das Problem von Groovy und Grails, die ich ab jetzt mit GG abkürze). Das Spring Framework, ein führendes Enterprise-App Framework und Teil der Basis von Grails sowie die Basis von Spring Roo ist Open Source. Open … Offen …. Offen für jeden … es zu benutzen, es zu verbessern und es herunterzuladen.
Auch Eclipse, die zur Zeit wohl beste Java-IDE ist Open Source. Diese IDE ist so gut, das Borland von ihrem eigenen Produkt JBuilder zu einem kommerziellen Plugin für Eclipse übergegangen sind. Der Flexbuilder von Adobe basiert auf Eclipse. Einfach DIE IDE zur Zeit. Und auch die SpringSource Tool Suite basiert auf Eclipse.
Will man sich die STS jedoch runterladen sieht man folgende AGBs.
Gleich 3 Listen mit Menschen und Organisationen für die das ganze nicht ganz so offen ist.
Und ich bin echt überrascht. Vor allem von der Häufigkeit von “Germany” in den Listen. Neben Iran und Korea zwar nichts aber was ist das für eine Politik?
Offen für alle ausser den Bösen? Böse welche die USA bestimmt?
Ich steh zwar nicht auf einer der Listen, aber mich schreckt sowas ab. Wie heisst so eine Lizenz?
TNEUSV würde ich vorschlagen … und jedem davon abraten.
Mein kleines Projekt staemmebbcodes hat sich in einem Jahr sehr entwickelt. Etwa 16.000 Benutzer, 5 Sprachen und und und…
Um da den Ueberblick zu behalten und auch anderen die Moeglichkeit zu geben, mal in meinen Code zu schauen, habe ich mich (endlich) entschieden, mal code.google auszuprobieren. Ein Projekt ist ja schnell angelegt, nun wollte ich es nur noch mit meinem Ecclipse verbinden, um nicht alles Webbasiert zu gestalten.
Und das ging erstaunlich einfach, fuer viele Aufgaben gibt es schon vorkunfigurierte Plug-ins.
Subclipse ist das erste Plugin, was schon im Handumdrehen konfiguriert ist, man muss nur beachten, das man zum commiten https statt http benutzt. Das wars schon !
Mylin ist das zweite Plugin, welches wunderbar mit code.google harmoniert. Auf die entsprechene Perspektive wechseln, Refresh druecken und die Offenen Tasks flattern einem um die Ohren (leider !!).
Um interessierten das Ausprobieren zu erleichtern, habe ich die XML mit den entsprechenden Ecclipse-Update-Mirrors verlinkt, damit ihr nicht im Web danach suchen muesst.
Evtl hat ja jetzt sogar einer Lust, bei dem einem oder anderen Projekt mitzuarbeiten, vllt sogar bei Meinem.

Categories
Tag Cloud
Blog RSS
Comments RSS
Last 50 Posts
Back
Back
Void « Default
Life
Earth
Wind
Water
Fire
Light 