10 maja 2009

Java i Chuck Norris na wesoło

Dzisiaj podczas przeglądania newsów na DZone.com trafiłem na artykuł z żartami o Chucku Norrisie w kontekście programowania w Javie.

Poniżej te, które najbardziej przypadły mi do gustu :)

Chuck Norris can make a class that is both abstract and final.
Chuck Norris demonstrated the meaning of Float.POSITIVE_INFINITY by counting to it, twice.
A synchronize doesn’t protect against Chuck Norris, if he wants the object, he takes it.
Code runs faster when Chuck Norris watches it.
If you get a ChuckNorrisException you’ll probably die.
Java visibility levels are public, default, protected, private and “protected by Chuck Norris”, don’t try to access a field with this last modifier!!
Garbage collector only runs on Chuck Norris code to collect the bodies.
Chuck Norris can do multiple inheritance in Java.
Chuck Norris extends God.
Chuck Norris codes generics since 1.3.

Dla zainteresowanych całość do przeczytania tutaj.

Java Persistence API i prosty audyt

Niedawno po raz pierwszy zetknąłem się z zagadnieniem audytu zmian w bazie danych dokonywanych przez użytkowników aplikacji. Chwila googlania i mamy kilka rozwiązań, jedno z nich to ciekawy artykuł na JavaLobby: "Using a Hibernate Interceptor To Set Audit Trail Properties". Jednak istnieje też prostsze rozwiązanie, które chce tutaj przedstawić.

Jeśli korzystamy z Java Persistence API to możemy zastosować dwie bardzo przydatne adnotacje: @PrePersist i @PreUpdate. Załóżmy, że w każdej klasie chcemy mieć dane o tym kto i kiedy utworzył dany rekord oraz kto i kiedy go ostatnio modyfikował. Ponieważ wszystkie encje mają mieć taką funkcjonalność, logikę i zmienne możemy przenieść do klasy abstrakcyjnej, którą będą rozszerzały wszystkie klasy odwzorowujące:


import java.util.Date;
import javax.persistence.*;

@MappedSuperclass
public abstract class BaseEntity {

private String createdBy;
private Date createdOn;
private String modifiedBy;
private Date modifiedOn;

@Column(name="created_by")
public String getCreatedBy() {
return createdBy;
}

@Column(name="created_on")
@Temporal(TemporalType.TIMESTAMP)
public Date getCreatedOn() {
return createdOn;
}

@Column(name="modified_by")
public String getModifiedBy() {
return modifiedBy;
}

@Column(name="modified_on")
@Temporal(TemporalType.TIMESTAMP)
public Date getModifiedOn() {
return modifiedOn;
}

// tutaj powinny byc settery dla zmiennych

@PrePersist
public void prePersist() {
User user = ApplicationUtils.getLoggedUser();
setCreatedBy(user.getName());
setCreatedOn(new Date());
}

@PreUpdate
public void preUpdate() {
User user = ApplicationUtils.getLoggedUser();
setModifiedBy(user.getName());
setModifiedOn(new Date());
}

}

Metody oznaczone adnotacjami są wywoływane tuż przed zapisem obiektu w bazie danych, pozwalając na dodanie informacji o tym kto i kiedy modyfikował rekord.

Wszystkie adnotacje o zbliżonym działaniu znajdujące się w JPA API to PostLoad, PostPersist, PostRemove, PostUpdate, PrePersist, PreRemove i PreUpdate. Dzięki nim możemy być informowani o wszystkich operacjach zapisu/odczytu/usuwania odbywających się na obiektach z bazy danych. Może to być przydatne, gdy przykładowo takie informacje chcemy zapisywać w pliku logowania.

9 maja 2009

Eclipse i problem z generowanymi equals()

Ostatnio w pracy kolega natrafił na następujący problem:
Tabela JSF <rich:dataTable> do prezentacji korzystała z HashMapy z obiektami pobieranymi z bazy danych. Po zapisaniu nowych obiektów do bazy danych i odświeżeniu tabeli, na stronie pokazywały się dane zupełnie inne niż te, które znajdowały się w bazie. Po kilku godzinach bezskutecznych prób poskromienia JSF-a (bo myśleliśmy, że tam jest problem) okazało się, że przyczyna tkwi w zupełnie innym miejscu.

Otóż Eclipse generuje metodę equals() sprawdzając czy klasy porównywanych obiektów sa takie same:

...
іf (getClass() != obϳ.getClass()) {
return fаlse;
}
...

co w połączeniu z Hibernate może sprawić sporo kłopotów, bo często podczas wczytywania danych z bazy zamiast obiektu naszej klasy tworzony jest obiekt proxy rozszerzający klasę, którą wczytujemy. Przez co metoda powyżej zwróci false i obiekty w naszej HashMapie się nie odnajdą. Rozwiązaniem jest proste: należy stosować instanceof zamiast porównywania samych klas.

Dodatkowo, jak to jest opisane w komentarzu na blogu Proxorkut, jeśli stosujemy leniwe ładowanie, to w metodach equals i hashCode należy odwoływać się do zmiennych przez metody getXXX, a nie bezpośrednio, tak, aby Hibernate miał możliwość dociągnięcia z bazy brakujących danych.

Kilka skrótów klawiszowych w NetBeans, których możesz nie znać

Ten postał został przeniesiony na mojego nowego bloga i można go znaleźć tutaj.