2 * Copyright (C) 2007 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.
10 package eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.common
;
12 import java
.lang
.reflect
.Field
;
13 import java
.lang
.reflect
.InvocationTargetException
;
14 import java
.lang
.reflect
.Method
;
15 import java
.lang
.reflect
.Modifier
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Collection
;
18 import java
.util
.HashMap
;
19 import java
.util
.HashSet
;
20 import java
.util
.List
;
24 import javax
.naming
.Reference
;
26 import org
.apache
.commons
.lang
.StringUtils
;
27 import org
.apache
.commons
.lang
.UnhandledException
;
28 import org
.apache
.log4j
.Logger
;
29 import org
.hibernate
.Criteria
;
30 import org
.hibernate
.HibernateException
;
31 import org
.hibernate
.MappingException
;
32 import org
.hibernate
.Query
;
33 import org
.hibernate
.Session
;
34 import org
.hibernate
.SessionFactory
;
35 import org
.hibernate
.criterion
.CriteriaSpecification
;
36 import org
.hibernate
.criterion
.Criterion
;
37 import org
.hibernate
.criterion
.Restrictions
;
38 import org
.hibernate
.engine
.spi
.SessionFactoryImplementor
;
39 import org
.hibernate
.internal
.SessionFactoryImpl
;
40 import org
.hibernate
.internal
.SessionImpl
;
41 import org
.hibernate
.metadata
.ClassMetadata
;
42 import org
.hibernate
.metadata
.CollectionMetadata
;
43 import org
.hibernate
.persister
.collection
.CollectionPersister
;
44 import org
.hibernate
.persister
.collection
.OneToManyPersister
;
45 import org
.hibernate
.persister
.entity
.AbstractEntityPersister
;
46 import org
.hibernate
.stat
.Statistics
;
47 import org
.hibernate
.type
.AnyType
;
48 import org
.hibernate
.type
.BooleanType
;
49 import org
.hibernate
.type
.CollectionType
;
50 import org
.hibernate
.type
.ComponentType
;
51 import org
.hibernate
.type
.DoubleType
;
52 import org
.hibernate
.type
.EntityType
;
53 import org
.hibernate
.type
.EnumType
;
54 import org
.hibernate
.type
.FloatType
;
55 import org
.hibernate
.type
.IntegerType
;
56 import org
.hibernate
.type
.LongType
;
57 import org
.hibernate
.type
.MaterializedClobType
;
58 import org
.hibernate
.type
.SerializableType
;
59 import org
.hibernate
.type
.SetType
;
60 import org
.hibernate
.type
.StringType
;
61 import org
.hibernate
.type
.Type
;
62 import org
.jadira
.usertype
.dateandtime
.joda
.PersistentDateTime
;
63 import org
.springframework
.dao
.DataAccessException
;
64 import org
.springframework
.stereotype
.Repository
;
66 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
67 import eu
.etaxonomy
.cdm
.common
.DoubleResult
;
68 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
69 import eu
.etaxonomy
.cdm
.hibernate
.PartialUserType
;
70 import eu
.etaxonomy
.cdm
.hibernate
.URIUserType
;
71 import eu
.etaxonomy
.cdm
.hibernate
.UUIDUserType
;
72 import eu
.etaxonomy
.cdm
.hibernate
.WSDLDefinitionUserType
;
73 import eu
.etaxonomy
.cdm
.model
.common
.AnnotatableEntity
;
74 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
75 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
76 import eu
.etaxonomy
.cdm
.model
.common
.Extension
;
77 import eu
.etaxonomy
.cdm
.model
.common
.ICdmBase
;
78 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
79 import eu
.etaxonomy
.cdm
.model
.common
.Marker
;
80 import eu
.etaxonomy
.cdm
.model
.metadata
.CdmMetaData
;
81 import eu
.etaxonomy
.cdm
.model
.name
.BotanicalName
;
82 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
83 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
84 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
85 import eu
.etaxonomy
.cdm
.strategy
.match
.CacheMatcher
;
86 import eu
.etaxonomy
.cdm
.strategy
.match
.DefaultMatchStrategy
;
87 import eu
.etaxonomy
.cdm
.strategy
.match
.FieldMatcher
;
88 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchStrategy
;
89 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
90 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchException
;
91 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
92 import eu
.etaxonomy
.cdm
.strategy
.match
.Matching
;
93 import eu
.etaxonomy
.cdm
.strategy
.merge
.DefaultMergeStrategy
;
94 import eu
.etaxonomy
.cdm
.strategy
.merge
.IMergable
;
95 import eu
.etaxonomy
.cdm
.strategy
.merge
.IMergeStrategy
;
96 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeException
;
99 public class CdmGenericDaoImpl
extends CdmEntityDaoBase
<CdmBase
> implements ICdmGenericDao
{
100 private static final Logger logger
= Logger
.getLogger(CdmGenericDaoImpl
.class);
103 private Set
<Class
<?
extends CdmBase
>> allCdmClasses
= null;
104 private Map
<Class
<?
extends CdmBase
>, Set
<ReferenceHolder
>> referenceMap
= new HashMap
<Class
<?
extends CdmBase
>, Set
<ReferenceHolder
>>();
106 private class ReferenceHolder
{
108 Class
<?
extends CdmBase
> otherClass
;
109 Class
<?
extends CdmBase
> itemClass
;
110 public boolean isCollection(){return itemClass
!= null;};
111 public String
toString(){return otherClass
.getSimpleName() + "." + propertyName
;};
115 public CdmGenericDaoImpl() {
116 super(CdmBase
.class);
120 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getCdmBasesByFieldAndClass(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.common.CdmBase)
123 public List
<CdmBase
> getCdmBasesByFieldAndClass(Class clazz
, String propertyName
, CdmBase referencedCdmBase
){
124 Session session
= super.getSession();
125 Criteria criteria
= session
.createCriteria(clazz
);
126 criteria
.add(Restrictions
.eq(propertyName
, referencedCdmBase
));
127 return criteria
.list();
131 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getCdmBasesByFieldAndClass(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.common.CdmBase)
133 public List
<CdmBase
> getCdmBasesWithItemInCollection(Class itemClass
, Class clazz
, String propertyName
, CdmBase item
){
134 Session session
= super.getSession();
135 String thisClassStr
= itemClass
.getSimpleName();
136 String otherClassStr
= clazz
.getSimpleName();
137 String queryStr
= " SELECT other FROM "+ thisClassStr
+ " this, " + otherClassStr
+ " other " +
138 " WHERE this = :referencedObject AND this member of other."+propertyName
;
139 Query query
= session
.createQuery(queryStr
).setEntity("referencedObject", item
);
140 List
<CdmBase
> result
= query
.list();
145 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getAllCdmClasses(boolean)
147 public Set
<Class
<?
extends CdmBase
>> getAllCdmClasses(boolean includeAbstractClasses
){
148 Set
<Class
<?
extends CdmBase
>> result
= new HashSet
<Class
<?
extends CdmBase
>>();
150 SessionFactory sessionFactory
= getSession().getSessionFactory();
151 Map
<?
,?
> allClassMetadata
= sessionFactory
.getAllClassMetadata();
152 Collection
<?
> keys
= allClassMetadata
.keySet();
153 for (Object oKey
: keys
){
154 if (oKey
instanceof String
){
155 String strKey
= (String
)oKey
;
156 if (! strKey
.endsWith("_AUD")){
158 Class clazz
= Class
.forName(strKey
);
159 boolean isAbstractClass
= Modifier
.isAbstract(clazz
.getModifiers());
160 if (! isAbstractClass
|| includeAbstractClasses
){
163 } catch (ClassNotFoundException e
) {
164 logger
.warn("Class not found: " + strKey
);
168 logger
.warn("key is not of type String: " + oKey
);
174 public Set
<CdmBase
> getReferencingObjects(CdmBase referencedCdmBase
){
175 Set
<CdmBase
> result
= new HashSet
<CdmBase
>();
177 referencedCdmBase
= (CdmBase
)HibernateProxyHelper
.deproxy(referencedCdmBase
);
178 Class
<?
extends CdmBase
> referencedClass
= referencedCdmBase
.getClass();
180 Set
<ReferenceHolder
> holderSet
= referenceMap
.get(referencedClass
);
181 if (holderSet
== null){
182 holderSet
= makeHolderSet(referencedClass
);
183 referenceMap
.put(referencedClass
, holderSet
);
185 for (ReferenceHolder refHolder
: holderSet
){
186 handleReferenceHolder(referencedCdmBase
, result
, refHolder
);
189 } catch (Exception e
) {
191 throw new RuntimeException(e
);
197 * @param referencedCdmBase
201 private void handleReferenceHolder(CdmBase referencedCdmBase
,
202 Set
<CdmBase
> result
, ReferenceHolder refHolder
) {
203 boolean isCollection
= refHolder
.isCollection();
205 result
.addAll(getCdmBasesWithItemInCollection(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
));
207 result
.addAll(getCdmBasesByFieldAndClass(refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
));
213 * @param referencedClass
215 * @throws NoSuchFieldException
216 * @throws ClassNotFoundException
218 private Set
<ReferenceHolder
> makeHolderSet(Class referencedClass
) throws ClassNotFoundException
, NoSuchFieldException
{
219 Set
<ReferenceHolder
> result
= new HashSet
<ReferenceHolder
>();
222 if (allCdmClasses
== null){
223 allCdmClasses
= getAllCdmClasses(false); //findAllCdmClasses();
225 //referencedCdmBase = (CdmBase)HibernateProxyHelper.deproxy(referencedCdmBase);
226 SessionFactory sessionFactory
= getSession().getSessionFactory();
229 for (Class
<?
extends CdmBase
> cdmClass
: allCdmClasses
){
230 ClassMetadata classMetadata
= sessionFactory
.getClassMetadata(cdmClass
);
231 Type
[] propertyTypes
= classMetadata
.getPropertyTypes();
233 for (Type propertyType
: propertyTypes
){
234 String propertyName
= classMetadata
.getPropertyNames()[propertyNr
];
235 makePropertyType(result
, referencedClass
, sessionFactory
, cdmClass
, propertyType
, propertyName
, false);
244 * @param referencedCdmBase
246 * @param referencedClass
247 * @param sessionFactory
249 * @param propertyType
250 * @param propertyName
251 * @throws ClassNotFoundException
252 * @throws NoSuchFieldException
254 private void makePropertyType(
255 // CdmBase referencedCdmBase,
256 Set
<ReferenceHolder
> result
,
257 Class referencedClass
,
258 SessionFactory sessionFactory
, Class
<?
extends CdmBase
> cdmClass
,
259 Type propertyType
, String propertyName
, boolean isCollection
)
260 throws ClassNotFoundException
, NoSuchFieldException
{
263 if (propertyType
.isEntityType()){
264 EntityType entityType
= (EntityType
)propertyType
;
265 String associatedEntityName
= entityType
.getAssociatedEntityName();
266 Class entityClass
= Class
.forName(associatedEntityName
);
267 if (entityClass
.isInterface()){
268 logger
.debug("There is an interface");
270 if (entityClass
.isAssignableFrom(referencedClass
)){
271 makeSingleProperty(referencedClass
, entityClass
, propertyName
, cdmClass
, result
, isCollection
);
273 }else if (propertyType
.isCollectionType()){
274 CollectionType collectionType
= (CollectionType
)propertyType
;
275 //String role = collectionType.getRole();
276 Type elType
= collectionType
.getElementType((SessionFactoryImplementor
)sessionFactory
);
277 makePropertyType(result
, referencedClass
, sessionFactory
, cdmClass
, elType
, propertyName
, true);
278 }else if (propertyType
.isAnyType()){
279 AnyType anyType
= (AnyType
)propertyType
;
280 Field field
= cdmClass
.getDeclaredField(propertyName
);
281 Class returnType
= field
.getType();
282 if (returnType
.isInterface()){
283 logger
.debug("There is an interface");
285 if (returnType
.isAssignableFrom(referencedClass
)){
286 makeSingleProperty(referencedClass
, returnType
, propertyName
, cdmClass
, result
, isCollection
);
288 }else if (propertyType
.isComponentType()){
289 ComponentType componentType
= (ComponentType
)propertyType
;
290 Type
[] subTypes
= componentType
.getSubtypes();
291 // Field field = cdmClass.getDeclaredField(propertyName);
292 // Class returnType = field.getType();
294 for (Type subType
: subTypes
){
295 String subPropertyName
= componentType
.getPropertyNames()[propertyNr
];
296 if (!isNoDoType(subType
)){
297 logger
.warn("SubType not yet handled: " + subType
);
299 // handlePropertyType(referencedCdmBase, result, referencedClass,
300 // sessionFactory, cdmClass, subType, subPropertyName, isCollection);
303 }else if (isNoDoType(propertyType
)){
306 logger
.warn("propertyType not yet handled: " + propertyType
.getName());
309 // if (! type.isInterface()){
310 // if (referencedClass.isAssignableFrom(type)||
311 // type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
312 // handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
315 // }else if (type.isAssignableFrom(referencedClass)){
316 // handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
320 private boolean makeSingleProperty(Class itemClass
, Class type
, String propertyName
, Class cdmClass
, Set
<ReferenceHolder
> result
,/*CdmBase item,*/ boolean isCollection
){
321 String fieldName
= StringUtils
.rightPad(propertyName
, 30);
322 String className
= StringUtils
.rightPad(cdmClass
.getSimpleName(), 30);
323 String returnTypeName
= StringUtils
.rightPad(type
.getSimpleName(), 30);
325 // logger.debug(fieldName + "\t\t" + className + "\t\t" + returnTypeName);
326 ReferenceHolder refHolder
= new ReferenceHolder();
327 refHolder
.propertyName
= propertyName
;
328 refHolder
.otherClass
= cdmClass
;
329 refHolder
.itemClass
= (isCollection ? itemClass
: null) ;
330 result
.add(refHolder
);
335 * @param propertyType
338 private boolean isNoDoType(Type propertyType
) {
339 boolean result
= false;
340 Class
[] classes
= new Class
[]{
341 PersistentDateTime
.class,
342 WSDLDefinitionUserType
.class,
344 PartialUserType
.class,
348 MaterializedClobType
.class,
351 SerializableType
.class,
356 Set
<String
> classNames
= new HashSet
<String
>();
357 for (Class clazz
: classes
){
358 classNames
.add(clazz
.getCanonicalName());
359 if (clazz
== propertyType
.getClass()){
363 String propertyTypeClassName
= propertyType
.getName();
364 if (classNames
.contains(propertyTypeClassName
)){
371 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getHqlResult(java.lang.String)
373 public List
<CdmBase
> getHqlResult(String hqlQuery
){
374 Query query
= getSession().createQuery(hqlQuery
);
375 List
<CdmBase
> result
= query
.list();
379 public <T
extends CdmBase
> void merge(T cdmBase1
, T cdmBase2
, IMergeStrategy mergeStrategy
) throws MergeException
{
380 Class
<T
> clazz
= (Class
<T
>)cdmBase1
.getClass();
381 SessionImpl session
= (SessionImpl
) getSession();
382 SessionFactory sessionFactory
= session
.getSessionFactory();
383 if (mergeStrategy
== null){
384 mergeStrategy
= DefaultMergeStrategy
.NewInstance(cdmBase1
.getClass());
387 //test null and types
388 testMergeValid(cdmBase1
, cdmBase2
);
394 Set
<ICdmBase
> deleteSet
= new HashSet
<ICdmBase
>();
395 Set
<ICdmBase
> cloneSet
= new HashSet
<ICdmBase
>();
396 if (cdmBase1
instanceof IMergable
){
397 IMergable mergable1
= (IMergable
)cdmBase1
;
398 IMergable mergable2
= (IMergable
)cdmBase2
;
399 deleteSet
= mergeStrategy
.invoke(mergable1
, mergable2
, cloneSet
);
400 //session.saveOrUpdate(mergable1);
403 //((IMergable)cdmBase1).mergeInto(cdmBase2, DefaultMergeStrategy.NewInstance(cdmBase1.getClass()));
405 mergeExternal(cdmBase1
, cdmBase2
, clazz
, session
);
409 if (cdmBase2
.getId() > 0){
410 session
.saveOrUpdate(cdmBase2
);
411 //rearrange references to cdmBase2
412 reallocateReferences(cdmBase1
, cdmBase2
, sessionFactory
, clazz
, cloneSet
);
415 //remove deleted objects
417 //session.delete(null, mergable2, true, null);
418 session
.delete(cdmBase2
);
419 for (ICdmBase toBeDeleted
: deleteSet
){
420 logger
.debug("Delete " + toBeDeleted
);
421 if (toBeDeleted
!= cdmBase2
){
422 session
.delete(toBeDeleted
);
430 } catch (Exception e
) {
431 throw new MergeException(e
);
441 * @param sessionFactory
442 * @throws MergeException
443 * @throws ClassNotFoundException
444 * @throws NoSuchFieldException
446 private <T
extends CdmBase
> void mergeExternal(T cdmBase1
, T cdmBase2
, Class
<T
> clazz
,
447 Session session
) throws MergeException
{
449 logger
.warn("Merge external");
450 handleAnnotationsEtc(cdmBase1
, cdmBase2
, session
);
452 SessionFactoryImpl sessionFactory
= (SessionFactoryImpl
) session
.getSessionFactory();
454 Map allClassMetadata
= sessionFactory
.getAllClassMetadata();
457 getCollectionRoles(clazz
, sessionFactory
);
459 TaxonNameBase name1
= BotanicalName
.NewInstance(null);
460 name1
.getTaxonBases();
462 Type propType
= sessionFactory
.getReferencedPropertyType(BotanicalName
.class.getCanonicalName(), "taxonBases");
463 Map collMetadata
= sessionFactory
.getAllCollectionMetadata();
464 //roles = sessionFactory.getCollectionRolesByEntityParticipant("eu.etaxonomy.cdm.model.name.BotanicalName");
465 CollectionPersister collPersister
;
467 collPersister
= sessionFactory
.getCollectionPersister(TaxonNameBase
.class.getCanonicalName()+".annotations");
468 } catch (MappingException e
) {
469 // TODO Auto-generated catch block
472 Statistics statistics
= sessionFactory
.getStatistics();
474 ClassMetadata taxonMetaData
= sessionFactory
.getClassMetadata(Taxon
.class);
475 String ename
= taxonMetaData
.getEntityName();
477 Reference ref
= sessionFactory
.getReference();
479 } catch (Exception e
) {
480 // TODO Auto-generated catch block
485 ClassMetadata classMetadata
= getSession().getSessionFactory().getClassMetadata(clazz
);
486 Type
[] propertyTypes
= classMetadata
.getPropertyTypes();
488 for (Type propertyType
: propertyTypes
){
489 String propertyName
= classMetadata
.getPropertyNames()[propertyNr
];
490 logger
.debug(propertyName
);
491 makeMergeProperty(cdmBase1
, cdmBase2
, propertyType
, propertyName
, sessionFactory
, false);
494 Set
<String
> collectionRoles
;
495 if (classMetadata
instanceof AbstractEntityPersister
){
496 AbstractEntityPersister persister
= (AbstractEntityPersister
)classMetadata
;
497 String rootName
= persister
.getRootEntityName();
498 collectionRoles
= sessionFactory
.getCollectionRolesByEntityParticipant(rootName
);
499 for (String collectionRole
: collectionRoles
){
500 CollectionMetadata collMetadata2
= sessionFactory
.getCollectionMetadata(collectionRole
);
501 String role
= collMetadata2
.getRole();
502 Type elType
= collMetadata2
.getElementType();
511 * @param sessionFactory
513 private <T
> Set
<String
> getCollectionRoles(Class
<T
> clazz
,
514 SessionFactoryImpl sessionFactory
) {
515 Set
<String
> collectionRoles
= null;
516 ClassMetadata classMetaData
= sessionFactory
.getClassMetadata(clazz
);
517 if (classMetaData
instanceof AbstractEntityPersister
){
518 AbstractEntityPersister persister
= (AbstractEntityPersister
)classMetaData
;
519 String rootName
= persister
.getRootEntityName();
520 collectionRoles
= sessionFactory
.getCollectionRolesByEntityParticipant(rootName
);
521 for (String collectionRole
: collectionRoles
){
522 CollectionMetadata collMetadata
= sessionFactory
.getCollectionMetadata(collectionRole
);
523 CollectionPersister collPersister
= sessionFactory
.getCollectionPersister(collectionRole
);
527 logger
.warn("Class metadata is not of type AbstractEntityPersister");
528 throw new UnhandledException("Class metadata is not of type AbstractEntityPersister", null);
530 return collectionRoles
;
534 private <T
extends CdmBase
> void makeMergeProperty(T cdmBase1
, T cdmBase2
, Type propertyType
, String propertyName
, SessionFactoryImpl sessionFactory
, boolean isCollection
) throws MergeException
538 Class
<T
> clazz
= (Class
<T
>)cdmBase1
.getClass();
539 if (isNoDoType(propertyType
)){
541 }else if (propertyType
.isEntityType()){
542 //Field field = clazz.getField(propertyName);
543 EntityType entityType
= (EntityType
)propertyType
;
544 String associatedEntityName
= entityType
.getAssociatedEntityName();
545 Class entityClass
= Class
.forName(associatedEntityName
);
546 // Type refPropType = sessionFactory.getReferencedPropertyType(entityClass.getCanonicalName(), propertyName);
547 Set
<String
> collectionRoles
= getCollectionRoles(clazz
, sessionFactory
);
548 for (String collectionRole
: collectionRoles
){
549 CollectionMetadata collMetadata
= sessionFactory
.getCollectionMetadata(collectionRole
);
550 String role
= collMetadata
.getRole();
555 // if (entityClass.isInterface()){
556 // logger.debug("So ein interface");
558 // if (entityClass.isAssignableFrom(clazz)){
559 // makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
561 }else if (propertyType
.isCollectionType()){
562 CollectionType collectionType
= (CollectionType
)propertyType
;
563 String role
= collectionType
.getRole();
564 Type elType
= collectionType
.getElementType((SessionFactoryImplementor
)sessionFactory
);
565 String n
= collectionType
.getAssociatedEntityName(sessionFactory
);
566 CollectionMetadata collMetadata
= sessionFactory
.getCollectionMetadata(role
);
567 if (collMetadata
instanceof OneToManyPersister
){
568 OneToManyPersister oneManyPersister
= (OneToManyPersister
)collMetadata
;
569 String className
= oneManyPersister
.getOwnerEntityName();
570 Class
<?
> myClass
= Class
.forName(className
);
571 Field field
= myClass
.getDeclaredField(propertyName
);
572 field
.setAccessible(true);
574 if (collectionType
instanceof SetType
){
575 Set set2
= (Set
)field
.get(cdmBase2
);
576 Set
<Object
> set1
= (Set
<Object
>)field
.get(cdmBase1
);
577 for (Object obj2
: set2
){
581 } catch (IllegalArgumentException e
) {
582 // TODO Auto-generated catch block
584 } catch (IllegalAccessException e
) {
585 // TODO Auto-generated catch block
592 // makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
593 }else if (propertyType
.isAnyType()){
594 AnyType anyType
= (AnyType
)propertyType
;
595 Field field
= clazz
.getDeclaredField(propertyName
);
596 Class returnType
= field
.getType();
597 // if (returnType.isInterface()){
598 // logger.debug("So ein interface");
600 // if (returnType.isAssignableFrom(referencedClass)){
601 // makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
603 }else if (propertyType
.isComponentType()){
604 ComponentType componentType
= (ComponentType
)propertyType
;
605 Type
[] subTypes
= componentType
.getSubtypes();
606 // Field field = cdmClass.getDeclaredField(propertyName);
607 // Class returnType = field.getType();
609 for (Type subType
: subTypes
){
610 String subPropertyName
= componentType
.getPropertyNames()[propertyNr
];
611 if (!isNoDoType(subType
)){
612 logger
.warn("SubType not yet handled: " + subType
);
614 // handlePropertyType(referencedCdmBase, result, referencedClass,
615 // sessionFactory, cdmClass, subType, subPropertyName, isCollection);
619 logger
.warn("propertyType not yet handled: " + propertyType
.getName());
622 // if (! type.isInterface()){
623 // if (referencedClass.isAssignableFrom(type)||
624 // type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
625 // handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
628 // }else if (type.isAssignableFrom(referencedClass)){
629 // handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
630 } catch (Exception e
) {
631 throw new MergeException(e
);
638 private void reallocateReferences(CdmBase cdmBase1
, CdmBase cdmBase2
, SessionFactory sessionFactory
, Class clazz
, Set
<ICdmBase
> cloneSet
){
640 Set
<ReferenceHolder
> holderSet
= referenceMap
.get(clazz
);
641 if (holderSet
== null){
642 holderSet
= makeHolderSet(clazz
);
643 referenceMap
.put(clazz
, holderSet
);
645 for (ReferenceHolder refHolder
: holderSet
){
646 reallocateByHolder(cdmBase1
, cdmBase2
, refHolder
, cloneSet
);
649 } catch (Exception e
) {
651 throw new RuntimeException(e
);
659 * @throws MergeException
661 private void reallocateByHolder(CdmBase cdmBase1
, CdmBase cdmBase2
, ReferenceHolder refHolder
, Set
<ICdmBase
> cloneSet
) throws MergeException
{
663 if (refHolder
.isCollection()){
664 reallocateCollection(cdmBase1
, cdmBase2
, refHolder
, cloneSet
);
666 reallocateSingleItem(cdmBase1
, cdmBase2
, refHolder
, cloneSet
);
668 } catch (Exception e
) {
669 throw new MergeException("Error during reallocation of references to merge object: " + cdmBase2
, e
);
679 * @throws MergeException
680 * @throws NoSuchFieldException
681 * @throws SecurityException
682 * @throws IllegalAccessException
683 * @throws IllegalArgumentException
684 * @throws InvocationTargetException
686 private void reallocateCollection(CdmBase cdmBase1
, CdmBase cdmBase2
,
687 ReferenceHolder refHolder
, Set
<ICdmBase
> cloneSet
) throws MergeException
, SecurityException
, NoSuchFieldException
, IllegalArgumentException
, IllegalAccessException
, InvocationTargetException
{
688 List
<CdmBase
> list
= getCdmBasesWithItemInCollection(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, cdmBase2
);
689 for (CdmBase referencingObject
: list
){
690 Field referencingField
= getFieldRecursive(refHolder
.otherClass
, refHolder
.propertyName
);
691 referencingField
.setAccessible(true);
692 Object collection
= referencingField
.get(referencingObject
);
693 if (! (collection
instanceof Collection
)){
694 throw new MergeException ("Reallocation of collections for collection other than set and list not yet implemented");
696 Method addMethod
= DefaultMergeStrategy
.getAddMethod(referencingField
, false);
697 Method removeMethod
= DefaultMergeStrategy
.getAddMethod(referencingField
, true);
698 addMethod
.invoke(referencingObject
, cdmBase1
);
699 removeMethod
.invoke(referencingObject
, cdmBase2
);
703 private Field
getFieldRecursive(Class clazz
, String propertyName
) throws NoSuchFieldException
{
705 return clazz
.getDeclaredField(propertyName
);
706 } catch (NoSuchFieldException e
) {
707 Class superClass
= clazz
.getSuperclass();
708 if (CdmBase
.class.isAssignableFrom(superClass
)){
709 return getFieldRecursive(superClass
, propertyName
);
717 * @throws NoSuchFieldException
718 * @throws SecurityException
719 * @throws IllegalAccessException
720 * @throws IllegalArgumentException
723 private void reallocateSingleItem_Old(CdmBase cdmBase1
, CdmBase cdmBase2
, ReferenceHolder refHolder
) throws SecurityException
, NoSuchFieldException
, IllegalArgumentException
, IllegalAccessException
{
724 List
<CdmBase
> referencingObjects
= getCdmBasesByFieldAndClass(refHolder
.otherClass
, refHolder
.propertyName
, cdmBase2
);
725 for (CdmBase referencingObject
: referencingObjects
){
726 Field referencingField
= refHolder
.otherClass
.getDeclaredField(refHolder
.propertyName
);
727 referencingField
.setAccessible(true);
728 Object test
= referencingField
.get(referencingObject
);
729 assert(test
.equals(cdmBase2
));
730 referencingField
.set(referencingObject
, cdmBase1
);
734 private void reallocateSingleItem(CdmBase cdmBase1
, CdmBase cdmBase2
, ReferenceHolder refHolder
, Set
<ICdmBase
> cloneSet
) throws SecurityException
, NoSuchFieldException
, IllegalArgumentException
, IllegalAccessException
{
735 List
<CdmBase
> referencingObjects
= getCdmBasesByFieldAndClass(refHolder
.otherClass
, refHolder
.propertyName
, cdmBase2
);
736 Session session
= getSession();
737 for (CdmBase referencingObject
: referencingObjects
){
738 if (!cloneSet
.contains(referencingObject
)){
739 String className
= refHolder
.otherClass
.getSimpleName();
740 String propertyName
= refHolder
.propertyName
;
741 String hql
= "update " + className
+ " c set c."+propertyName
+" = :newValue where c.id = :id";
742 Query query
= session
.createQuery(hql
);
743 query
.setEntity("newValue", cdmBase1
);
744 query
.setInteger("id",referencingObject
.getId());
745 int rowCount
= query
.executeUpdate();
746 logger
.debug("Rows affected: " + rowCount
);
747 session
.refresh(referencingObject
);
761 private <T
> void handleAnnotationsEtc(T cdmBase1
, T cdmBase2
, Session session
) {
762 //when handling annotations and other elements linked via @Any an JDBC errors occurs
763 //due to the unique column constraint in the association table on the column referencing
765 //For some reason not delete command is executed for the old collection
767 session
.flush(); //for debugging
768 if (cdmBase1
instanceof AnnotatableEntity
){
769 AnnotatableEntity annotatableEntity1
= (AnnotatableEntity
)cdmBase1
;
770 AnnotatableEntity annotatableEntity2
= (AnnotatableEntity
)cdmBase2
;
772 List
<Annotation
> removeListAnnotation
= new ArrayList
<Annotation
>();
773 for (Annotation annotation
: annotatableEntity2
.getAnnotations()){
774 Annotation clone
= null;
776 clone
= annotation
.clone(annotatableEntity1
);
777 } catch (CloneNotSupportedException e
) {
780 annotatableEntity1
.addAnnotation(clone
);
781 removeListAnnotation
.add(annotation
);
783 for (Annotation annotation
: removeListAnnotation
){
784 annotatableEntity2
.removeAnnotation(annotation
);
785 getSession().delete(annotation
);
788 List
<Marker
> removeListMarker
= new ArrayList
<Marker
>();
789 for (Marker marker
: annotatableEntity2
.getMarkers()){
792 clone
= marker
.clone(annotatableEntity1
);
793 } catch (CloneNotSupportedException e
) {
796 annotatableEntity1
.addMarker(clone
);
797 removeListMarker
.add(marker
);
799 for (Marker marker
: removeListMarker
){
800 annotatableEntity2
.removeMarker(marker
);
801 getSession().delete(marker
);
804 if (cdmBase1
instanceof IdentifiableEntity
){
805 IdentifiableEntity identifiableEntity1
= (IdentifiableEntity
)cdmBase1
;
806 IdentifiableEntity identifiableEntity2
= (IdentifiableEntity
)cdmBase2
;
808 List
<Extension
> removeListExtension
= new ArrayList
<Extension
>();
809 for (Extension changeObject
: (Set
<Extension
>)identifiableEntity2
.getExtensions()){
811 Extension clone
= changeObject
.clone(identifiableEntity1
);
812 identifiableEntity1
.addExtension(clone
);
813 removeListExtension
.add(changeObject
);
814 } catch (CloneNotSupportedException e
) {
819 for (Extension removeObject
: removeListExtension
){
820 identifiableEntity2
.removeExtension(removeObject
);
821 getSession().delete(removeObject
);
825 session
.saveOrUpdate(cdmBase1
);
826 session
.saveOrUpdate(cdmBase2
);
830 private <T
extends CdmBase
> void testMergeValid(T cdmBase1
, T cdmBase2
)throws IllegalArgumentException
, NullPointerException
{
831 if (cdmBase1
== null || cdmBase2
== null){
832 throw new NullPointerException("Merge arguments must not be (null)");
834 cdmBase1
= (T
)HibernateProxyHelper
.deproxy(cdmBase1
);
835 cdmBase2
= (T
)HibernateProxyHelper
.deproxy(cdmBase2
);
837 if (cdmBase1
.getClass() != cdmBase2
.getClass()){
838 throw new IllegalArgumentException("Merge arguments must be of same type");
843 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#test()
846 SessionFactoryImpl factory
= (SessionFactoryImpl
)getSession().getSessionFactory();
847 Type propType
= factory
.getReferencedPropertyType(BotanicalName
.class.getCanonicalName(), "titleCache");
848 Map collMetadata
= factory
.getAllCollectionMetadata();
849 Object roles
= factory
.getCollectionRolesByEntityParticipant("eu.etaxonomy.cdm.model.name.BotanicalName");
850 CollectionPersister collPersister
;
852 collPersister
= factory
.getCollectionPersister(TaxonNameBase
.class.getCanonicalName()+".annotations");
853 } catch (MappingException e
) {
854 // TODO Auto-generated catch block
857 Statistics statistics
= factory
.getStatistics();
858 Map allClassMetadata
= factory
.getAllClassMetadata();
863 public <T
extends CdmBase
> T
find(Class
<T
> clazz
, int id
){
865 session
= getSession();
866 //session = getSession().getSessionFactory().getCurrentSession();
867 return (T
)session
.get(clazz
, id
);
871 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#findMatching(eu.etaxonomy.cdm.strategy.match.IMatchable, eu.etaxonomy.cdm.strategy.match.IMatchStrategy)
873 public <T
extends IMatchable
> List
<T
> findMatching(T objectToMatch
,
874 IMatchStrategy matchStrategy
) throws MatchException
{
876 List
<T
> result
= new ArrayList
<T
>();
877 if(objectToMatch
== null){
880 if (matchStrategy
== null){
881 matchStrategy
= DefaultMatchStrategy
.NewInstance(objectToMatch
.getClass());
883 result
.addAll(findMatchingNullSafe(objectToMatch
, matchStrategy
));
885 } catch (IllegalArgumentException e
) {
886 throw new MatchException(e
);
887 } catch (IllegalAccessException e
) {
888 throw new MatchException(e
);
892 public <T
extends IMatchable
> List
<T
> findMatchingNullSafe(T objectToMatch
, IMatchStrategy matchStrategy
) throws IllegalArgumentException
, IllegalAccessException
, MatchException
{
893 List
<T
> result
= new ArrayList
<T
>();
894 Session session
= getSession();
895 Class matchClass
= objectToMatch
.getClass();
896 ClassMetadata classMetaData
= session
.getSessionFactory().getClassMetadata(matchClass
.getCanonicalName());
897 Criteria criteria
= session
.createCriteria(matchClass
);
898 boolean noMatch
= makeCriteria(objectToMatch
, matchStrategy
, classMetaData
, criteria
);
899 logger
.debug(criteria
);
901 if (noMatch
== false){
902 List
<T
> matchCandidates
= criteria
.list();
903 matchCandidates
.remove(objectToMatch
);
904 for (T matchCandidate
: matchCandidates
){
905 if (matchStrategy
.invoke(objectToMatch
, matchCandidate
)){
906 result
.add(matchCandidate
);
908 logger
.warn("Match candidate did not match: " + matchCandidate
);
917 * @param objectToMatch
918 * @param matchStrategy
919 * @param classMetaData
922 * @throws IllegalAccessException
923 * @throws MatchException
925 private <T
> boolean makeCriteria(T objectToMatch
,
926 IMatchStrategy matchStrategy
, ClassMetadata classMetaData
,
927 Criteria criteria
) throws IllegalAccessException
, MatchException
{
928 Matching matching
= matchStrategy
.getMatching();
929 boolean noMatch
= false;
930 Map
<String
, List
<MatchMode
>> replaceMatchers
= new HashMap
<String
, List
<MatchMode
>>();
931 for (CacheMatcher cacheMatcher
: matching
.getCacheMatchers()){
932 boolean cacheProtected
= (Boolean
)cacheMatcher
.getProtectedField(matching
).get(objectToMatch
);
933 if (cacheProtected
== true){
934 String cacheValue
= (String
)cacheMatcher
.getField().get(objectToMatch
);
935 if (CdmUtils
.isEmpty(cacheValue
)){
936 return true; //no match
938 criteria
.add(Restrictions
.eq(cacheMatcher
.getPropertyName(), cacheValue
));
939 criteria
.add(Restrictions
.eq(cacheMatcher
.getProtectedPropertyName(), cacheProtected
));
941 List
<DoubleResult
<String
, MatchMode
>> replacementModes
= cacheMatcher
.getReplaceMatchModes(matching
);
942 for (DoubleResult
<String
, MatchMode
> replacementMode
: replacementModes
){
943 String propertyName
= replacementMode
.getFirstResult();
944 List
<MatchMode
> replaceMatcherList
= replaceMatchers
.get(propertyName
);
945 if (replaceMatcherList
== null){
946 replaceMatcherList
= new ArrayList
<MatchMode
>();
947 replaceMatchers
.put(propertyName
, replaceMatcherList
);
949 replaceMatcherList
.add(replacementMode
.getSecondResult());
955 for (FieldMatcher fieldMatcher
: matching
.getFieldMatchers(false)){
956 String propertyName
= fieldMatcher
.getPropertyName();
957 Type propertyType
= classMetaData
.getPropertyType(propertyName
);
958 Object value
= fieldMatcher
.getField().get(objectToMatch
);
959 List
<MatchMode
> matchModes
= new ArrayList
<MatchMode
>();
960 matchModes
.add(fieldMatcher
.getMatchMode());
961 if (replaceMatchers
.get(propertyName
) != null){
962 matchModes
.addAll(replaceMatchers
.get(propertyName
));
965 boolean isIgnore
= false;
966 for (MatchMode matchMode
: matchModes
){
967 isIgnore
|= matchMode
.isIgnore(value
);
970 if (propertyType
.isComponentType()){
971 matchComponentType(criteria
, fieldMatcher
, propertyName
, value
, matchModes
);
973 noMatch
= matchNonComponentType(criteria
, fieldMatcher
, propertyName
, value
, matchModes
, propertyType
);
985 * @param fieldMatcher
986 * @param propertyName
989 * @throws MatchException
990 * @throws IllegalAccessException
992 private void matchComponentType(Criteria criteria
,
993 FieldMatcher fieldMatcher
, String propertyName
, Object value
,
994 List
<MatchMode
> matchModes
) throws MatchException
, IllegalAccessException
{
996 boolean requiresSecondNull
= requiresSecondNull(matchModes
, value
);
997 if (requiresSecondNull
){
998 criteria
.add(Restrictions
.isNull(propertyName
));
1001 logger
.warn("Component type not yet implemented for (null) value: " + propertyName
);
1002 throw new MatchException("Component type not yet fully implemented for (null) value. Property: " + propertyName
);
1005 Class
<?
> componentClass
= fieldMatcher
.getField().getType();
1006 Map
<String
, Field
> fields
= CdmUtils
.getAllFields(componentClass
, Object
.class, false, false, true, false);
1007 for (String fieldName
: fields
.keySet()){
1008 String restrictionPath
= propertyName
+"."+fieldName
;
1009 Object componentValue
= fields
.get(fieldName
).get(value
);
1010 //TODO diffentiate matchMode
1011 createCriterion(criteria
, restrictionPath
, componentValue
, matchModes
);
1016 private boolean matchNonComponentType(Criteria criteria
,
1017 FieldMatcher fieldMatcher
, String propertyName
, Object value
,
1018 List
<MatchMode
> matchModes
, Type propertyType
) throws HibernateException
, DataAccessException
, MatchException
, IllegalAccessException
{
1019 boolean noMatch
= false;
1020 if (isRequired(matchModes
) && value
== null){
1023 }else if (requiresSecondNull(matchModes
,value
)){
1024 criteria
.add(Restrictions
.isNull(propertyName
));
1026 if (isMatch(matchModes
)){
1027 if (propertyType
.isCollectionType()){
1028 //TODO collection not yet handled for match
1030 int joinType
= CriteriaSpecification
.INNER_JOIN
;
1031 if (! requiresSecondValue(matchModes
,value
)){
1032 joinType
= CriteriaSpecification
.LEFT_JOIN
;
1034 Criteria matchCriteria
= criteria
.createCriteria(propertyName
, joinType
).add(Restrictions
.isNotNull("id"));
1035 Class matchClass
= value
.getClass();
1036 if (IMatchable
.class.isAssignableFrom(matchClass
)){
1037 IMatchStrategy valueMatchStrategy
= DefaultMatchStrategy
.NewInstance((Class
<IMatchable
>)matchClass
);
1038 ClassMetadata valueClassMetaData
= getSession().getSessionFactory().getClassMetadata(matchClass
.getCanonicalName());;
1039 noMatch
= makeCriteria(value
, valueMatchStrategy
, valueClassMetaData
, matchCriteria
);
1041 logger
.error("Class to match (" + matchClass
+ ") is not of type IMatchable");
1042 throw new MatchException("Class to match (" + matchClass
+ ") is not of type IMatchable");
1045 }else if (isEqual(matchModes
)){
1046 createCriterion(criteria
, propertyName
, value
, matchModes
);
1048 logger
.warn("Unhandled match mode: " + matchModes
+ ", value: " + (value
==null?
"null":value
));
1056 * @param propertyName
1059 * @throws MatchException
1061 private void createCriterion(Criteria criteria
, String propertyName
,
1062 Object value
, List
<MatchMode
> matchModes
) throws MatchException
{
1063 Criterion finalRestriction
= null;
1064 Criterion equalRestriction
= Restrictions
.eq(propertyName
, value
);
1065 Criterion nullRestriction
= Restrictions
.isNull(propertyName
);
1066 if (this.requiresSecondValue(matchModes
, value
)){
1067 finalRestriction
= equalRestriction
;
1068 }else if (requiresSecondNull(matchModes
, value
) ){
1069 finalRestriction
= nullRestriction
;
1071 finalRestriction
= Restrictions
.or(equalRestriction
, nullRestriction
);
1073 //return finalRestriction;
1074 criteria
.add(finalRestriction
);
1081 * @throws MatchException
1083 private boolean requiresSecondNull(List
<MatchMode
> matchModes
, Object value
) throws MatchException
{
1084 boolean result
= true;
1085 for (MatchMode matchMode
: matchModes
){
1086 result
&= matchMode
.requiresSecondNull(value
);
1095 * @throws MatchException
1097 private boolean requiresSecondValue(List
<MatchMode
> matchModes
, Object value
) throws MatchException
{
1098 boolean result
= true;
1099 for (MatchMode matchMode
: matchModes
){
1100 result
&= matchMode
.requiresSecondValue(value
);
1109 * @throws MatchException
1111 private boolean isRequired(List
<MatchMode
> matchModes
) throws MatchException
{
1112 boolean result
= true;
1113 for (MatchMode matchMode
: matchModes
){
1114 result
&= matchMode
.isRequired();
1120 * Returns true if at least one match mode is of typ MATCH_XXX
1124 * @throws MatchException
1126 private boolean isMatch(List
<MatchMode
> matchModes
) throws MatchException
{
1127 boolean result
= false;
1128 for (MatchMode matchMode
: matchModes
){
1129 result
|= matchMode
.isMatch();
1135 * Returns true if at least one match mode is of typ EQUAL_XXX
1139 * @throws MatchException
1141 private boolean isEqual(List
<MatchMode
> matchModes
) throws MatchException
{
1142 boolean result
= false;
1143 for (MatchMode matchMode
: matchModes
){
1144 result
|= matchMode
.isEqual();
1150 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#saveMetaData(eu.etaxonomy.cdm.model.common.CdmMetaData)
1152 public void saveMetaData(CdmMetaData cdmMetaData
) {
1153 getSession().saveOrUpdate(cdmMetaData
);
1157 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getMetaData()
1159 public List
<CdmMetaData
> getMetaData() {
1160 Session session
= getSession();
1161 Criteria crit
= session
.createCriteria(CdmMetaData
.class);
1162 List
<CdmMetaData
> results
= crit
.list();