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.