FetchType, SessionFactory

Postanowiłem zmniejszyć obciążenie bazy danych. Za każdym razem jak szło zapytanie do bazy (np chciałem pobrać wszystkie choroby – Disease.class) to JPA puszczał też zapytanie dotyczące innych typów nieprostych – np listy leków List, które są przypisane do tej choroby. Działo się tak bo w deklaracji encji ustawione było


@ManyToMany(fetch = FetchType.EAGER)
private List<Medicament> medicaments;

ale dzięki temu jak przekazałem obiekt do widoku do jsp to mogłem w łatwy sposób dostać się do leków przypisanych do choroby


<c:forEach items="${disease.medicaments}" var="medicament">
	${medicament.name}
</c:forEach>

W momencie jak chcę wyświetlić choroby i nic więcej to nie ma potrzeby puszczać tylu zapytać do bazy. Więc zmieniłem FetchType na LAZY. Spowodowało to, że JPA puszcza zapytanie dotyczące obiektu który jest w zapytaniu.

JPA niestety uniemożliwia zmianę FetchType w trakcie działania, np encja ma ustawione LAZY ale puszczam zapytanie z EAGER – przynajmnije nie mogłem znaleźć informacji na ten temat.

Stwierdziłem więc, że w JPA pobiorę dwa obiekty i wrzucę je do tablicy, a tę tablicę do listy


@Query("select d, m from Disease d LEFT OUTER JOIN d.medicaments m")
List<Object[]> findWithMedicaments();

Poszło jedno zapytanie do bazy! Teraz mogę wyciągnąć z tablicy obiekty Dosease i Medicament


List<Object[]> diseasesObjects = diseaseRepository.findWithMedicaments();
for (Object objects[] : diseasesObjects) {
	Disease disease = (Disease) objects[0];
	if(objects[1] != null)
	{
		Medicament medicament = (Medicament) objects[1];
		disease.addMedicament(medicament);
	}
}

ale niestety rzuca błedem:

Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: pl.tomo.entity.Disease.medicaments, could not initialize proxy - no Session

Pomyślałem, że utworzę drugą listę w Disease

@ManyToMany(fetch = FetchType.LAZY)
private List<Medicament> medicaments;	//pierwsza lista
@Transient
private List<Medicament> medicaments2;	//druga lista

i zmienię metodę addMedicament żeby wrzucała Disease do listy która jest Transient(nie jest w bazie danych). I to działa!

Następnie pomyślałem, że na pewno musi istnieć jakiś sposób żeby puścić zapytanie i dostać powiązane obiekty, mimo ustawienia FetchType.LAZY. Z pomocą przyszedł stackoverflow

Dodałem beana:

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
		<property name="packagesToScan" value="pl.tomo.entity" />
	</bean>

Wstrzyknąłem go do serwisu

@Service
public class DiseaseService {
	@Autowired
	private SessionFactory sessionFactory;
	public void test(){
		Session openSession = sessionFactory.openSession();
		Query query = openSession.createQuery("Select d from Disease d");
		List<Disease> list = query.list();
		for (Disease disease : list) {
			Hibernate.initialize(disease.getMedicaments());
		}
		openSession.close();
}

List list = query.list() puszcza Hibernate: select disease…
Hibernate.initialize(disease.getMedicaments()) puszcza dla każdej pozycji z listy Hibernate: select medicament…

Reklamy

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s