Pracowałem nad aplikacją serwletową Java, która musi konstruować bardzo dynamiczne instrukcje SQL do celów raportowania adhoc. Podstawową funkcją aplikacji jest dostarczenie zestawu nazwanych parametrów żądania HTTP do wstępnie zakodowanego zapytania i wygenerowanie ładnie sformatowanej tabeli danych wyjściowych. Użyłem Spring MVC i struktury iniekcji zależności, aby przechowywać wszystkie moje zapytania SQL w plikach XML i ładować je do aplikacji raportującej wraz z informacjami o formatowaniu tabeli. Ostatecznie wymagania dotyczące raportowania stały się bardziej skomplikowane niż możliwości istniejących ram mapowania parametrów i musiałem napisać własne. Było to interesujące ćwiczenie w rozwoju i stworzyło ramy do mapowania parametrów o wiele bardziej solidne niż cokolwiek innego, co mogłem znaleźć.
Nowe mapowania parametrów wyglądały następująco:
select app.name as "App",
${optional(" app.owner as "Owner", "):showOwner}
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = ${integer(0,50):serverId}
and app.id in ${integerList(50):appId}
group by app.name, ${optional(" app.owner, "):showOwner} sv.name
order by app.name, sv.name
Piękno powstałej struktury polegało na tym, że mógł przetwarzać parametry żądania HTTP bezpośrednio w zapytaniu z odpowiednim sprawdzaniem typu i sprawdzaniem limitów. Do weryfikacji danych wejściowych nie są wymagane żadne dodatkowe mapowania. W powyższym przykładowym zapytaniu parametr o nazwie serverId
zostanie sprawdzony, aby upewnić się, że może rzutować na liczbę całkowitą i mieści się w zakresie od 0 do 50. Parametr appId byłby przetwarzany jako tablica liczb całkowitych z ograniczeniem długości do 50. Jeśli pole showOwnerjest obecny i ustawiony na „true”, bity SQL w cudzysłowach zostaną dodane do wygenerowanego zapytania dla opcjonalnych mapowań pól. field Dostępnych jest kilka innych mapowań typów parametrów, w tym opcjonalne segmenty SQL z dodatkowymi mapowaniami parametrów. Pozwala na tak złożone mapowanie zapytań, jak tylko może wymyślić programista. Ma nawet kontrolki w konfiguracji raportu, które pozwalają określić, czy dane zapytanie będzie miało ostateczne mapowania za pośrednictwem PreparedStatement, czy po prostu uruchomione jako wstępnie zbudowane zapytanie.
Przykładowe wartości żądania HTTP:
showOwner: true
serverId: 20
appId: 1,2,3,5,7,11,13
Spowoduje to wygenerowanie następującego kodu SQL:
select app.name as "App",
app.owner as "Owner",
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = 20
and app.id in (1,2,3,5,7,11,13)
group by app.name, app.owner, sv.name
order by app.name, sv.name
Naprawdę uważam, że Spring lub Hibernate lub jeden z tych frameworków powinien oferować bardziej niezawodny mechanizm mapowania, który weryfikuje typy, pozwala na złożone typy danych, takie jak tablice i inne tego typu funkcje. Napisałem swój silnik tylko do moich celów, nie jest on do końca czytany do ogólnego wydania. Obecnie działa tylko z zapytaniami Oracle, a cały kod należy do dużej korporacji. Któregoś dnia mogę wziąć swoje pomysły i zbudować nowy framework open source, ale mam nadzieję, że jeden z istniejących dużych graczy podejmie wyzwanie.
InputStream
wnętrzeif (props == null)
instrukcji, aby nie tworzyć jej wystąpienia, gdy nie jest potrzebne.