Active Record z Google Guice i JPA cześć I
Ostatnie kilka wpisów było poświęcone zabawie z Guice, Assisted Injection i JPA. Nie może to się skończyć inaczej niż jakimś widowiskowym podsumowaniem 😉
Na początek
Elementów API Guice nie można teoretycznie wstrzykiwać… nie dotyczy to injectora. Warto o tym pamiętać. Tak samo jak o tym, że injector najlepiej wstrzykiwać jako pole statyczne.
Wymagania co do encji
Łatwo w życiu nie ma. W mojej wersji Active Record klasa encji musi spełniać kilka warunków. Niektóre są prostsze do spełnienia, niektóre wymagają trochę pisania.
Encja oznaczona jest @Entity
LOL…
Encja ma @Id
Nic nie narzucam wymagań co do typu identyfikatora. Zamiast @Id można użyć @EmbeddedId
Encja ma prywatny konstruktor bezparametrowy
Jako, że posiadanie konstruktora bezparametrowego jest wymogiem JPA… Dodatkowo konstruktor powinien być prywatny by nie korciło ręczne tworzenie obiektów.
Encja ma prywatny konstruktor z jednym parametrem
Typ parametru musi być zgodny z typem identyfikatora. Inaczej mówiąc encja posiada prywatny konstruktor w którym ustawiany jest identyfikator. Dodatkowo parametr musi być oznaczony adnotacją @Assisted(„id”). Konstruktor musi być oznaczony @AssistedInject.
Encja ma przypisany listener GuiceJPAListener
Encja musi być oznaczona @EntityListeners(value = { GuiceJPAListener.class }). Podziękujcie Wojtkowi Ebertowskiemu, który mi przypomniał o listenerach encji.
Encja musi mieć wewnętrzny interfejs rozszerzający EntityFactory
Coś czego nie jestem na chwilę obecną przeskoczyć.
Encja rozszerza SelfManagedEntity
W tej super klasie są zdefiniowane metody CRUD.
Przykładowa encja
Poniżej znajdziecie przykładową encję, która spełnia wszystkie warunki:
Listing 1. Przykładowa encja
@Entity
@EntityListeners(value = { GuiceJPAListener.class })
public class SimpleUser extends SelfManagedEntity<simpleuser> {
interface Factory extends EntityFactory<simpleuser string=""> {
}
@Id
private String name;
private String password;
private SimpleUser() {
}
@AssistedInject
private SimpleUser(@Assisted("id") String name) {
this.name = name;
}
public String name() {
return name;
}
public SimpleUser password(String newPassword) {
password = newPassword;
return this;
}
public String password() {
return password;
}
public static SimpleUser get(String name) {
return lookFor(SimpleUser.class, name);
}
}
</simpleuser></simpleuser>
Encja może dodatkowo zdefiniować metodę get, która będzie opakowywać metodę lookFor z nadklasy.
Listener i konfiguracja modułu
Zaczniemy od przygotowania dwóch najprostszych elementów. Listenera nasłuchującego tworzenie encji, którego zadaniem będzie wstrzykiwanie zależności do encji tworzonych przez ORM. Moduły guice, który będzie ogarniał temat konfiguracji.
Listing 2. Klasa GuilceJPAListener
public class GuiceJPAListener {
@Inject
private static Injector injector;
@PostLoad
public void postLoad(Object o){
injector.injectMembers(o);
}
}
Jak widać nie jest to nic skomplikowanego. I na całe szczęście.
Listing 3. Konfiguracja modułu
public class MyModule extends AbstractModule {
@Override
protected void configure() {
install(new JpaPersistModule("test-pu"));
bind(EntityCache.class).in(Scopes.SINGLETON);
installEntity(AIO.class, AIO.AIOFactory.class);
installEntity(SimpleUser.class, SimpleUser.Factory.class);
requestStaticInjection(SelfManagedEntity.class);
requestStaticInjection(GuiceJPAListener.class);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void installEntity(Class entityClass, Class entityClassFactory) {
install(new FactoryModuleBuilder().implement(entityClass, entityClass).build(entityClassFactory));
}
}
Tu mamy lekki hardcore ponieważ całość jest jeszcze nie do użytku zewnętrznego 🙂 Jak widać dodanie klasy encji do mechanizmu AR sprowadza się do wywołania jednej metody.
Na teraz starczy. Reszta wieczorem.