Project

General

Profile

Download (15.7 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2014 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
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.
8
 */
9
package eu.etaxonomy.cdm.api.service;
10

    
11
import java.util.Arrays;
12
import java.util.List;
13
import java.util.UUID;
14

    
15
import org.apache.log4j.Logger;
16
import org.hibernate.LazyInitializationException;
17
import org.junit.Assert;
18
import org.junit.Ignore;
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;
24

    
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.common.OriginalSourceType;
30
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
31
import eu.etaxonomy.cdm.model.description.Feature;
32
import eu.etaxonomy.cdm.model.description.TaxonDescription;
33
import eu.etaxonomy.cdm.model.description.TextData;
34
import eu.etaxonomy.cdm.model.name.IBotanicalName;
35
import eu.etaxonomy.cdm.model.name.INonViralName;
36
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
37
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
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;
42

    
43
/**
44
 * This test class tries to describe how hibernate works with objects (save, update, merge).
45
 *
46
 * @author cmathew
47
 * @date 17 Sep 2014
48
 *
49
 */
50

    
51
public class HandlingCdmEntitiesTest extends CdmIntegrationTest {
52

    
53
    @SuppressWarnings("unused")
54
	private static final Logger logger = Logger.getLogger(CommonServiceImplTest.class);
55

    
56
    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";
57
    private static final String LIE_NOSESSION = "could not initialize proxy - no Session";
58

    
59
    @SpringBeanByType
60
    private IReferenceService referenceService;
61

    
62
    @SpringBeanByType
63
    private IAnnotationService annotationService;
64

    
65
    @SpringBeanByType
66
    private ITaxonService taxonService;
67

    
68
    @SpringBeanByType
69
    private IAgentService agentService;
70

    
71
    @SpringBeanByType
72
    private ICommonService commonService;
73

    
74
    public static final String[] includeTables = new String[]{
75
        "TAXONBASE",
76
        "TAXONNAMEBASE",
77
        "AGENTBASE",
78
        "AGENTBASE_AGENTBASE",
79
        "HOMOTYPICALGROUP"
80
    };
81

    
82
    @Override
83
    @Ignore
84
    @Test
85
    @Transactional(TransactionMode.DISABLED)
86
    public final void createTestDataSet() {
87
        Team combAuthor = Team.NewTitledInstance("Avengers", "Avengers");
88
        combAuthor.addTeamMember(Person.NewTitledInstance("Iron Man"));
89
        IBotanicalName name = TaxonNameFactory.NewBotanicalInstance(null, "Abies alba", null, null, null, null, null, null, null);
90
        name.setCombinationAuthorship(combAuthor);
91
        Taxon taxon = Taxon.NewInstance(name, null);
92
        UUID taxonUuid = taxonService.save(taxon).getUuid();
93
        printDataSetWithNull(System.out,false,null,includeTables);
94
    }
95

    
96
    @Test
97
    @DataSet
98
    @Transactional(TransactionMode.DISABLED)
99
    public void testNonTransactionalUpdateForExistingTaxon() {
100
        // this method tests the updating of a 'truely' detached object which
101
        // attempts to initialize a lazy loaded proxy object while trying to
102
        // update the same.
103

    
104
        // setting the TransactionMode for this method to DISABLED is important
105
        // to ensure that transaction boundaries remain at the service layer calls
106
        // to simulate detachment and update of the persisted object
107

    
108
        UUID taxonUuid = UUID.fromString("23c35977-01b5-452c-9225-ecce440034e0");
109

    
110
        // ---- loading taxon with find (uuid) ----
111

    
112
        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
113

    
114
        // at this point the taxonNew object is detached and all lazy loaded proxy
115
        // objects in the object graph (including teamMembers) will have values of
116
        // initialized=false and session=null
117

    
118
        // since name is lazy loaded the call to getName should fail
119
        try {
120
            CdmBase.deproxy(taxon.getName(), TaxonNameBase.class);
121
            Assert.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
122
        } catch(LazyInitializationException lie) {
123

    
124
            if(!lie.getMessage().equals(LIE_NOSESSION)) {
125
                Assert.fail("LazyInitializationException thrown, but not : " + LIE_NOSESSION);
126
            }
127
        }
128

    
129
        // ---- loading taxon with find (id) ----
130

    
131
        taxon = (Taxon)commonService.find(taxon.getClass(), taxon.getId());
132

    
133
        // at this point the taxonNew object is detached and all lazy loaded proxy
134
        // objects in the object graph (including teamMembers) will have values of
135
        // initialized=false and session=null
136

    
137
        // since name is lazy loaded the call to getName should fail
138
        try {
139
            CdmBase.deproxy(taxon.getName(),TaxonNameBase.class);
140
            Assert.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
141
        } catch(LazyInitializationException lie) {
142

    
143
            if(!lie.getMessage().equals(LIE_NOSESSION)) {
144
                Assert.fail("LazyInitializationException thrown, but not : " + LIE_NOSESSION);
145
            }
146
        }
147

    
148
        // ---- loading taxon with findTaxonByUuid ----
149

    
150
        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
151
                "name"
152
        });
153

    
154
        // loading the taxon with its taxon name object pre-initialized
155
        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
156

    
157
        // at this point the taxonNew object is detached and all lazy loaded proxy
158
        // objects in the object graph (including teamMembers) will have values of
159
        // initialized=false and session=null
160

    
161
        INonViralName nvn = CdmBase.deproxy(taxon.getName(),TaxonNameBase.class);
162

    
163
        // normally this call should throw a lazy loading exception since
164
        // the combinationAuthorship object is not initialized, but
165
        // the findTaxonByUuid performs this initialization (via the
166
        // AdvancedBeanInitializer) since the combinationAuthorship
167
        // is annotated with CacheUpdate
168

    
169
        Team team = CdmBase.deproxy(nvn.getCombinationAuthorship(),Team.class);
170

    
171
        // setting the protected title cache to false to ensure that
172
        // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
173
        // to initialize the teamMembers persistent collection which fails
174

    
175
        team.setProtectedTitleCache(false);
176

    
177
        try {
178
            taxonService.update(taxon);
179
            Assert.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
180
        } catch(LazyInitializationException lie) {
181

    
182
            if(!lie.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION)) {
183
                Assert.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION);
184
            }
185
        }
186

    
187
        // the above fails due to the fact that hibernate does not resolve lazy
188
        // loading on a detached object until the object is persisted. The attempt
189
        // to initialize teamMembers before the object graph is persisted means that
190
        // the current session is not yet attached to the proxy objects and hibernate
191
        // tries to use the existing session set in the teamMembers object which is
192
        // null, leading to the exception
193

    
194
        // setting the protected title cache to true to ensure that
195
        // TeamDefaultCacheStrategy.getTitleCache is not called, implying
196
        // that the teamMembers are not initialized, so no exception is thrown
197

    
198
        team.setProtectedTitleCache(true);
199
        taxonService.update(taxon);
200

    
201
    }
202

    
203
    @Test
204
    @DataSet
205
    public void testTransactionalUpdateAfterFindTaxonByUuidForExistingTaxon() {
206
        // this method tests the updating of a detached object inside a single
207
        // transaction.
208

    
209
        // this method is transactional, meaning that a transaction is started
210
        // at the start of the method and ends only with the end of the method
211

    
212
        // since this method is transactional, any object initialized within this method
213
        // will have a valid session attached to any lazy loaded proxy objects
214
        // in the object graph (including teamMembers)
215

    
216
        UUID taxonUuid = UUID.fromString("23c35977-01b5-452c-9225-ecce440034e0");
217

    
218
        // ---- loading taxon with find (uuid) ----
219

    
220
        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
221

    
222
        // at this point the taxonNew object is detached and all lazy loaded proxy
223
        // objects in the object graph (including teamMembers) will have a new
224
        // session attached implying that all the following calls will succeed
225

    
226
        INonViralName nvn =  CdmBase.deproxy(taxon.getName());
227
        Team team = CdmBase.deproxy(nvn.getCombinationAuthorship(),Team.class);
228
        taxonService.update(taxon);
229

    
230
        // ---- loading taxon with find (id) ----
231

    
232
        taxon = (Taxon)commonService.find(taxon.getClass(), taxon.getId());
233

    
234
        // at this point the taxonNew object is detached and all lazy loaded proxy
235
        // objects in the object graph (including teamMembers) will have a new
236
        // session attached implying that all the following calls will succeed
237

    
238
        nvn =  CdmBase.deproxy(taxon.getName());
239
        team = CdmBase.deproxy(nvn.getCombinationAuthorship(), Team.class);
240
        taxonService.update(taxon);
241

    
242
        // ---- loading taxon with findTaxonByUuid ----
243

    
244
        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
245
                "name"
246
        });
247

    
248
        // loading the taxon with its taxon name object pre-initialized
249
        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
250

    
251
        nvn = CdmBase.deproxy(taxon.getName(),TaxonNameBase.class);
252
        team = CdmBase.deproxy(nvn.getCombinationAuthorship(),Team.class);
253

    
254
        // since a valid session is now attached to teamMembers, forcing the
255
        // initializing of the teamMembers (in TeamDefaultCacheStrategy.getTitleCache)
256
        // by setting the protected title cache to false does not throw an exception
257
        // because the teamMember persistent collection now has a valid session,
258
        // which is used to initialize the persistent collection
259

    
260
        team.setProtectedTitleCache(false);
261
        taxonService.update(taxon);
262
    }
263

    
264
    @Test
265
    @Transactional(TransactionMode.DISABLED)
266
    public void testNonTransactionalUpdateForNewTaxon() {
267

    
268
        // this test is only to prove that the update problem occurs
269
        // also for newly created objects (as expected)
270

    
271
        // create / save new taxon with name and author team with team member
272

    
273
        Team combAuthor = Team.NewTitledInstance("X-Men", "X-Men");
274
        combAuthor.addTeamMember(Person.NewTitledInstance("Wolverine"));
275

    
276

    
277
        IBotanicalName name = TaxonNameFactory.NewBotanicalInstance(null, "Pinus Alba", null, null, null, null, null, null,  null);
278
        name.setCombinationAuthorship(combAuthor);
279

    
280
        Taxon taxon = Taxon.NewInstance(name, null);
281

    
282
        UUID taxonUuid = taxonService.save(taxon).getUuid();
283

    
284
        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
285
                "name"
286
        });
287

    
288
        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
289

    
290
        INonViralName nvn = CdmBase.deproxy(taxon.getName());
291
        Team team = CdmBase.deproxy(nvn.getCombinationAuthorship(), Team.class);
292
        team.setProtectedTitleCache(false);
293

    
294
        try {
295
            taxonService.update(taxon);
296
            Assert.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
297
        } catch(LazyInitializationException lie) {
298

    
299
            if(!lie.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION)) {
300
                Assert.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION);
301
            }
302
        }
303

    
304
    }
305

    
306
    @Test
307
    @DataSet
308
    @Transactional(TransactionMode.DISABLED)
309
    public void testNonTransactionalMergeForExistingTaxon() {
310
        // this method tests the updating of a 'truely' detached object
311
        // using merge
312

    
313
        // setting the TransactionMode for this method to DISABLED is important
314
        // to ensure that transaction boundaries remain at the service layer calls
315
        // to simulate detachment and update of the persisted object
316

    
317
        UUID taxonUuid = UUID.fromString("23c35977-01b5-452c-9225-ecce440034e0");
318

    
319
        // ---- loading taxon with find (uuid) ----
320

    
321
        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
322

    
323
        // at this point the taxonNew object is detached and all lazy loaded proxy
324
        // objects in the object graph (including teamMembers) will have values of
325
        // initialized=false and session=null
326

    
327
        taxonService.merge(taxon);
328

    
329
        // ---- loading taxon with find (id) ----
330

    
331
        taxon = (Taxon)commonService.find(taxon.getClass(), taxon.getId());
332

    
333
        taxonService.merge(taxon);
334

    
335
        // ---- loading taxon with findTaxonByUuid ----
336

    
337
        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
338
                "name"
339
        });
340

    
341
        // loading the taxon with its taxon name object pre-initialized
342
        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
343

    
344
        // at this point the taxonNew object is detached and all lazy loaded proxy
345
        // objects in the object graph (including teamMembers) will have values of
346
        // initialized=false and session=null
347

    
348
        INonViralName nvn = CdmBase.deproxy(taxon.getName());
349

    
350
        // normally this call should throw a lazy loading exception since
351
        // the combinationAuthorship object is not initialized, but
352
        // the findTaxonByUuid performs this initialization (via the
353
        // AdvancedBeanInitializer) since the combinationAuthorship
354
        // is annotated with CacheUpdate
355

    
356
        Team team = CdmBase.deproxy(nvn.getCombinationAuthorship(),Team.class);
357

    
358
        // setting the protected title cache to false to ensure that
359
        // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
360
        // to initialize the teamMembers persistent collection which fails
361
        team.setProtectedTitleCache(false);
362

    
363

    
364
        taxonService.merge(taxon);
365

    
366
    }
367

    
368

    
369
    @Test
370
    public final void testTaxonDescriptionMerge() {
371

    
372
        IBotanicalName name = TaxonNameFactory.NewBotanicalInstance(null, "Abies alba", null, null, null, null, null, null, null);
373
        Taxon taxon = Taxon.NewInstance(name, null);
374
        TaxonDescription description = TaxonDescription.NewInstance(taxon);
375

    
376
        TextData textData = TextData.NewInstance();
377

    
378
        textData.setFeature(Feature.ECOLOGY());
379
        description.addElement(textData);
380

    
381
        DescriptionElementSource descriptionElementSource = DescriptionElementSource.NewInstance(OriginalSourceType.PrimaryTaxonomicSource);
382

    
383
        textData.addSource(descriptionElementSource);
384

    
385
        taxonService.merge(taxon);
386
    }
387

    
388
    @Test  //testing of bidirectionality of supplemental data #5743
389
    public final void testReferenceWithAnnotationMerge() {
390

    
391
        Reference ref = ReferenceFactory.newBook();
392

    
393
        ref.addAnnotation(Annotation.NewDefaultLanguageInstance("ref"));
394

    
395
        referenceService.merge(ref);
396
    }
397

    
398
    @Test //testing of bidirectionality of supplemental data #5743
399
    public final void testAnnotationMerge() {
400

    
401
        Reference ref = ReferenceFactory.newBook();
402

    
403
        Annotation annotation = Annotation.NewDefaultLanguageInstance("anno");
404
        ref.addAnnotation(annotation);
405

    
406
        annotationService.merge(annotation);
407
    }
408
}
(10-10/34)