2010-12-06

Użycie widoków w Hibernate

Spieszę donieść, iż błąd uniemożliwiający użycie @SecondaryTable w klasie bazowej, na który utyskiwałem w tym wpisie został w końcu poprawiony (po czterech latach od zgłoszenia!). Uaktualniłem projekt i rzeczywiście Hibernate w wersji 3.5.4 poprawnie obsługuje mapowanie bazowej encji na wiele tabel. Trafiłem jednak na kolejny problem i - w przeciwieństwie do opisywanego wcześniej - nie bardzo widzę sposób na obejście go.

Mając tabele i widok:

CREATE TABLE `TB_SOME_TABLE` (
`ID_PARENT` INT(11) NOT NULL AUTO_INCREMENT,
`READ_WRITE` TEXT,
PRIMARY KEY (`ID_PARENT`)
)

CREATE TABLE `TB_OTHER_TABLE` (
`PARENT_ID` INT(11) NOT NULL,
`SOME_DATA` TEXT
)

CREATE VIEW `VW_SOME_VIEW` AS
SELECT
`T`.`PARENT_ID` AS `PARENT_ID`,
COUNT(`T`.`PARENT_ID`) AS `READ_ONLY`
FROM
`TB_OTHER_TABLE` `T`
GROUP BY
`T`.`PARENT_ID`

i klasę:

@Entity
@Table(name = "TB_SOME_TABLE")
@SecondaryTable(
name = "VW_SOME_VIEW"
pkJoinColumns = @PrimaryKeyJoinColumn(name = "PARENT_ID")
)
public class Parent {
@Id
@Column(name = "ID_PARENT")
private Long id;

@Basic
@Column(name = "READ_WRITE, table = "TB_SOME_TABLE")
private String readWriteAtribute;

@Basic
@Column(
name = "READ_ONLY", table = "VW_SOME_VIEW",
insertable = false, updatable = false
)
private String readOnlyAttribute;

...
}

nie jesteśmy w stanie zapisać do bazy obiektu tej klasy. Pomimo, że jedyny atrybut zamapowany na widok jest tylko do odczytu (insertable i updatable równe false), to Hibernate próbuje wstawić dane do widoku, co kończy się wyjątkiem:
Unhandled exception 'JDBC exception on Hibernate data access:
SQLException for SQL [insert into VW_SOME_VIEW (PARENT_ID) values (?)];
SQL state [HY000]; error code [1471]; could not insert: [Parent]

Widać że się stara (bo w wygenerowanym SQLu nie ma kolumny READ_ONLY), ale do końca mu nie wychodzi (bo jednak próbuje wstawić do widoku wiersz nie zawierający żadnych danych). I zupełnie nie mam pomysłu co na to poradzić.

2010-10-26

Krótko

Nabyłem drogą kupna Photoshopa (Elements oczywiście). Wiem: ekstrawagancja i burżuazja, ale czasami mi się to zdarza. Ale ja nie o tym chciałem. Rzeczony Photoshop został wysyłany przez Amazon UK DHLem za darmo (od niedawna Free Super Saver Delivery obejmuje też Polskę) i kosztował 280 PLN. W naszym rodzimym sklepie, np. przysłowiowym Vobisie kosztuje 399 PLN. Rrrwa mać po trzykroć.

2010-10-17

Icon Finder

Nie raz i nie dwa pisząc program lub tworząc stronę internetową potrzebowałem różnego rodzaju ikon. Podstawowe kryteria, które musiały spełniać to: estetyczne, spójne i last but not least legalne. Bardzo ładnym i obszernym zestawem są Crystal Icons (licencja LGPL), ale jednak nie do wszystkiego pasują - są odrobinę zbyt śliczne. Drugi zestaw z którego tu i ówdzie korzystałem to Mini Icons ze strony famfamfam.com - prościutkie, małe ikonki idealne do użycia w aplikacjach webowych, ale jest ich tylko 144 i nie zawsze znajdzie się to czego potrzeba. Dzisiaj trafiłem na stronę Icon Finder. W tej chwili jest tam 645 zestawów, 188325 ikon! I przede wszystkim możliwość wygodnego wyszukiwania bo wszystkie ikonki mają przypisane tagi (np. "red", "arrow", "left"). Mój ulubiony na dzień dzisiejszy zestaw to Simplico (licencja Creative Commons).

2010-08-26

Groovy 1.7.0 i Spring

W jednej z aplikacji które popełniłem, używam skryptów Groovy jak kontrolerów Spring MVC. Jest to wygodne w sytuacji, kiedy kod kontrolera często się zmienia a nie ma możliwości restartu całej aplikacji. Dzięki poniższej definicji:

komponent some.Controller będzie automatycznie przeładowywany (czyli skrypt będzie ponownie kompilowany) jeżeli plik źródłowy Script.groovy się zmieni (ale nie częściej niż podany czas - tutaj 1000 ms). Wszystko działało tak jak powinno do momentu upgrade'u Springa do wersji 3.0.0.RELEASE - wtedy aplikacja przestała zauważać zmiany w skryptach. Uznałem, że chwilowo mogę bez tego żyć a w wersji 3.0.1 pewnie poprawią. Kiedy jednak po kolejnych uaktualnieniach doszedłem do wersji 3.0.3 i dalej nie było ani trochę lepiej, zacząłem intensywniej szukać. Okazało się, że Spring jest niewinny a problemy sprawia sam Groovy, którego wersję podniosłem do 1.7.0 na fali ogólnego uaktualniania aplikacji (w zależnościach Springa jest 1.6.3). Aby dynamiczne przeładowywanie zaczęło znowu działać trzeba albo cofnąć się do wersji 1.6.x, albo użyć najnowszego Groovy (w tej chwili 1.7.4). Dla zainteresowanych zgłoszenie w JIR-ze GROOVY-3981.

2010-08-15

SyntaxHighlighter

Edycja listingów we wpisach zamieszczanych na Bloggerze to makabra. Sam edytor nie jest szczytem marzeń, ale prawdziwy problem to psucie wcięć. Nawet jeżeli umieszczę listing w tagach <pre> które teoretycznie powinny zachowywać formatowanie, to przejście w Bloggerowym edytorze z zakładki Edytuj kod HTML na Nowy post zjada mi co najmniej jedną spację z wcięcia. Początkowo myślałem że robię coś nie tak, ale w sieci można znaleźć sporo informacji o tego typu problemach. Nie znalazłem rady na zjadanie spacji (anyone?), ale znalazłem sposób na formatowanie (dzięki któremu mogę w miarę komfortowo pisać tylko w HTMLu): SyntaxHighlighter. Jest to zestaw JavaScriptów i arkuszy stylów, który automatycznie formatuje listingi w większości popularnych języków programowania. Wystarczy dołączyć do strony odpowiednie skrypty i style (dla ciekawych: prawy przycisk myszy i View Page Source) a potem umieścić listing w tagu <pre> z zaznaczeniem sposobu formatowania:
<pre class="brush: java">
public class Test {
    private String a;
    public Test() {
        // konstruktor
    }
}
</pre>
Efekt wygląda tak:
public class Test {
    private String a;
    public Test() {
        // konstruktor   
    }
}
Jedyne niedociągnięcie jakie zauważyłem, to niepotrzebne wyświetlanie pionowego scrolla w przeglądarkach opartych na silniku WebKit (Safari, Google Chrome).

[aktualizacja 2010.08.16]

Dodałem SyntaxHighlightera do posta o Google App Engine. Very nice indeed.

2010-08-08

Google App Engine

Google App Engine (for Java) umożliwia uruchamianie aplikacji webowych na serwerach Google. Używając jednego konta można zarejestrować do 10 aplikacji, każda z nich może wykorzystywać do 1GB przestrzeni dyskowej, po 1GB transferu wychodzącego i przychodzącego oraz 6.5 godziny znormalizowanego 1) czasu procesora na dobę. I to wszystko za darmo. Całkiem nieźle, prawda? Jest jednak jedno ale: Google'owa maszyna wirtualna Javy i serwer aplikacji mają trochę ograniczeń, na które można trafić próbując uruchomić coś więcej niż "Hello World".

Nie wnikając w zbędne szczegóły: moja aplikacja miała rejestrować obiekty w bazie danych (za pośrednictwem JPA) oraz udostępniać je w postaci zserializowanej do XMLa. Trafiłem na kilka mniej lub bardziej poważnych problemów.


[1] Klasa utrwalana za pomocą JPA nie może być powiązana z kilkoma obiektami tego samego typu. W poniższej sytuacji:
@Entity
class A {
}

@Entity
class B {
@ManyToOne
A pole1;
@ManyToOne
A pole2;
}
dostaniemy wyjątek:
Error in meta-data for B.pole2: Class B has multiple relationship fields of type A: pole1
and pole2. This is not yet supported.


[2] Nie jest obsługiwana strategia dziedziczenia SINGLE_TABLE. W przykładzie:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
class A {
}

@Entity
class B extends A {
}
dostaniemy wyjątek:
Found inheritance strategy 'SINGLE_TABLE' on B.  This strategy is not supported in this
context.

[3] Google App Engine dopuszcza jako identyfikatory obiektów typy Long, String oraz własny typ com.google.appengine.api.datastore.Key. Jednak gdy obiekt ma być powiązany z innym:
@Entity
class A {
@Id
Long id;
}

@Entity class B {
@ManyToOne
A a;
}
jego klucz nie może być typu Long. W powyższym przykładzie dostaniemy wyjątek:
Cannot have a java.lang.Long primary key and be a child object


[4] Nie jest obsługiwane zachłanne wczytywanie powiązanych obiektów. Przy mapowaniu:
@Entity
class A {
}

@Entity
class B {
@ManyToOne(fetch = FetchType.EAGER)
A a;
}
serwer zapisze do logu ostrzeżenie:
WARNING: Meta-data warning for B.a: The datastore does not support joins and therefore
cannot honor requests to eagerly load related objects.
The field will be fetched lazily on first access.
i zgodnie z jego treścią, obiekt powiązany trzeba będzie jawnie doczytać.


[5] Niedostępna jest klasa sun.reflect.ReflectionFactory, która wbrew nazwie nie jest specyficzna dla Sunowskiej implementacji JVM (jest dostępna także w maszynach wirtualnych IBMa i Apple). Co prawda nie próbowałem jej używać bezpośrednio, ale pośrednie owszem. Próba wykorzystania biblioteki XStream do serializacji obiektów do XMLa kończy się wyjątkiem:
java.lang.NoClassDefFoundError: sun.reflect.ReflectionFactory is a restricted class.


[6] W niektórych przypadkach uprawnienia nie pozwalają na akcje, które w typowych maszynach wirtualnych są dopuszczalne. Przykładem może być dziedziczenie z klasy java.io.ObjectOutputStream (wykorzystywane np. przez Apache Wicket i XStream) - App Engine zgłasza wyjątek:
java.security.AccessControlException: access denied
(java.io.SerializablePermission enableSubclassImplementation)


(*)Znormalizowana jednostka zużycia mocy obliczeniowej jest - dość mgliście - określona jako wydajność 1.2 GHz procesora z serii x86 Intela.

2010-06-22

iPhone OS 4

Wczoraj pojawił się w sieci update do iPhone OS 4 (czy według nowej nomenklatury iOS 4). W pierwszym odruchu chciałem poczekać z aktualizacją - wiadomo: problemy wieku dziecięcego, zawsze bezpieczniej jest instalować drugie wydanie nowego systemu. Jednak po krótkim wahaniu uznałem, że trzeba być twardym a nie miętkim i zainstaluję. Zerknąłem tylko pobieżnie na forum MyApple.pl i nie znalazłszy żadnych niepokojących sygnałów (właściwie nie znalazłszy wiele ponad "jakie mam wolne łącze", "a u mnie jeszcze 5 minut" i "czy zadziała po jailbreaku?") kliknąłem przycisk "Update" w iTunes.
Niby wszystko poszło ok: backup danych z telefonu, ściągnięcie nowej wersji systemu, instalacja i synchronizacja danych już z iOS 4. Po restarcie iPhone'a zaniepokoił mnie fakt, że zaczął do mnie mówić po angielsku. Gdy po wpisaniu PINu połączył się z siecią 3G zaniepokoiłem się trochę bardziej - w moich ustawieniach 3G było wyłączone. Na ekranie domowym co prawda były kupione aplikacje, ale w kolejności zdecydowanie losowej. Niedobrze. Kontakty - ok, ale to akurat się zsynchronizowało z systemową książką adresową. Historia SMSów - pusto. Aghrrr. W tym momencie zrobiłem się bardzo nerwowy. Niby nic takiego, ale jakoś tak mam że nie kasuję SMSów i nie chciałbym ich w tak głupi sposób stracić. Wykonanie "Restore" w iTunes (przywrócenie ustawień fabrycznych, a następnie odtworzenie danych z backupu) też nic nie dało - najwyraźniej backup robiony przed aktualizacją nie całkiem się udał, a iTunes nie uznał za stosowne poinformować o tym...
Kopia danych telefonu znajduje się w katalogu domowym w /Library/Application Support/MobileSync/Backup. Niestety przechowywana jest tylko ostatnio zrobiona, a więc nie na wiele się zdała. I nagle - one blinding flash of sense! Trzeba wyciągnąć backup z backupu tzn. z Time Machine. Tutaj muszę sobie pogratulować zapobiegliwości: przed aktualizacją telefonu kliknąłem "Backup Now" w Time Machine ot tak, na wszelki wypadek. Dzięki temu miałem zachowany jak najbardziej aktualny stan telefonu jeszcze z systemem 3.1.3. Po skasowaniu uszkodzonej kopii w katalogu ../Backup, nadpisaniu jej wersją z Time Machine i wykonaniu "Restore" (jestem genialny! mój plan zadziałał! jestem wybrańcem! no chodź, robimy performance!) iPhone przywitał się po polsku i cała zawartość była na swoim miejscu. Czego i Państwu życzę.

2010-05-26

Amazon bardziej dostępny w Polsce

Dostałem wczoraj mailing z Amazona informujący o rozszerzeniu grupy towarów wysyłanych do Polski. Oczywiście najbardziej zainteresowała mnie elektronika - kliknąłem w link "Learn more" i trafiłem na stronę "Super Saver Delivery in the UK". Trochę nie na temat. Jednak po sprawdzeniu w tabelkach "Delivery Rates" dla naszego regionu okazało się [tada!], że rzeczywiście dla polaków już są dostępne kategorie "PC" i "Electronics", przy czym koszty przesyłki nie są wyższe niż były dotąd dla książek. Ponieważ ceny w angielskim Amazonie zawierają angielski VAT a koszt przesyłki nie zawiera VATu wcale, to najprostszym sposobem na sprawdzenie rzeczywistej ceny jest wrzucenie towaru do koszyka i kliknięcie na "Proceeed to Checkout".
Jako że trochę zastanawiam się nad wymianą notebooka, to sprawdziłem MacBooka Pro 13" (model MC374) i 15" (MC375). Z przesyłką i VATem ten pierwszy kosztuje 970 GBP a drugi 1166 GBP, co przy dzisiejszym kursie funta w mBanku daje odpowiednio 4823 i 5797 PLN. Oficjalne ceny to 4999 i 6499 PLN, więc rzeczywiście jest taniej (w przypadku 13" nie jakoś powalająco, ale jednak), a gdyby funt wrócił do poziomu sprzed dwóch miesięcy to byłoby rewelacyjnie. Na pohybel polskim dystrybutorom Apple.

2010-05-11

Emoji w iPhone

Jednym z efektów ubocznych niedawnej imprezy rodzinnej była wymiana doświadczeń z kuzynem będącym - szczęśliwym oczywiście - posiadaczem iPhone. Dowiedziałem się m. in. jak włączyć klawiaturę zawierającą japońskie emoticony emoji. Jestem pewien, że jest to jedna z najbardziej pożądanych funkcji iPhone'a więc niniejszym dzielę się instrukcją How to enable emoji emoticons on your iPhone. Sposób jest iście magiczny, ale działa co widać na obrazku poniżej.

BTW: jak można przeczytać w Wikipedii Apple i Google proponują dodanie emoji jako części standardu Unicode. Dokument ma nieco ponad rok i nie został jeszcze zatwierdzony. Zapewne szacowne Konsorcjum Unicode zastanawia się czy aby na pewno świat potrzebuje standaryzacji symboli typu "woman with bunny ears" (kod 1f356).
[aktualizacja 2010.05.12]
Obrazek powyżej chyba nie oddaje całej powagi sytuacji. Oto przegląd wszystkich emoji dostępnych w iPhone (po kliknięciu otwiera się w pełnej rozdzielczości).

2010-05-03

Novatron NTR 81

Gdy liczba poniewierających się w okolicy telewizora płyt z filmami dla dzieci osiągnęła liczbę kilkudziesięciu pomyślałem "ja to muszę skomasować" i zacząłem przegląd dostępnych na polskim rynku NMT. Wcześniej słyszałem tylko o dwóch: Popcorn Hour i Xtreamer, ale okazało się że wybór jest znacznie szerszy. Większość tego typu urządzeń jest stosunkowo nowa i produkowana przez mało znane firmy, więc można się spodziewać mniejszych lub większych niedoróbek. Dlatego ważnym kryterium przy wyborze była dostępność na miejscu - za stary jestem na zabawę w reklamacje sprzętu kupionego wysyłkowo. We Wrocławiu dwie firmy specjalizują się w NMT: Human Media i iCroon, Human Media ma bogatszą ofertę i postanowiłem wybrać coś u nich. Z bardziej zaawansowanych urządzeń najlepszy wydaje się Popcorn Hour C200, ale po pierwsze jego cena jest dość wysoka a po drugie wygląda dość siermiężnie (a nie mam możliwości umieszczenia odtwarzacza w szafce na przykład). Niedrogi i o przyzwoitych możliwościach jest Xtreamer, ale dla mnie zdyskwalifikował go brak wyjścia component (jedyne HDMI w telewizorze mam zajęte przez PS3). Ostatecznie wybór padł na NTR Novatrona. Poszczególne modele 81, 82 i 83 różnią się tylko obudową, a ponieważ najbardziej pasowała mi pionowa kupiłem NTR 81.
Po kilku dniach użytkowania wrażenia są pozytywne, ale nie jest idealnie.
+ Bardzo dobra jakość obrazu, nawet przy podłączeniu przez kabel composite filmy HD wyglądają wyraźnie lepiej niż DVD.
+ Wygodny i estetyczny interfejs. Zarówno część służąca do przeglądania i odtwarzania filmów, jak i konfiguracyjna są logicznie zaplanowane i działają szybko i stabilnie.
+ Wygodny pilot, szczególnie porównując z tymi które są dodawane do odtwarzaczy innych firm.
- Wentylator jest zdecydowanie za głośny. Wieczorem, przy zamkniętych oknach słuchać go bardzo wyraźnie. W czasie filmu brzęczenie trochę ginie, ale w cichszych scenach jednak jest słyszalne. Być może to kwestia tego konkretnego egzemplarza - postaram się porównać w sklepie z innym.
- Pilot wymaga dokładnego celowania - wystarczy go odchylić (szczególnie w pionie) i przestaje reagować.
- Serwer FTP jest zbyt wolny: transfer (przez gigabitowy router) do NTRa to 5.7 MB/s, z NTRa 6.4 MB/s. Dodatkowo w czasie kopiowania reakcja na naciśnięcie klawiszy pilota jest wyraźnie opóźniona, choć na odtwarzanie transfer w tle nie ma wpływu.
- Drobne błędy przy obsłudze obrazów ISO - podświetlenie opcji menu DVD niekiedy jest w niewłaściwym miejscu.
Nie sprawdzałem jeszcze nagrywania - we Wrocławiu nie ma telewizji w standardzie DVB-T. Pewnie dla przetestowania spróbuję kiedyś podłączyć Wii przez wejście composite, ale tak można nagrywać tylko w rozdzielczości PAL.
Podsumowując: jest dobrze, odtwarzacz jak najbardziej spełnia funkcje do których jest przeznaczony, a do pełni szczęścia brakuje cichszego chłodzenia. Panowie w Human Media wydają się chętni do współpracy, więc liczę na to że gdyby trafił się mniej hałaśliwy egzemplarz to uda się wymienić.
[aktualizacja 2010.05.06]
+ Pomimo że w instrukcji nie ma nic na ten temat NTR 81 obsługuje Makowy system plików. Przydatne bo dysk zewnętrzny mam sformatowany w HFS właśnie.

2010-03-12

Mała rzecz a cieszy

Consuetudo est altera natura(*), czego doświadczyłem po przesiadce w pracy na Linuksa. Ubuntu zachowuje się (i po instalacji odpowiednich tematów wygląda) na tyle podobnie do Mac OSa, że kilka razy złapałem się na tym iż szukam przycisku zamykającego okno po lewej stronie. Uznałem, że łatwiej będzie zmienić konfigurację niż przyzwyczajenia, ale ani w "Window Preferences", ani w żadnym innym panelu konfiguracyjnym nie znalazłem stosownej opcji. Pozostało pogrzebanie w ustawieniach zarządzanych przez GConf i nadzieja, że umiejscowienie przycisków jest w ogóle konfigurowalne. Uruchamiamy w terminalu:
gconf-editor
i szukamy ustawień związanych z window managerem Metacity. Tada! Jest parametr:
apps/metacity/general/button_layout
Wystarczy wpisać tam:
close,minimize,maximize
i przyciski na ramce okna w końcu są we właściwym miejscu.
(*) ależ ja jestem wykształcony, prawda?

2010-02-19

Dlaczego trochę mniej lubię Hibernate

Używając Hibernate nigdy nie trafiłem na ewidentny bug. Owszem zdarzało się walczyć z oporem materii (głównie chodziło o zasięg sesji i LazyInitializationException), ale koniec końców okazywało się że biblioteka zachowuje się tak jak powinna się zachowywać, a błąd robiłem ja.
Dlatego, gdy po próbie zamapowania encji na dwie tabele, zobaczyłem:
org.hibernate.AssertionFailure: Table ... not found
zacząłem szukać ewentualnych literówek, brakujących adnotacji czy błędów w konfiguracji. I nic, absolutnie nic nie znalazłem. Pozostało - oczywiście - zapytać Google. Dość szybko trafiłem na zgłoszenie ANN-376 dotyczące Hibernate Annotations: nie można dodać drugiej tabeli do klasy, która jest częścią hierarchii zamapowanej z opcją InheritanceType.JOINED. A dokładnie to w sytuacji gdy mamy tabele powiązane kluczami obcymi:
CREATE TABLE A(
    INT id PRIMARY KEY NOT NULL
)

CREATE TABLE B(
    INT id NOT NULL,
    VARCHAR(100) b
)
ALTER TABLE B ADD CONSTRAINT FK1 FOREIGN KEY (id) REFERENCES A

CREATE TABLE C(
    INT id NOT NULL,
    VARCHAR(100) c
)
ALTER TABLE C ADD CONSTRAINT FK2 FOREIGN KEY (id) REFERENCES A
i próbujemy na nie zamapować klasy:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@SecondaryTable(name = "C")
public class A {
    @Id
    Long id;

    @Column(table = "C")
    String c;
}

@Entity
public class B extends A {
    String b;
}
dostajemy wyjątek:
org.hibernate.AssertionFailure: Table C not found
Co ciekawe, wystarczy zmiana sposobu dziedziczenia (i struktury tabel) na InheritanceType.TABLE_PER_CLASS lub InheritanceType.SINGLE_TABLE i wszystko jest ok.
Niestety, w moim przypadku takie rozwiązanie było nie do przyjęcia - system już działał, w tabelach były dane i zmiana struktury bazy nie wchodziła w rachubę. Ani w wspomnianym wcześniej zgłoszeniu, ani w powiązanych z nim HHH-4240 i HHH-2053 nikt nie opisał sensownego obejścia, które nie wymagałoby modyfikacji bazy. Ostatecznie zrobiłem tak:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class A {
    @Id
    Long id;
}

@Entity
public abstract class C extends A {
    String c;
}

@Entity
public class B extends C {
    String b;
}
Niezbyt ładne, ale skuteczne (klasa C jest abstrakcyjna aby nikomu nie przyszło do głowy tworzenie jej instancji).
Zgłoszenia w Hibernate'owej JIRZe(*) związane z tym błędem nie są przypisane do żadnej konkretnej wersji, więc będą poprawione w bliżej nieokreślonej przyszłości. A najstarsze z nich zostało zarejestrowane 20. czerwca 2006 roku.
(*) tak swoją drogą, jak należy odmieniać nazwę JIRA?
© The Useful Pot To Keep Things In
Maira Gall