3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.api
.service
;
12 import java
.util
.Arrays
;
13 import java
.util
.List
;
14 import java
.util
.UUID
;
16 import org
.apache
.log4j
.Logger
;
17 import org
.hibernate
.LazyInitializationException
;
18 import org
.junit
.Assert
;
19 import org
.junit
.Ignore
;
20 import org
.junit
.Test
;
21 import org
.unitils
.database
.annotations
.Transactional
;
22 import org
.unitils
.database
.util
.TransactionMode
;
23 import org
.unitils
.dbunit
.annotation
.DataSet
;
24 import org
.unitils
.spring
.annotation
.SpringBeanByType
;
26 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
27 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
28 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
29 import eu
.etaxonomy
.cdm
.model
.name
.BotanicalName
;
30 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
31 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
32 import eu
.etaxonomy
.cdm
.test
.integration
.CdmIntegrationTest
;
35 * This test class tries to describe how hibernate works with objects (save, update, merge).
42 public class HandlingCdmEntitiesTest
extends CdmIntegrationTest
{
44 private static final Logger logger
= Logger
.getLogger(CommonServiceImplTest
.class);
46 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";
47 private static final String LIE_NOSESSION
= "could not initialize proxy - no Session";
50 private ITaxonService taxonService
;
53 private IAgentService agentService
;
56 private ICommonService commonService
;
58 public static final String
[] includeTables
= new String
[]{
62 "AGENTBASE_AGENTBASE",
68 @Transactional(TransactionMode
.DISABLED
)
69 public final void createTestDataSet() {
70 Team combAuthor
= Team
.NewTitledInstance("Avengers", "Avengers");
71 combAuthor
.addTeamMember(Person
.NewTitledInstance("Iron Man"));
72 BotanicalName name
= BotanicalName
.NewInstance(null, "Abies alba", null, null, null, null, null, null, null);
73 name
.setCombinationAuthorTeam(combAuthor
);
74 Taxon taxon
= Taxon
.NewInstance(name
, null);
75 UUID taxonUuid
= taxonService
.save(taxon
);
76 printDataSetWithNull(System
.out
,false,null,includeTables
);
81 @Transactional(TransactionMode
.DISABLED
)
82 public void testNonTransactionalUpdateForExistingTaxon() {
83 // this method tests the updating of a 'truely' detached object which
84 // attempts to initialize a lazy loaded proxy object while trying to
87 // setting the TransactionMode for this method to DISABLED is important
88 // to ensure that transaction boundaries remain at the service layer calls
89 // to simulate detachment and update of the persisted object
91 UUID taxonUuid
= UUID
.fromString("23c35977-01b5-452c-9225-ecce440034e0");
93 // ---- loading taxon with find (uuid) ----
95 Taxon taxon
= (Taxon
)taxonService
.find(taxonUuid
);
97 // at this point the taxonNew object is detached and all lazy loaded proxy
98 // objects in the object graph (including teamMembers) will have values of
99 // initialized=false and session=null
101 // since name is lazy loaded the call to getName should fail
103 CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
104 Assert
.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
105 } catch(LazyInitializationException lie
) {
107 if(!lie
.getMessage().equals(LIE_NOSESSION
)) {
108 Assert
.fail("LazyInitializationException thrown, but not : " + LIE_NOSESSION
);
112 // ---- loading taxon with find (id) ----
114 taxon
= (Taxon
)commonService
.find(taxon
.getClass(), taxon
.getId());
116 // at this point the taxonNew object is detached and all lazy loaded proxy
117 // objects in the object graph (including teamMembers) will have values of
118 // initialized=false and session=null
120 // since name is lazy loaded the call to getName should fail
122 CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
123 Assert
.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
124 } catch(LazyInitializationException lie
) {
126 if(!lie
.getMessage().equals(LIE_NOSESSION
)) {
127 Assert
.fail("LazyInitializationException thrown, but not : " + LIE_NOSESSION
);
131 // ---- loading taxon with findTaxonByUuid ----
133 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
137 // loading the taxon with its taxon name object pre-initialized
138 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
140 // at this point the taxonNew object is detached and all lazy loaded proxy
141 // objects in the object graph (including teamMembers) will have values of
142 // initialized=false and session=null
144 NonViralName nvn
= CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
146 // normally this call should throw a lazy loading exception since
147 // the combinationAuthorTeam object is not initialized, but
148 // the findTaxonByUuid performs this initialization (via the
149 // AdvancedBeanInitializer) since the combinationAuthorTeam
150 // is annotated with CacheUpdate
152 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorTeam(),Team
.class);
154 // setting the protected title cache to false to ensure that
155 // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
156 // to initialize the teamMembers persistent collection which fails
158 team
.setProtectedTitleCache(false);
161 taxonService
.update(taxon
);
162 Assert
.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
163 } catch(LazyInitializationException lie
) {
165 if(!lie
.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION
)) {
166 Assert
.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION
);
170 // the above fails due to the fact that hibernate does not resolve lazy
171 // loading on a detached object until the object is persisted. The attempt
172 // to initialize teamMembers before the object graph is persisted means that
173 // the current session is not yet attached to the proxy objects and hibernate
174 // tries to use the existing session set in the teamMembers object which is
175 // null, leading to the exception
177 // setting the protected title cache to true to ensure that
178 // TeamDefaultCacheStrategy.getTitleCache is not called, implying
179 // that the teamMembers are not initialized, so no exception is thrown
181 team
.setProtectedTitleCache(true);
182 taxonService
.update(taxon
);
188 public void testTransactionalUpdateAfterFindTaxonByUuidForExistingTaxon() {
189 // this method tests the updating of a detached object inside a single
192 // this method is transactional, meaning that a transaction is started
193 // at the start of the method and ends only with the end of the method
195 // since this method is transactional, any object initialized within this method
196 // will have a valid session attached to any lazy loaded proxy objects
197 // in the object graph (including teamMembers)
199 UUID taxonUuid
= UUID
.fromString("23c35977-01b5-452c-9225-ecce440034e0");
201 // ---- loading taxon with find (uuid) ----
203 Taxon taxon
= (Taxon
)taxonService
.find(taxonUuid
);
205 // at this point the taxonNew object is detached and all lazy loaded proxy
206 // objects in the object graph (including teamMembers) will have a new
207 // session attached implying that all the following calls will succeed
209 NonViralName nvn
= CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
210 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorTeam(),Team
.class);
211 taxonService
.update(taxon
);
213 // ---- loading taxon with find (id) ----
215 taxon
= (Taxon
)commonService
.find(taxon
.getClass(), taxon
.getId());
217 // at this point the taxonNew object is detached and all lazy loaded proxy
218 // objects in the object graph (including teamMembers) will have a new
219 // session attached implying that all the following calls will succeed
221 nvn
= CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
222 team
= CdmBase
.deproxy(nvn
.getCombinationAuthorTeam(),Team
.class);
223 taxonService
.update(taxon
);
225 // ---- loading taxon with findTaxonByUuid ----
227 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
231 // loading the taxon with its taxon name object pre-initialized
232 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
234 nvn
= CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
235 team
= CdmBase
.deproxy(nvn
.getCombinationAuthorTeam(),Team
.class);
237 // since a valid session is now attached to teamMembers, forcing the
238 // initializing of the teamMembers (in TeamDefaultCacheStrategy.getTitleCache)
239 // by setting the protected title cache to false does not throw an exception
240 // because the teamMember persistent collection now has a valid session,
241 // which is used to initialize the persistent collection
243 team
.setProtectedTitleCache(false);
244 taxonService
.update(taxon
);
248 @Transactional(TransactionMode
.DISABLED
)
249 public void testNonTransactionalUpdateForNewTaxon() {
251 // this test is only to prove that the update problem occurs
252 // also for newly created objects (as expected)
254 // create / save new taxon with name and author team with team member
256 Team combAuthor
= Team
.NewTitledInstance("X-Men", "X-Men");
257 combAuthor
.addTeamMember(Person
.NewTitledInstance("Wolverine"));
260 BotanicalName name
= BotanicalName
.NewInstance(null, "Pinus Alba", null, null, null, null, null, null, null);
261 name
.setCombinationAuthorTeam(combAuthor
);
263 Taxon taxon
= Taxon
.NewInstance(name
, null);
265 UUID taxonUuid
= taxonService
.save(taxon
);
267 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
271 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
273 NonViralName nvn
= CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
274 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorTeam(),Team
.class);
275 team
.setProtectedTitleCache(false);
278 taxonService
.update(taxon
);
279 Assert
.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
280 } catch(LazyInitializationException lie
) {
282 if(!lie
.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION
)) {
283 Assert
.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION
);
291 @Transactional(TransactionMode
.DISABLED
)
292 public void testNonTransactionalMergeForExistingTaxon() {
293 // this method tests the updating of a 'truely' detached object
296 // setting the TransactionMode for this method to DISABLED is important
297 // to ensure that transaction boundaries remain at the service layer calls
298 // to simulate detachment and update of the persisted object
300 UUID taxonUuid
= UUID
.fromString("23c35977-01b5-452c-9225-ecce440034e0");
302 // ---- loading taxon with find (uuid) ----
304 Taxon taxon
= (Taxon
)taxonService
.find(taxonUuid
);
306 // at this point the taxonNew object is detached and all lazy loaded proxy
307 // objects in the object graph (including teamMembers) will have values of
308 // initialized=false and session=null
310 taxonService
.merge(taxon
);
312 // ---- loading taxon with find (id) ----
314 taxon
= (Taxon
)commonService
.find(taxon
.getClass(), taxon
.getId());
316 taxonService
.merge(taxon
);
318 // ---- loading taxon with findTaxonByUuid ----
320 List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[] {
324 // loading the taxon with its taxon name object pre-initialized
325 taxon
= (Taxon
)taxonService
.findTaxonByUuid(taxonUuid
, TAXON_INIT_STRATEGY
);
327 // at this point the taxonNew object is detached and all lazy loaded proxy
328 // objects in the object graph (including teamMembers) will have values of
329 // initialized=false and session=null
331 NonViralName nvn
= CdmBase
.deproxy(taxon
.getName(),NonViralName
.class);
333 // normally this call should throw a lazy loading exception since
334 // the combinationAuthorTeam object is not initialized, but
335 // the findTaxonByUuid performs this initialization (via the
336 // AdvancedBeanInitializer) since the combinationAuthorTeam
337 // is annotated with CacheUpdate
339 Team team
= CdmBase
.deproxy(nvn
.getCombinationAuthorTeam(),Team
.class);
341 // setting the protected title cache to false to ensure that
342 // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
343 // to initialize the teamMembers persistent collection which fails
344 team
.setProtectedTitleCache(false);
347 taxonService
.merge(taxon
);