# Performance ApplicationBean vs. Datenbanksuche



## markai (18. Jun 2012)

Ich habe folgendes Problem:
Die Benutzer meiner Applikation sollen Rezepte anlegen können. Dazu sollen (mittels Primefaces Autocomplete) zusätzlich zum Rezept die jeweiligen Zutaten eingegeben werden können um später Rezepte nach Zutaten suchen zu können. Das bedeutet es werden alle Zutaten aus meiner derby DB benötigt. Um die Datenbank zu entlasten dachte ich an die Möglichkeit einer ApplicationScoped bean, in der ich eine Hashmap habe die ebenfalls meine Zutaten speichert (nur halt im RAM). Da es möglich sein kann dass Benutzer Rezepte anlegen, dessen Zutaten noch nicht in der DB gespeichert sind, sollen Benutzer auch selber Zutaten in meine DB speichern können, die dann natürlich auch in meine Hashmap gespeichert werden müssen.

Macht man das so? Gibt es eine einfachere oder elgantere Lösung oder soll ich einfach jedes mal wenn ein Benutzer ein Rezept anlegt diesen die ganze DB nach Zutaten durchsuchen lassen? Bitte helft mir...


----------



## SlaterB (20. Jun 2012)

ein solcher Cache erscheint sinnvoll, sofern es dir möglich ist,
nicht immmer klappt das, z.B. bei google-Suche Autocomplete

es kann aber auch reichen, nur einen Teil der Daten für die wichtigsten Autocomplete/ die ersten Vorschläge vorzuhalten
und genaueres später nachzuladen, dann evtl. zeitweise in den Cache


----------



## FArt (21. Jun 2012)

Ich würde alle Daten in der DB ablegen, da du dann keine Untesrcheidung machen musst. Wenn du die Daten nicht vermischen möchtest, kannst du diese immer noch markieren.

Ein reines Caching aus Performancegründen kann dann immer noch eingeführt werden, aber unabhängig von den Daten.


----------



## markai (22. Jun 2012)

Vielen Dank für eure Tipps! War mir nicht sicher ob man das so machen kann. Leider hatte ich einige Probleme beim initialisieren der ApplicationScopedBean, darum habe ich mich dafür entschieden bei einer Benutzereingabe ein Select Statement abzuschicken:


```
@NamedQuery(name = "findRecipeStartsWith",
    query = "Select i from Ingredients i where lower(i.name) like :searchPattern")
```

in meiner Facade mache ich:


```
public List<Ingredients> findRecipeStartsWith(String name) {
        final String searchPattern = new StringBuilder().append(name.toLowerCase()).append('%').toString();
        return em.createNamedQuery("findRecipeStartsWith", Ingredients.class).setParameter("searchPattern", searchPattern).getResultList();
    }
```

Ist glaub damit habe ich eine halbwegs schöne Lösung gefunden, die die DB nicht so belastet als wenn ich alle Records bei jeder Anfrage in den Cache lade und dann drüber iteriere (wie ich es ursprünglich hatte). Ich danke euch


----------



## SlaterB (22. Jun 2012)

ein ständiges LIKE kannst du aber auch nicht ernsthaft als geringe Belastung bezeichnen,
wenn das ganze eh nur selten geschieht, dann ist es fast egal, 
wobei dies hier in der Tat dem Laden aller Einträge/ dauerhaften Cache vorzuziehen sein kann

wenn es aber doch ein spürbarer Faktor ist, dann wäre ständige LIKE-Suche schon schlimm,
dann überlegen, wenigstens in der DB einen Index anzulegen, der die Suche unterstützt,

was die Technik an sich dazu an Möglichkeiten anbietet kann ich nicht so gut beurteilen,
mit normalen DB-Tabellen könnte man theoretisch die Java-Cach-Variante einer einfachen Map nachbauen

'Gemüsesup' -> Text-Freifeld einer Liste von Namen, oder Liste von Ids, die in zweiter Query nachgeladen werden,
bei jeder Änderung der Rezeptliste müsste auch auf Aktualisierung des Index geachtet werden,

theoretisch hat man dabei die Möglichkeit (*), auch bisschen individueller vorzugehen, 
unter 'Gemüsesuppe'-Suchen auch 'kalter Kaffe' einzuordnen, wenn das mit angezeigt werden soll

für die ersten Buchstaben hat man vielleicht zu lange Listen, da könnte man abweichen und in der Tabelle der Rezepte an sich (bzw. einer parallelen Tabelle) zusätzliche Spalten einfügen:
'begin1', 'begin2', 'begin3' usw., für jeden Eintrag als String den ersten Buchstaben ablegen, die ersten 2, die ersten 3 usw.,
dann könnte man darauf wieder mit = vergleichen, was besser als LIKE ist, oder einen DB-Index laufen lassen der das dann schon schafft,

die Möglichkeit (*) würde hier freilich wieder wegfallen, 
aber lohnt sich wohl auch kaum, von 'g' auf Gemüsesuppe und dann dazu auch auf 'kalter Kaffe' zu schließen

-------

ok, wenn man all das betrachtet, ist ein einfaches LIKE sicher die einfachere Variante 
wie man bemerkt haben dürfte habe ich mir das eh gerade überwiegend ausgedacht, funktioniert am Ende vielleicht gar nicht hilfreich

wie immer bei Indexen und Caches ist aber der Grundgedanke:
Speicher- oder DB- oder Festplattenplatz in der Breite belegen, im Voraus langwidrig anlegen/ ständig pflegen,
um zur Laufzeit die entscheidenden Sekunden Berechnungszeit zu sparen


----------

