Tag Archives: Hibernate

Hibernate SQL logging

If you need to log SQL queries generated by Hibernate together with parameter values you do not need P6Spy. It is enough to set-up log4j or your favorite logging framework like this


log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.type=TRACE

Then you will see everything in your log. For example:


7820 DEBUG [org.hibernate.SQL] {main} - insert into Client (name, personalNumber, id) values (?, ?, ?)
7821 TRACE [org.hibernate.type.StringType] {main} - binding 'John Doe' to parameter: 1
7821 TRACE [org.hibernate.type.StringType] {main} - binding '123X' to parameter: 2
7821 TRACE [org.hibernate.type.LongType] {main} - binding '10' to parameter: 3

Hibernate: přistupovat pomocí polí nebo vlastností?

Onehdá jsem s kolegou řešil, jestli v Hibernate (JPA) používat přístup pomocí polí (field) nebo vlastností (property). Uvědomuji si, že nejsem ani první ani poslední kdo něco podobného řeší, nicméně mi došlo, že v tom sám nemám moc jasno tak jsem si dal za domácí úkol to nastudovat.

O co jde? Když mapuji entity, můžu si vybrat, jak bude Hibernate k objektům přistupovat. Mohu například napsat takovouto třídu.

@Entity
@Configurable
public class Client  {
	
	@Id @GeneratedValue(strategy=GenerationType.SEQUENCE)
	private Long id;

	@Column(unique=true,nullable=false)
	private String personalNumber;

	...
}

V takovémto případě bude při načítání z databáze Hibernate data zapisovat přímo do polí. To znamená, že například pro ID nemusíme mít vůbec set metodu, Hibernate nebo jiný JPA poskytovatel to tam magicky zapíše.

Nebo mohu zvolit druhou variantu.

@Entity
public class ClientGroup {
	
	private Long id;
	
	private List clients = new ArrayList();
	
	private String name;

	@Id @GeneratedValue(strategy=GenerationType.SEQUENCE)
	public Long getId() {
		return id;
	}

	private void setId(Long id) {
		this.id = id;
	}

	@OneToMany(cascade=CascadeType.ALL)
	public List getClients() {
		return clients;
	}

	
	private void setClients(List clients) {
		this.clients = clients;
	}
	public void addClient(Client client)
	{
		clients.add(client);
	}
	...
}

V tomto případě jsem dal anotace nad get metody a Hibernatu jsem tím dal najevo, že má pro přístup používat vlastnosti (property). Tzn. při plnění instancí z databáze bude volat set metody, při ukládání do databáze bude používat hodnoty z get metod.

Protože tu tvrdošíjně používám českou terminologii, shrnu to do slovníku.
Field access – přístup pomocí polí – anotace nad poli
Property access – přístup pomocí vlastností – anotace nad get metodami

Dobrá zpráva je, že v jednom programu mohu používat oba přístupy. Jednu třídu mohu mapovat tak, druhou onak. Sice bych to moc nedoporučoval, ale teoreticky to jde. U jednotlivých tříd ale oba přístupy míchat nemůžeme. Nemohu si říci, že ID chci mapovat jako field a seznam klientů jako property. Specifikaci už si nepamatuji, ale v praxi to funguje tak, že se typ přístupu určuje podle toho, nad co dám anotaci @Id.

Takže tu máme dva různé přístupy a nabízí se otázka, který je vhodnější. Oba mají svá pro a proti. Já osobně preferuji přístup přes pole. Prostě se smířím s tím, že mi Hibernate magicky mění vnitřnosti objektu. Podle některých to porušuje zapouzdření. Částečně mají tito lidé pravdu, ale tento přístup je podle mě menším ze dvou zel. Radši se smířím s tím, že mi zapouzdření bude narušovat Hibernate, než abych se smířil s tím, že mi ho bude narušovat programátor.

Vezměme si příklad ID. To je obvykle nastavováno Hibernatem. Rozhodně nechci, aby mi ho nastavoval nebo měnil programátor. V případě přístupu přes pole jednoduše nevytvořím set metodu a mám hotovo. V případě přístupu přes vlastnost musím vytvořit set metodu. Může být klidně i private, ale musí tam být. Takže stejně musím nechat Hibernate, aby mi prolomil zapouzdření. Stejně ho musím nechat volat private metodu. Nebo ji mohu nechat public, ale pak se musím modlit, aby mi někdo ID nezměnil. Mě osobně připadá čistší tam ten setter vůbec nedávat.

Podobný problém je i u kolekcí. Obvykle nechci nechat programátora, aby mi kolekci nastavoval přímo (viz. ClientGroup příklad). Ale u přístupu pomocí vlastností stejně musím mít set metodu, i když opět může být private.

Property přístup je podle mě také o trochu náchylnější na chybu. Představme si, že do objektu přidáme metodu getFullName, která jen zřetězí křestní jméno a příjmení. Když máme anotace nad poli, není tu žádný problém, pokud máme anotace nad metodami, tak si bude JPA myslet, že chceme mít v databázi sloupeček FULL_NAME a ošklivě nám vynadá. Budeme to muset vyřešit dobře mířenou anotací @Transient.

Specifickou kapitolou jsou netriviální get a set metody. V set metodách můžu mít validace vstupních hodnot, v get metodách vracet defenzivní kopie. U přístupu přes pole to není problém. Ale pokud používám přístup pomocí vlastností tak se mohou dít ošklivé věci. Validační kód se bude volat při každém načtení z databáze, což může mít na výkonnost dopad jen malý. Horší je to s defenzivními kopiemi. Ty mohou ovlivnit mechanismus detekce změn. Pokud budu například vracet defenzivní kopii kolekce, Hibernate bude přesvědčen, že se mu změnil obsah kolekce a pokaždé danou kolekci z databáze smaže a vloží ji tam znovu. A to i v situaci, že jsme danou instanci vůbec nezměnili! To může mít na výkonnost dopad dost ošklivý.

Teď mi zrovna došlo, v čem je problém. V podstatě obvykle potřebujeme, aby entita měla dvě rozhraní. Jedno pro programátory, druhé pro perzistenci. To je přesně to, co mi zajistí mapování přes pole. Říkám tím, že ORM je taková chytřejší serializace po které chci aby mi uložila stav objektu. U klasické Java serializace si taky nikdo nestěžuje, že narušuje zapozdření tím, že nastavuje pole napřímo. Při přístupu přes vlastnosti na druhou stranu tvrdím, že ORM a programátor jsou si rovni a že by měli používat stejné rozhraní. Někdy to tak být může, ale obvykle tomu tak není.

Předchozí věta by tvořila pěkný závěr článku, naneštěstí ale ještě končit nemůžu. Je jeden konkrétní případ, kdy jsou anotace nad metodami lepší volba. Je to v případě, že načítáme ID u lazy proxy, které ještě není načtené z databáze. Řekněme, že dělám například toto:

clientGroup.getClient(0).getId()

Může se stát, že Hibernate zná jenom ID klientů, kteří patří do dané skupiny, ale ještě nemá načtená jejich data. V tom případe se používá proxy, která zajistí načtení dat, až když jsou potřeba. Když používám přístup pomocí polí, Hibernate musí všechny informace o klientovi načíst, i když mě zajímá jen ID. Neví totiž, že metoda getId() jenom vrací klíč. Neví jestli náhodou v této metodě nečtu třeba jméno. Nemá šanci to poznat. Teoreticky by se mohl podívat do kódu, ale to by už bylo příliš mnoho magie. Na druhou stranu pokud mám nad metodou getId() anotaci @Id, Hibernate ví, že to je ID a může mi ho rovnou vrátit, aniž by musel přistupovat do databáze.

Ale toto je jediný případ, v kterém je přístup přes vlastnosti lepší. Záleží případ od případu, jestli zrovna tuto vlastnost použiji. Dovedu si představit aplikaci, kde budu například ID používat i v UI pro odkazy v tabulce. Dovoluji si ale tvrdit, že i tak by bylo lepší použít nějakou lepší fetch strategii než se prát s anotacemi nad metodami. Jinak to totiž zavání předčasnou optimalizací.

Další čtení:
http://forum.hibernate.org/viewtopic.php?p=2349555&sid=dd846f97d3eb352ed0b8fbe57739801c

http://chstath.blogspot.com/2007/05/field-access-vs-property-access-in-jpa.html

http://shashivelur.com/blog/2009/01/hibernate-direct-field-acess-and-encapsulation/

Spring managed Hibernate interceptor in JPA

I have been trying to teach Hibernate injecting dependencies into Entities (I know, there is magic @Configurable annotation, I wanted to try it without magic). It is quite easy to do it using Hibernate interceptor (for example like this). But there is one drawback. It is not straightforward to inject interceptor into Hibernate when JPA abstraction is in the way.

It is simple to define interceptor in persistence.xml using hibernate.ejb.interceptor property. But it is only possible to specify class name, you can not inject Spring bean. If you read documentation to Spring LocalContainerEntityManagerFactoryBean there is no possibility to specify the interceptor bean there neither. But there is a way how to achieve it. First of all, you have to redefine Hibernate PersistenceProvider.

public class ConfigurableHibernatePersistence extends HibernatePersistence {
	private Interceptor interceptor;
	public Interceptor getInterceptor() {
		return interceptor;
	}

	public void setInterceptor(Interceptor interceptor) {
		this.interceptor = interceptor;
	}

	@SuppressWarnings("unchecked")
	@Override
	public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
		Ejb3Configuration cfg = new Ejb3Configuration();
		Ejb3Configuration configured = cfg.configure( info, map );
		postprocessConfiguration(info, map, configured);
		return configured != null ? configured.buildEntityManagerFactory() : null;
	}

	@SuppressWarnings("unchecked")
	protected void postprocessConfiguration(PersistenceUnitInfo info, Map map, Ejb3Configuration configured) {
		if (this.interceptor != null)
		{
			if (configured.getInterceptor()==null || EmptyInterceptor.class.equals(configured.getInterceptor().getClass()))
			{
				configured.setInterceptor(this.interceptor);
			}
			else
			{
				throw new IllegalStateException("Hibernate interceptor already set in persistence.xml ("+configured.getInterceptor()+")");
			}
		}
	}
}

(source)

Here we override method createContainerEntityManagerFactory in order to add postprocessConfiguration method call. In this method, it is possible to change Hibernate configuration as needed. Now the only thing to be done is configuring Spring to use our new PersistenceProvider. It is quite simple.

	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="testPU" />
		<property name="dataSource" ref="dataSource" />
		<property name="persistenceProvider">
			<bean class="net.krecan.javacrumbs.jpa.interceptor.ConfigurableHibernatePersistence">
				<property name="interceptor">
					<bean class="net.krecan.javacrumbs.jpa.interceptor.SpringInjectingInterceptor"/>
				</property>
			</bean>
		</property>
	</bean>

And that all folks, we have Spring configured interceptor even when using JPA abstraction. It would be better if similar classes were in Spring or Hibernate, but it’s just few lines of code so feel free to copy them if you need to. If you know easier way how to do it, please mention it in the comments below.