Ciekawy artykuł o perspektywach zmaterializowanych w PostgreSQL
PostgreSQL niestety nie ma wbudowanej obsługi perspektyw zmaterializowanych, ale jak się okazuje niektóre ich typy można łatwo zrealizować za pomocą zwykłych widoków, tabel i odpowiednich triggerów. Jak to zrobić opisane jest w artykule Jonathana Gardnera, który znajduje się tutaj: http://www.jonathangardner.net/tech/w/PostgreSQL/Materialized_Views.
Sortowanie w PostgreSQL
Sortowanie w PostgreSQL uwzględniające zasady obowiązujące dla konkretnego języka może być dość kłopotliwe. Niestety PostgreSQL nie pozwala na dynamiczne zmienianie sposobu sortowania tak jak jest to możliwe w MySQL (klauzula COLLATE) albo w Oraclu (parametr sesji NLS_SORT), tylko wymaga podania sposobu sortowania podczas tworzenia klastra (parametry –locale, –lc-collate i poniekąd –lc-ctype polecenia initdb) i później już parametry te pozostają niezmienne.
Jest to niewątpliwa wada jeśli chciałoby się przechowywać w bazie dane w wielu językach. Sytuacje poprawia nieco fakt, że przy locale bazujących na normie ISO14651 (a jest to chyba większość locales w nowoczesnych linuxach) sortowanie w wielu językach jest w miarę poprawne bez względu na wybrane locale.
Np. w przypadku Ubuntu 7 i 8 polskie napisy sortują się poprawnie zarówno przy locale pl_PL.utf8 jak i en_US.utf8, co można sprawdzić wykonując polecenia (PostgreSQL będzie sortował identycznie przy tym samym locale):
echo -e "ą\na\nb\nc\nć\nł\nz\nż\nź" |LC_ALL=pl_PL.utf8 sort
echo -e "ą\na\nb\nc\nć\nł\nz\nż\nź" |LC_ALL=en_US.utf8 sort
Natomiast przy locale ustawionym na POSIX lub C sortowanie nie będzie poprawne, ponieważ w tym przypadku porównywane są poprostu binarne kody znaków i polskie litery lądują na końcu.
Wróćmy na razie do PostgreSQL. Jeżeli podczas wykonywania initdb nie podaliśmy explicite parametrów –locale, –lc-collate i/lub –lc-ctype to initdb pobiera wartości tych parametrów ze zmiennych systemowych LC_ALL w pierwszej kolejności, LC_COLLATE oraz LC_CTYPE jeśli LC_ALL nie jest ustawione i LANG jeśli poprzednie zmienne również nie były ustawione. Jeśli żadna z tych zmiennych nie jest ustawiona to locale jest ustawiane na C.
Czyli jeśli korzystamy z klastra stworzonego np. automatycznie przy instalacji PostgreSQL, to może się zdażyć, że locale będzie inne niż byśmy sobie tego życzyli. Najlepiej w tami wypadku klaster usunąć (po wkonaniu backupu) i stworzyć ręcznie od nowa z odpowiednim locale. W Ubuntu można się zamiast initdb posłużyć wygodniejszym pg_createcluster.
OK, ustawiliśmy sobie locale na pl_PL.utf8 i badamy dalej sortowanie (dla ułatwienia eksperymentów posługuję się dalej kombinacją poleceń echo/sort – PostgreSQL będzie sortował tak samo):
echo -e "język polski\njęzyk angielski\njęzykowy\njęzyk-language\njęzyka\njęzyk" |LC_ALL=pl_PL.utf8 sort
W efekcie dostajemy taki porządek sortowania:
język języka język-language językowy język angielski język polski
Nie wygląda to najlepiej i nie jest zgodne z polskimi zasadami sortowania (są one dość dobrze opisane np. tutaj: http://www.ia.pw.edu.pl/~wujek/tex/idx/porzadek.html#reguly). Lepiej by było gdyby napisy zostały posortowane następująco:
język język angielski język polski język-language języka językowy
Jak coś takiego osiągnąć? Trzeba nieco zmienić plik z definicją locale.
Pliki źródłowe definiujące zasady obowiązujące dla poszczególnych locale znajdują się w katalogu /usr/share/i18n/locales. Najlepiej nie zmieniać standardowego polskiego locale pl_PL tylko stworzyć jego inny wariant np. pl_PL@bettersort. W tym celu kopijemy plik pl_PL i zabieramy się za jego edycję.
Interesuje nas sekcja LC_COLLATE a szczególnie fragment:
reorder-after <U00A0>
<U0020> <U0020>;IGNORE;<U0020>;<U0020>
Zmieniamy go na następujący:
reorder-after <RES-1>
<U0020> <U0020>;IGNORE;<U0020>;<U0020>
<U00A0> <U0020>;IGNORE;<U0020>;<U00A0>
<U002D> <U002D>;IGNORE;<U002D>;<U002D>
<U2010> <U002D>;IGNORE;<U002D>;<U2010>
<U2011> <U002D>;IGNORE;<U002D>;<U2011>
<U2012> <U002D>;IGNORE;<U002D>;<U2012>
<U2013> <U002D>;IGNORE;<U002D>;<U2013>
<U2212> <U002D>;IGNORE;<U002D>;<U2212>
Co to oznacza? Symbol RES-1 to symbol specjalny znajdujący się w porządku sortowania przed wszystkimi innymi znakami. Polecenie reorder-after <RES-1> powoduje wstawienie w porządku sortowania symboli znajdujących się w kolejnych linijkach właśnie za symbolem RES-1, a więc na pewno przed wszystkimi innymi znakami. Dwie pierwsze linijki zaczynające się od symboli <U0020> i <U00A0> są odpowiedzialne za sortowanie zwykłej spacji i spacji nieroździlającej (nbsp). Kolejne linijki to różne wersje dywizu i myślnika.
Po wyedytowaniu tego pliku należy jeszcze dopisać go do listy obsługiwanych locale znajdującej się w pliku /usr/share/i18n/SUPPORTED. Dodajemy tam wpis:
pl_PL.UTF-8@bettersort UTF-8
Następnie trzeba skompilować nasze nowe locale poleceniem:
sudo locale-gen pl_PL.UTF-8@bettersort
Sprawdzamy jeszcze czy wszystko się udało poleceniem: locale -a i testujemy sortowanie:
echo -e "język polski\njęzyk angielski\njęzykowy\njęzyk-language\njęzyka\njęzyk" |LC_ALL=pl_PL@bettersort.utf8 sort
Powinniśmy dostać poprawne sortowanie.
Pozstaje nam jeszcze stworzenie od nowa klsatra PostgreSQL poleceniem:
sudo pg_createcluster --locale=pl_PL.utf8@bettersort --lc-messages=en_US.utf8 --start 8.3 main
W tak stworzonej bazie sortowanie powinno być poprawne.
Springowe beany w JSP
Na początek coś prostego, ale przydatnego. Tag JSP, który umożliwia dostanie się do springowego beana bezpośrednio w kodzie strony JSP.
Tag używamy w następujący sposób:
<js:useSpringBean id="nazwaZmiennej" name="nazwaBeana"/>
Ewentualnie dodatkowo można dodać parametr type, dzięki czemu tag sprawdzi dodatkowo czy typ pobranego beana zgadza się z oczekiwanym i w przeciwnym wypadku rzuci wyjątek BeanNotOfRequiredTypeException.
Kod taga:
public class UseSpringBeanTag extends TagSupport {
protected String name = null;
protected String type = null;
@Override
public int doStartTag() throws JspException {
ServletContext servletContext = pageContext.getServletContext();
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
Object bean = null;
if (type == null) {
bean = applicationContext.getBean(name);
} else {
try {
bean = applicationContext.getBean(name, Class.forName(type));
} catch (ClassNotFoundException ex) {
throw new JspException(ex);
}
}
pageContext.setAttribute(id, bean);
return SKIP_BODY;
}
@Override
public void release() {
super.release();
name = null;
type = null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Ponieważ tag ten udostępnia w stronie JSP zmienną, pod którą dostępny będzie pobrany ze springa bean, dodatkowo potrzebna jest klasa dziedzicząca z TagExtraInfo dostarczająca informacji o zmiennej:
public class UseSpringBeanTei extends TagExtraInfo {
@Override
public VariableInfo[] getVariableInfo(TagData data) {
String id = data.getAttributeString("id");
String type = data.getAttributeString("type");
if (id != null) {
if (type == null) {
type = "java.lang.Object";
}
return new VariableInfo[] { new VariableInfo(id, type, true,
VariableInfo.AT_END) };
}
return super.getVariableInfo(data);
}
}
Dodatkowo oczywiście potrzebujemy stworzyć plik TLD zawierający specyfikacje naszego taga:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>spring-jsp-tag</short-name> <tag> <name>useSpringBean</name> <tag-class>eu.bojar.web.taglib.UseSpringBeanTag</tag-class> <tei-class>eu.bojar.web.taglib.UseSpringBeanTei</tei-class> <body-content>empty</body-content> <attribute> <name>id</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>name</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>type</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>