2 * Copyright (C) 2014 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.api
.service
;
11 import java
.util
.Arrays
;
12 import java
.util
.List
;
13 import java
.util
.UUID
;
15 import org
.apache
.logging
.log4j
.LogManager
;
16 import org
.apache
.logging
.log4j
.Logger
;
17 import org
.hibernate
.LazyInitializationException
;
18 import org
.junit
.Assert
;
19 import org
.junit
.Test
;
20 import org
.unitils
.database
.annotations
.Transactional
;
21 import org
.unitils
.database
.util
.TransactionMode
;
22 import org
.unitils
.dbunit
.annotation
.DataSet
;
23 import org
.unitils
.spring
.annotation
.SpringBeanByType
;
25 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
26 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
27 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
28 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
29 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
30 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
31 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
32 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
33 import eu
.etaxonomy
.cdm
.model
.name
.IBotanicalName
;
34 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
35 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
36 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameFactory
;
37 import eu
.etaxonomy
.cdm
.model
.reference
.OriginalSourceType
;
38 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
39 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
40 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
41 import eu
.etaxonomy
.cdm
.test
.integration
.CdmIntegrationTest
;
44 * This test class tries to describe how hibernate works with objects (save, update, merge).
49 public class HandlingCdmEntitiesTest
extends CdmIntegrationTest
{
51 @SuppressWarnings("unused")
52 private static final Logger logger
= LogManager
.getLogger();
54 private static final String LIE_TEAMMEMBERS_NOSESSION
= "failed to lazily initialize a collection of role: eu.etaxonomy.cdm.model.agent.Team.teamMembers, could not initialize proxy - no Session";
55 private static final String LIE_NOSESSION
= "could not initialize proxy .* no Session";
57 private static final UUID taxonUuid
= UUID
.fromString("23c35977-01b5-452c-9225-ecce440034e0");
60 private IReferenceService referenceService
;
63 private IAnnotationService annotationService
;
66 private ITaxonService taxonService
;
69 private IAgentService agentService
;
72 private ICommonService commonService
;
74 public static final String
[] includeTables
= new String
[]{
78 "AGENTBASE_AGENTBASE",
83 @Transactional(TransactionMode
.DISABLED
)
84 public final void createTestDataSet() {
85 Team combAuthor
= Team
.NewTitledInstance("Avengers", "Avengers");
86 combAuthor
.addTeamMember(Person
.NewTitledInstance("Iron Man"));
87 IBotanicalName name
= TaxonNameFactory
.NewBotanicalInstance(null, "Abies alba", null, null, null, null, null, null, null);
88 name
.setCombinationAuthorship(combAuthor
);
89 Taxon taxon
= Taxon
.NewInstance(name
, null);
90 taxonService
.save(taxon
).getUuid();
91 printDataSet(System
.out
,false,null,includeTables
);
96 @Transactional(TransactionMode
.DISABLED
)
97 public void testNonTransactionalUpdateForExistingTaxon() {
98 // this method tests the updating of a 'truely' detached object which
99 // attempts to initialize a lazy loaded proxy object while trying to
102 // setting the TransactionMode for this method to DISABLED is important
103 // to ensure that transaction boundaries remain at the service layer calls
104 // to simulate detachment and update of the persisted object
106 // ---- loading taxon with find (uuid) ----
108 Taxon taxon
= (Taxon
)taxonService
.find(taxonUuid
);
110 // at this point the taxonNew object is detached and all lazy loaded proxy
111 // objects in the object graph (including teamMembers) will have values of
112 // initialized=false and session=null
114 // since name is lazy loaded the call to getName should fail
116 CdmBase
.deproxy(taxon
.getName(), TaxonName
.class);
117 Assert
.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
118 } catch(LazyInitializationException lie
) {
119 assertIsLieNoSession(lie
);
122 // ---- loading taxon with find (id) ----
124 taxon
= commonService
.find(taxon
.getClass(), taxon
.getId());
126 // at this point the taxonNew object is detached and all lazy loaded proxy
127 // objects in the object graph (including teamMembers) will have values of
128 // initialized=false and session=null
130 // since name is lazy loaded the call to getName should fail
132 CdmBase
.deproxy(taxon
.getName(),TaxonName
.class);
133 Assert
.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
134 } catch(LazyInitializationException lie
) {
135 assertIsLieNoSession(lie
);
138 // ---- loading taxon with findTaxonByUuid ----
140 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
144 // loading the taxon with its taxon name object pre-initialized
145 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
147 // at this point the taxonNew object is detached and all lazy loaded proxy
148 // objects in the object graph (including teamMembers) will have values of
149 // initialized=false and session=null
151 TaxonName nvn
= CdmBase
.deproxy(taxon
.getName());
153 // normally this call should throw a lazy loading exception since
154 // the combinationAuthorship object is not initialized, but
155 // the findTaxonByUuid performs this initialization (via the
156 // AdvancedBeanInitializer) since the combinationAuthorship
157 // is annotated with CacheUpdate
159 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorship(),Team
.class);
161 // setting the protected title cache to false to ensure that
162 // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
163 // to initialize the teamMembers persistent collection which fails
165 team
.setProtectedTitleCache(false);
168 taxonService
.update(taxon
);
169 Assert
.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
170 } catch(LazyInitializationException lie
) {
172 if(!lie
.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION
)) {
173 Assert
.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION
);
177 // the above fails due to the fact that hibernate does not resolve lazy
178 // loading on a detached object until the object is persisted. The attempt
179 // to initialize teamMembers before the object graph is persisted means that
180 // the current session is not yet attached to the proxy objects and hibernate
181 // tries to use the existing session set in the teamMembers object which is
182 // null, leading to the exception
184 // setting the protected title cache to true to ensure that
185 // TeamDefaultCacheStrategy.getTitleCache is not called, implying
186 // that the teamMembers are not initialized, so no exception is thrown
188 team
.setProtectedTitleCache(true);
189 team
.setProtectedCollectorTitleCache(true);
190 team
.setProtectedNomenclaturalTitleCache(true);
191 taxonService
.update(taxon
);
195 private void assertIsLieNoSession(LazyInitializationException lie
) {
196 if(!lie
.getMessage().matches(LIE_NOSESSION
)) {
197 Assert
.fail("LazyInitializationException ("+lie
.getMessage()+") thrown, but does not match: " + LIE_NOSESSION
);
203 public void testTransactionalUpdateAfterFindTaxonByUuidForExistingTaxon() {
204 // this method tests the updating of a detached object inside a single
207 // this method is transactional, meaning that a transaction is started
208 // at the start of the method and ends only with the end of the method
210 // since this method is transactional, any object initialized within this method
211 // will have a valid session attached to any lazy loaded proxy objects
212 // in the object graph (including teamMembers)
214 // ---- loading taxon with find (uuid) ----
216 Taxon taxon
= (Taxon
)taxonService
.find(taxonUuid
);
218 // at this point the taxon object is detached and all lazy loaded proxy
219 // objects in the object graph (including teamMembers) will have a new
220 // session attached implying that all the following calls will succeed
222 INonViralName nvn
= CdmBase
.deproxy(taxon
.getName());
223 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorship(),Team
.class);
224 taxonService
.update(taxon
);
226 // ---- loading taxon with find (id) ----
228 taxon
= commonService
.find(taxon
.getClass(), taxon
.getId());
230 // at this point the taxon object is detached and all lazy loaded proxy
231 // objects in the object graph (including teamMembers) will have a new
232 // session attached implying that all the following calls will succeed
234 nvn
= CdmBase
.deproxy(taxon
.getName());
235 team
= CdmBase
.deproxy(nvn
.getCombinationAuthorship(), Team
.class);
236 taxonService
.update(taxon
);
238 // ---- loading taxon with findTaxonByUuid ----
240 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
244 // loading the taxon with its taxon name object pre-initialized
245 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
247 nvn
= CdmBase
.deproxy(taxon
.getName(),TaxonName
.class);
248 team
= CdmBase
.deproxy(nvn
.getCombinationAuthorship(), Team
.class);
250 // since a valid session is now attached to teamMembers, forcing the
251 // initializing of the teamMembers (in TeamDefaultCacheStrategy.getTitleCache)
252 // by setting the protected title cache to false does not throw an exception
253 // because the teamMember persistent collection now has a valid session,
254 // which is used to initialize the persistent collection
256 team
.setProtectedTitleCache(false);
257 taxonService
.update(taxon
);
261 @Transactional(TransactionMode
.DISABLED
)
262 public void testNonTransactionalUpdateForNewTaxon() {
264 // this test is only to prove that the update problem occurs
265 // also for newly created objects (as expected)
267 // create / save new taxon with name and author team with team member
269 Team combAuthor
= Team
.NewTitledInstance("X-Men", "X-Men");
270 combAuthor
.addTeamMember(Person
.NewTitledInstance("Wolverine"));
273 IBotanicalName name
= TaxonNameFactory
.NewBotanicalInstance(null, "Pinus Alba", null, null, null, null, null, null, null);
274 name
.setCombinationAuthorship(combAuthor
);
276 Taxon taxon
= Taxon
.NewInstance(name
, null);
278 UUID taxonUuid
= taxonService
.save(taxon
).getUuid();
280 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
284 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
286 INonViralName nvn
= CdmBase
.deproxy(taxon
.getName());
287 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorship(), Team
.class);
288 team
.setProtectedTitleCache(false);
291 taxonService
.update(taxon
);
292 Assert
.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
293 } catch(LazyInitializationException lie
) {
295 if(!lie
.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION
)) {
296 Assert
.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION
);
304 @Transactional(TransactionMode
.DISABLED
)
305 public void testNonTransactionalMergeForExistingTaxon() {
306 // this method tests the updating of a 'truely' detached object
309 // setting the TransactionMode for this method to DISABLED is important
310 // to ensure that transaction boundaries remain at the service layer calls
311 // to simulate detachment and update of the persisted object
313 // ---- loading taxon with find (uuid) ----
315 Taxon taxon
= (Taxon
)taxonService
.find(taxonUuid
);
317 // at this point the taxonNew object is detached and all lazy loaded proxy
318 // objects in the object graph (including teamMembers) will have values of
319 // initialized=false and session=null
321 taxonService
.merge(taxon
);
323 // ---- loading taxon with find (id) ----
325 taxon
= commonService
.find(taxon
.getClass(), taxon
.getId());
327 taxonService
.merge(taxon
);
329 // ---- loading taxon with findTaxonByUuid ----
331 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
335 // loading the taxon with its taxon name object pre-initialized
336 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
338 // at this point the taxonNew object is detached and all lazy loaded proxy
339 // objects in the object graph (including teamMembers) will have values of
340 // initialized=false and session=null
342 INonViralName nvn
= CdmBase
.deproxy(taxon
.getName());
344 // normally this call should throw a lazy loading exception since
345 // the combinationAuthorship object is not initialized, but
346 // the findTaxonByUuid performs this initialization (via the
347 // AdvancedBeanInitializer) since the combinationAuthorship
348 // is annotated with CacheUpdate
350 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorship(),Team
.class);
352 // setting the protected title cache to false to ensure that
353 // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
354 // to initialize the teamMembers persistent collection which fails
355 team
.setProtectedTitleCache(false);
358 taxonService
.merge(taxon
);
364 public final void testTaxonDescriptionMerge() {
366 IBotanicalName name
= TaxonNameFactory
.NewBotanicalInstance(null, "Abies alba", null, null, null, null, null, null, null);
367 Taxon taxon
= Taxon
.NewInstance(name
, null);
368 TaxonDescription description
= TaxonDescription
.NewInstance(taxon
);
370 TextData textData
= TextData
.NewInstance();
372 textData
.setFeature(Feature
.ECOLOGY());
373 description
.addElement(textData
);
375 DescriptionElementSource descriptionElementSource
= DescriptionElementSource
.NewInstance(OriginalSourceType
.PrimaryTaxonomicSource
);
377 textData
.addSource(descriptionElementSource
);
379 taxonService
.merge(taxon
);
382 @Test //testing of bidirectionality of supplemental data #5743
383 public final void testReferenceWithAnnotationMerge() {
385 Reference ref
= ReferenceFactory
.newBook();
387 ref
.addAnnotation(Annotation
.NewDefaultLanguageInstance("ref"));
389 referenceService
.merge(ref
);
392 @Test //testing of bidirectionality of supplemental data #5743
393 public final void testAnnotationMerge() {
395 Reference ref
= ReferenceFactory
.newBook();
397 Annotation annotation
= Annotation
.NewDefaultLanguageInstance("anno");
398 ref
.addAnnotation(annotation
);
400 annotationService
.merge(annotation
);