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
.Modifier
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Collection
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
21 import java
.util
.UUID
;
23 import org
.apache
.commons
.lang
.StringUtils
;
24 import org
.apache
.log4j
.Logger
;
25 import org
.hibernate
.Criteria
;
26 import org
.hibernate
.HibernateException
;
27 import org
.hibernate
.Query
;
28 import org
.hibernate
.Session
;
29 import org
.hibernate
.SessionFactory
;
30 import org
.hibernate
.criterion
.Criterion
;
31 import org
.hibernate
.criterion
.Order
;
32 import org
.hibernate
.criterion
.Restrictions
;
33 import org
.hibernate
.engine
.spi
.SessionFactoryImplementor
;
34 import org
.hibernate
.internal
.SessionImpl
;
35 import org
.hibernate
.metadata
.ClassMetadata
;
36 import org
.hibernate
.sql
.JoinType
;
37 import org
.hibernate
.type
.BooleanType
;
38 import org
.hibernate
.type
.CollectionType
;
39 import org
.hibernate
.type
.ComponentType
;
40 import org
.hibernate
.type
.DoubleType
;
41 import org
.hibernate
.type
.EntityType
;
42 import org
.hibernate
.type
.EnumType
;
43 import org
.hibernate
.type
.FloatType
;
44 import org
.hibernate
.type
.ForeignKeyDirection
;
45 import org
.hibernate
.type
.IntegerType
;
46 import org
.hibernate
.type
.LongType
;
47 import org
.hibernate
.type
.MaterializedClobType
;
48 import org
.hibernate
.type
.OneToOneType
;
49 import org
.hibernate
.type
.SerializableType
;
50 import org
.hibernate
.type
.StringType
;
51 import org
.hibernate
.type
.Type
;
52 import org
.jadira
.usertype
.dateandtime
.joda
.PersistentDateTime
;
53 import org
.springframework
.dao
.DataAccessException
;
54 import org
.springframework
.stereotype
.Repository
;
55 import org
.springframework
.util
.ReflectionUtils
;
57 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
58 import eu
.etaxonomy
.cdm
.common
.DoubleResult
;
59 import eu
.etaxonomy
.cdm
.database
.data
.FullCoverageDataGenerator
;
60 import eu
.etaxonomy
.cdm
.hibernate
.BigDecimalUserType
;
61 import eu
.etaxonomy
.cdm
.hibernate
.DOIUserType
;
62 import eu
.etaxonomy
.cdm
.hibernate
.EnumSetUserType
;
63 import eu
.etaxonomy
.cdm
.hibernate
.EnumUserType
;
64 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
65 import eu
.etaxonomy
.cdm
.hibernate
.OrcidUserType
;
66 import eu
.etaxonomy
.cdm
.hibernate
.PartialUserType
;
67 import eu
.etaxonomy
.cdm
.hibernate
.SeverityUserType
;
68 import eu
.etaxonomy
.cdm
.hibernate
.ShiftUserType
;
69 import eu
.etaxonomy
.cdm
.hibernate
.URIUserType
;
70 import eu
.etaxonomy
.cdm
.hibernate
.UUIDUserType
;
71 import eu
.etaxonomy
.cdm
.hibernate
.WSDLDefinitionUserType
;
72 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
73 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
74 import eu
.etaxonomy
.cdm
.model
.metadata
.CdmMetaData
;
75 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
76 import eu
.etaxonomy
.cdm
.persistence
.dto
.ReferencingObjectDto
;
77 import eu
.etaxonomy
.cdm
.strategy
.match
.CacheMatcher
;
78 import eu
.etaxonomy
.cdm
.strategy
.match
.DefaultMatchStrategy
;
79 import eu
.etaxonomy
.cdm
.strategy
.match
.FieldMatcher
;
80 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchStrategy
;
81 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
82 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchException
;
83 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
84 import eu
.etaxonomy
.cdm
.strategy
.match
.Matching
;
85 import eu
.etaxonomy
.cdm
.strategy
.merge
.IMergeStrategy
;
86 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeException
;
89 public class CdmGenericDaoImpl
90 extends CdmEntityDaoBase
<CdmBase
>
91 implements ICdmGenericDao
{
93 private static final Logger logger
= Logger
.getLogger(CdmGenericDaoImpl
.class);
95 private Set
<Class
<?
extends CdmBase
>> allCdmClasses
= null;
96 private final Map
<Class
<?
extends CdmBase
>, Set
<ReferenceHolder
>> referenceMap
= new HashMap
<>();
99 protected class ReferenceHolder
{
101 Class
<?
extends CdmBase
> otherClass
;
103 Class
<?
> targetClass
; //new as item class is used for isCollection we have a duplicate here
104 public boolean isCollection(){return itemClass
!= null;}
106 public String
toString(){return otherClass
.getSimpleName() + "." + propertyName
;}
109 public CdmGenericDaoImpl() {
110 super(CdmBase
.class);
114 private List
<ReferencingObjectDto
> getCdmBasesByFieldAndClassDto(Class
<?
extends CdmBase
> clazz
, String propertyName
,
115 CdmBase referencedCdmBase
, Integer limit
){
117 Query query
= getSession().createQuery("SELECT new eu.etaxonomy.cdm.persistence.dto.ReferencingObjectDto(this.uuid, this.id) "
118 + "FROM "+ clazz
.getSimpleName() + " this "
119 + "WHERE this." + propertyName
+" = :referencedObject")
120 .setEntity("referencedObject", referencedCdmBase
);
123 query
.setMaxResults(limit
);
125 @SuppressWarnings("unchecked")
126 List
<ReferencingObjectDto
> result
= query
.list();
127 result
.forEach(dto
->dto
.setType((Class
<CdmBase
>)clazz
));
132 public List
<CdmBase
> getCdmBasesByFieldAndClass(Class
<?
extends CdmBase
> clazz
, String propertyName
, CdmBase referencedCdmBase
, Integer limit
){
133 Session session
= super.getSession();
135 Criteria criteria
= session
.createCriteria(clazz
);
136 criteria
.add(Restrictions
.eq(propertyName
, referencedCdmBase
));
138 criteria
.setMaxResults(limit
);
141 @SuppressWarnings("unchecked")
142 List
<CdmBase
> result
= criteria
.list();
147 public long getCountByFieldAndClass(Class
<?
extends CdmBase
> clazz
, String propertyName
, CdmBase referencedCdmBase
){
148 Query query
= getSession().createQuery("SELECT count(this) "
149 + "FROM "+ clazz
.getSimpleName() + " this "
150 + "WHERE this." + propertyName
+" = :referencedObject")
151 .setEntity("referencedObject", referencedCdmBase
);
153 long result
=(Long
)query
.uniqueResult();
158 public List
<ReferencingObjectDto
> getCdmBasesWithItemInCollectionDto(Class
<?
> itemClass
,
159 Class
<?
extends CdmBase
> clazz
, String propertyName
, CdmBase item
, Integer limit
){
161 String queryStr
= withItemInCollectionHql(itemClass
, clazz
, propertyName
,
162 "new eu.etaxonomy.cdm.persistence.dto.ReferencingObjectDto(other.uuid, other.id)");
163 Query query
= getSession().createQuery(queryStr
).setEntity("referencedObject", item
);
165 query
.setMaxResults(limit
);
167 @SuppressWarnings("unchecked")
168 List
<ReferencingObjectDto
> result
= query
.list();
169 result
.forEach(dto
->dto
.setType((Class
)clazz
));
174 public List
<CdmBase
> getCdmBasesWithItemInCollection(Class
<?
> itemClass
,
175 Class
<?
> clazz
, String propertyName
, CdmBase item
, Integer limit
){
177 String queryStr
= withItemInCollectionHql(itemClass
, clazz
, propertyName
, "other");
178 Query query
= getSession().createQuery(queryStr
).setEntity("referencedObject", item
);
180 query
.setMaxResults(limit
);
182 @SuppressWarnings("unchecked")
183 List
<CdmBase
> result
= query
.list();
187 private String
withItemInCollectionHql(Class
<?
> itemClass
, Class
<?
> clazz
, String propertyName
, String select
) {
188 String thisClassStr
= itemClass
.getSimpleName();
189 String otherClassStr
= clazz
.getSimpleName();
190 String result
= "SELECT "+select
+" FROM "+ thisClassStr
+ " this, " + otherClassStr
+ " other " +
191 " WHERE this = :referencedObject AND this member of other." + propertyName
;
196 public long getCountWithItemInCollection(Class
<?
> itemClass
, Class
<?
> clazz
, String propertyName
,
199 String queryStr
= withItemInCollectionHql(itemClass
, clazz
, propertyName
, "count(this)");
201 Query query
= getSession().createQuery(queryStr
).setEntity("referencedObject", item
);
202 long result
=(Long
)query
.uniqueResult();
207 public Set
<Class
<?
extends CdmBase
>> getAllPersistedClasses(boolean includeAbstractClasses
){
208 Set
<Class
<?
extends CdmBase
>> result
= new HashSet
<>();
210 SessionFactory sessionFactory
= getSession().getSessionFactory();
211 Map
<String
,?
> allClassMetadata
= sessionFactory
.getAllClassMetadata();
212 Collection
<String
> keys
= allClassMetadata
.keySet();
213 for (String strKey
: keys
){
214 if (! strKey
.endsWith("_AUD")){
216 Class
<?
> clazz
= Class
.forName(strKey
);
217 boolean isAbstractClass
= Modifier
.isAbstract(clazz
.getModifiers());
218 if (! isAbstractClass
|| includeAbstractClasses
){
219 result
.add((Class
)clazz
);
221 } catch (ClassNotFoundException e
) {
222 String message
= "Persisted CDM class not found: " + strKey
;
223 logger
.warn(message
);
224 //TODO better throw exception, but currently some keys are really not found yet
225 // throw new RuntimeException("Persisted CDM class not found: " + strKey,e);
233 public Set
<ReferencingObjectDto
> getReferencingObjectsDto(CdmBase referencedCdmBase
){
234 Set
<ReferencingObjectDto
> result
= new HashSet
<>();
235 if (referencedCdmBase
== null) {
240 referencedCdmBase
= HibernateProxyHelper
.deproxy(referencedCdmBase
);
241 Class
<?
extends CdmBase
> referencedClass
= referencedCdmBase
.getClass();
243 Set
<ReferenceHolder
> holderSet
= getOrMakeHolderSet(referencedClass
);
244 for (ReferenceHolder refHolder
: holderSet
){
245 handleReferenceHolderDto(referencedCdmBase
, result
, refHolder
, false);
248 } catch (Exception e
) {
250 throw new RuntimeException(e
);
255 public Set
<CdmBase
> getReferencingObjects(CdmBase referencedCdmBase
){
256 Set
<CdmBase
> result
= new HashSet
<>();
257 if (referencedCdmBase
== null) {
262 referencedCdmBase
= HibernateProxyHelper
.deproxy(referencedCdmBase
);
263 Class
<?
extends CdmBase
> referencedClass
= referencedCdmBase
.getClass();
265 Set
<ReferenceHolder
> holderSet
= getOrMakeHolderSet(referencedClass
);
266 //Integer count = getReferencingObjectsCount(referencedCdmBase);
267 for (ReferenceHolder refHolder
: holderSet
){
268 // if (count > 100000) {
269 // handleReferenceHolder(referencedCdmBase, result, refHolder, true);
271 handleReferenceHolder(referencedCdmBase
, result
, refHolder
, false);
275 } catch (Exception e
) {
277 throw new RuntimeException(e
);
282 public long getReferencingObjectsCount(CdmBase referencedCdmBase
){
284 if (referencedCdmBase
== null) {
289 referencedCdmBase
= HibernateProxyHelper
.deproxy(referencedCdmBase
);
290 Class
<?
extends CdmBase
> referencedClass
= referencedCdmBase
.getClass();
292 Set
<ReferenceHolder
> holderSet
= getOrMakeHolderSet(referencedClass
);
293 for (ReferenceHolder refHolder
: holderSet
){
294 result
=+ handleReferenceHolderForCount(referencedCdmBase
, result
, refHolder
);
297 } catch (Exception e
) {
299 throw new RuntimeException(e
);
304 protected Set
<ReferenceHolder
> getOrMakeHolderSet(
305 Class
<?
extends CdmBase
> referencedClass
)
306 throws ClassNotFoundException
, NoSuchFieldException
{
307 Set
<ReferenceHolder
> holderSet
= referenceMap
.get(referencedClass
);
308 if (holderSet
== null){
309 holderSet
= makeHolderSet(referencedClass
);
310 referenceMap
.put(referencedClass
, holderSet
);
316 public Set
<CdmBase
> getReferencingObjectsForDeletion(CdmBase referencedCdmBase
){
317 if (referencedCdmBase
== null){
320 Set
<CdmBase
> result
= getReferencingObjects(referencedCdmBase
);
321 Set
<ReferenceHolder
> holderSet
= referenceMap
.get(IdentifiableEntity
.class);
323 if (holderSet
== null){
324 holderSet
= makeHolderSet(IdentifiableEntity
.class);
325 referenceMap
.put(IdentifiableEntity
.class, holderSet
);
327 Set
<CdmBase
> resultIdentifiableEntity
= new HashSet
<>();
328 for (ReferenceHolder refHolder
: holderSet
){
329 handleReferenceHolder(referencedCdmBase
, resultIdentifiableEntity
, refHolder
, false);
331 result
.removeAll(resultIdentifiableEntity
);
334 } catch (Exception e
) {
336 throw new RuntimeException(e
);
340 private void handleReferenceHolderDto(CdmBase referencedCdmBase
,
341 Set
<ReferencingObjectDto
> result
, ReferenceHolder refHolder
, boolean limited
) {
343 boolean isCollection
= refHolder
.isCollection();
346 result
.addAll(getCdmBasesWithItemInCollectionDto(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, 100));
348 result
.addAll(getCdmBasesWithItemInCollectionDto(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, null));
352 result
.addAll(getCdmBasesByFieldAndClassDto(refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, 100));
354 result
.addAll(getCdmBasesByFieldAndClassDto(refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, null));
359 private void handleReferenceHolder(CdmBase referencedCdmBase
,
360 Set
<CdmBase
> result
, ReferenceHolder refHolder
, boolean limited
) {
362 boolean isCollection
= refHolder
.isCollection();
365 result
.addAll(getCdmBasesWithItemInCollection(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, 100));
367 result
.addAll(getCdmBasesWithItemInCollection(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, null));
371 result
.addAll(getCdmBasesByFieldAndClass(refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, 100));
373 result
.addAll(getCdmBasesByFieldAndClass(refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
, null));
378 private Long
handleReferenceHolderForCount(CdmBase referencedCdmBase
,
379 Long result
, ReferenceHolder refHolder
) {
380 boolean isCollection
= refHolder
.isCollection();
382 result
+= getCountWithItemInCollection(refHolder
.itemClass
, refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
);
384 result
+= getCountByFieldAndClass(refHolder
.otherClass
, refHolder
.propertyName
, referencedCdmBase
);
390 * @param referencedClass
392 * @throws NoSuchFieldException
393 * @throws ClassNotFoundException
395 protected Set
<ReferenceHolder
> makeHolderSet(Class
<?
> referencedClass
) throws ClassNotFoundException
, NoSuchFieldException
{
396 Set
<ReferenceHolder
> result
= new HashSet
<>();
399 if (allCdmClasses
== null){
400 allCdmClasses
= getAllPersistedClasses(false); //findAllCdmClasses();
402 SessionFactory sessionFactory
= getSession().getSessionFactory();
404 for (Class
<?
extends CdmBase
> cdmClass
: allCdmClasses
){
405 ClassMetadata classMetadata
= sessionFactory
.getClassMetadata(cdmClass
);
406 Type
[] propertyTypes
= classMetadata
.getPropertyTypes();
408 for (Type propertyType
: propertyTypes
){
409 String propertyName
= classMetadata
.getPropertyNames()[propertyNr
];
410 makePropertyType(result
, referencedClass
, sessionFactory
, cdmClass
, propertyType
, propertyName
, false);
417 private void makePropertyType(
418 // CdmBase referencedCdmBase,
419 Set
<ReferenceHolder
> result
,
420 Class
<?
> referencedClass
,
421 SessionFactory sessionFactory
, Class
<?
extends CdmBase
> cdmClass
,
422 Type propertyType
, String propertyName
, boolean isCollection
)
423 throws ClassNotFoundException
, NoSuchFieldException
{
425 if (propertyType
.isEntityType()){
426 EntityType entityType
= (EntityType
)propertyType
;
427 String associatedEntityName
= entityType
.getAssociatedEntityName();
428 Class
<?
> entityClass
= Class
.forName(associatedEntityName
);
429 if (entityClass
.isInterface()){
430 logger
.debug("There is an interface");
432 if (entityType
instanceof OneToOneType
){
433 OneToOneType oneToOneType
= (OneToOneType
)entityType
;
434 ForeignKeyDirection direction
= oneToOneType
.getForeignKeyDirection();
435 if (direction
== ForeignKeyDirection
.TO_PARENT
){ //this
439 if (entityClass
.isAssignableFrom(referencedClass
)){
440 makeSingleProperty(referencedClass
, entityClass
, propertyName
, cdmClass
, result
, isCollection
);
442 }else if (propertyType
.isCollectionType()){
443 CollectionType collectionType
= (CollectionType
)propertyType
;
444 //String role = collectionType.getRole();
445 Type elType
= collectionType
.getElementType((SessionFactoryImplementor
)sessionFactory
);
446 makePropertyType(result
, referencedClass
, sessionFactory
, cdmClass
, elType
, propertyName
, true);
447 }else if (propertyType
.isAnyType()){
448 // AnyType anyType = (AnyType)propertyType;
449 Field field
= cdmClass
.getDeclaredField(propertyName
);
450 Class
<?
> returnType
= field
.getType();
451 if (returnType
.isInterface()){
452 logger
.debug("There is an interface");
454 if (returnType
.isAssignableFrom(referencedClass
)){
455 makeSingleProperty(referencedClass
, returnType
, propertyName
, cdmClass
, result
, isCollection
);
457 }else if (propertyType
.isComponentType()){
458 ComponentType componentType
= (ComponentType
)propertyType
;
459 Type
[] subTypes
= componentType
.getSubtypes();
460 // Field field = cdmClass.getDeclaredField(propertyName);
461 // Class returnType = field.getType();
463 for (Type subType
: subTypes
){
464 String subPropertyName
= componentType
.getPropertyNames()[propertyNr
];
465 if (!isNoDoType(subType
)){
466 logger
.warn("SubType not yet handled: " + subType
);
468 // handlePropertyType(referencedCdmBase, result, referencedClass,
469 // sessionFactory, cdmClass, subType, subPropertyName, isCollection);
472 }else if (isNoDoType(propertyType
)){
475 logger
.warn("propertyType not yet handled: " + propertyType
.getName());
478 // if (! type.isInterface()){
479 // if (referencedClass.isAssignableFrom(type)||
480 // type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
481 // handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
484 // }else if (type.isAssignableFrom(referencedClass)){
485 // handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
489 private boolean makeSingleProperty(Class
<?
> itemClass
, Class
<?
> type
, String propertyName
, Class
<?
extends CdmBase
> cdmClass
, Set
<ReferenceHolder
> result
,/*CdmBase item,*/ boolean isCollection
){
490 // String fieldName = StringUtils.rightPad(propertyName, 30);
491 // String className = StringUtils.rightPad(cdmClass.getSimpleName(), 30);
492 // String returnTypeName = StringUtils.rightPad(type.getSimpleName(), 30);
494 // logger.debug(fieldName + "\t\t" + className + "\t\t" + returnTypeName);
495 ReferenceHolder refHolder
= new ReferenceHolder();
496 refHolder
.propertyName
= propertyName
;
497 refHolder
.otherClass
= cdmClass
;
498 refHolder
.itemClass
= (isCollection ? itemClass
: null) ;
499 refHolder
.targetClass
= type
;
501 result
.add(refHolder
);
505 protected static boolean isNoDoType(Type propertyType
) {
506 boolean result
= false;
507 Class
<?
>[] classes
= new Class
[]{
508 PersistentDateTime
.class,
509 WSDLDefinitionUserType
.class,
511 PartialUserType
.class,
515 MaterializedClobType
.class,
518 SerializableType
.class,
526 EnumSetUserType
.class,
527 SeverityUserType
.class,
528 BigDecimalUserType
.class,
530 Set
<String
> classNames
= new HashSet
<>();
531 for (Class
<?
> clazz
: classes
){
532 classNames
.add(clazz
.getCanonicalName());
533 if (clazz
== propertyType
.getClass()){
537 String propertyTypeClassName
= propertyType
.getName();
538 if (classNames
.contains(propertyTypeClassName
)){
545 public List
<CdmBase
> getHqlResult(String hqlQuery
, Object
[] params
){
546 Query query
= getSession().createQuery(hqlQuery
);
547 for(int i
= 0; i
<params
.length
; i
++){
548 query
.setParameter(String
.valueOf(i
), params
[i
]); //for some reason using int, not String, throws exceptions, this seems to be a hibernate bug
550 @SuppressWarnings("unchecked")
551 List
<CdmBase
> result
= query
.list();
556 public Query
getHqlQuery(String hqlQuery
){
557 Query query
= getSession().createQuery(hqlQuery
);
562 public <T
extends CdmBase
> void merge(T cdmBase1
, T cdmBase2
, IMergeStrategy mergeStrategy
) throws MergeException
{
563 SessionImpl session
= (SessionImpl
) getSession();
565 DeduplicationHelper helper
= new DeduplicationHelper(session
, this);
566 helper
.merge(cdmBase1
, cdmBase2
, mergeStrategy
);
571 public <T
extends CdmBase
> boolean isMergeable(T cdmBase1
, T cdmBase2
, IMergeStrategy mergeStrategy
) throws MergeException
{
572 SessionImpl session
= (SessionImpl
) getSession();
573 DeduplicationHelper helper
= new DeduplicationHelper(session
, this);
574 return helper
.isMergeable(cdmBase1
, cdmBase2
, mergeStrategy
);
579 public <T
extends CdmBase
> T
find(Class
<T
> clazz
, int id
){
581 session
= getSession();
582 //session = getSession().getSessionFactory().getCurrentSession();
583 T o
= session
.get(clazz
, id
);
588 public <T
extends CdmBase
> T
find(Class
<T
> clazz
, int id
, List
<String
> propertyPaths
){
590 session
= getSession();
591 T bean
= session
.get(clazz
, id
);
595 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
600 public <T
extends CdmBase
> T
find(Class
<T
> clazz
, UUID uuid
){
601 return find(clazz
, uuid
, null);
605 public <T
extends CdmBase
> T
find(Class
<T
> clazz
, UUID uuid
, List
<String
> propertyPaths
){
606 Session session
= getSession();
607 Criteria crit
= session
.createCriteria(type
);
608 crit
.add(Restrictions
.eq("uuid", uuid
));
609 crit
.addOrder(Order
.desc("created"));
610 @SuppressWarnings("unchecked")
611 List
<T
> results
= crit
.list();
612 if (results
.isEmpty()){
615 if(results
.size() > 1){
616 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
618 T result
= results
.get(0);
619 if (result
== null || propertyPaths
== null){
622 defaultBeanInitializer
.initialize(result
, propertyPaths
);
629 public <T
extends IMatchable
> List
<T
> findMatching(T objectToMatch
,
630 IMatchStrategy matchStrategy
) throws MatchException
{
632 getSession().flush();
634 List
<T
> result
= new ArrayList
<>();
635 if(objectToMatch
== null){
638 if (matchStrategy
== null){
639 matchStrategy
= DefaultMatchStrategy
.NewInstance(objectToMatch
.getClass());
641 result
.addAll(findMatchingNullSafe(objectToMatch
, matchStrategy
));
643 } catch (IllegalArgumentException e
) {
644 throw new MatchException(e
);
645 } catch (IllegalAccessException e
) {
646 throw new MatchException(e
);
650 private <T
extends IMatchable
> List
<T
> findMatchingNullSafe(T objectToMatch
,
651 IMatchStrategy matchStrategy
) throws IllegalArgumentException
, IllegalAccessException
, MatchException
{
653 List
<T
> result
= new ArrayList
<>();
654 Session session
= getSession();
655 Class
<?
> matchClass
= objectToMatch
.getClass();
656 ClassMetadata classMetaData
= session
.getSessionFactory().getClassMetadata(matchClass
.getCanonicalName());
657 Criteria criteria
= session
.createCriteria(matchClass
);
658 boolean noMatch
= makeCriteria(objectToMatch
, matchStrategy
, classMetaData
, criteria
);
659 if (logger
.isDebugEnabled()){logger
.debug(criteria
);}
661 if (noMatch
== false){
662 @SuppressWarnings("unchecked")
663 List
<T
> matchCandidates
= criteria
.list();
664 matchCandidates
.remove(objectToMatch
);
665 for (T matchCandidate
: matchCandidates
){
666 if (matchStrategy
.invoke(objectToMatch
, matchCandidate
).isSuccessful()){
667 result
.add(matchCandidate
);
669 logger
.info("Match candidate did not match: " + matchCandidate
);
676 private <T
> boolean makeCriteria(T objectToMatch
,
677 IMatchStrategy matchStrategy
, ClassMetadata classMetaData
,
678 Criteria criteria
) throws IllegalAccessException
, MatchException
{
680 Matching matching
= matchStrategy
.getMatching((IMatchable
)objectToMatch
);
681 boolean noMatch
= false;
682 Map
<String
, List
<MatchMode
>> replaceMatchers
= new HashMap
<>();
683 for (CacheMatcher cacheMatcher
: matching
.getCacheMatchers()){
684 Field protectedField
= cacheMatcher
.getProtectedField(matching
);
685 boolean cacheProtected
= protectedField
== null ?
false : (Boolean
)protectedField
.get(objectToMatch
);
686 if (cacheProtected
== true){
687 String cacheValue
= (String
)cacheMatcher
.getField().get(objectToMatch
);
688 if (StringUtils
.isBlank(cacheValue
)){
689 return true; //no match
691 criteria
.add(Restrictions
.eq(cacheMatcher
.getPropertyName(), cacheValue
));
692 criteria
.add(Restrictions
.eq(cacheMatcher
.getProtectedPropertyName(), cacheProtected
));
694 List
<DoubleResult
<String
, MatchMode
>> replacementModes
= cacheMatcher
.getReplaceMatchModes(matching
);
695 for (DoubleResult
<String
, MatchMode
> replacementMode
: replacementModes
){
696 String propertyName
= replacementMode
.getFirstResult();
697 List
<MatchMode
> replaceMatcherList
= replaceMatchers
.get(propertyName
);
698 if (replaceMatcherList
== null){
699 replaceMatcherList
= new ArrayList
<>();
700 replaceMatchers
.put(propertyName
, replaceMatcherList
);
702 replaceMatcherList
.add(replacementMode
.getSecondResult());
708 for (FieldMatcher fieldMatcher
: matching
.getFieldMatchers(false)){
709 String propertyName
= fieldMatcher
.getPropertyName();
710 Type propertyType
= classMetaData
.getPropertyType(propertyName
);
711 Object value
= fieldMatcher
.getField().get(objectToMatch
);
712 List
<MatchMode
> matchModes
= new ArrayList
<>();
713 matchModes
.add(fieldMatcher
.getMatchMode());
714 if (replaceMatchers
.get(propertyName
) != null){
715 matchModes
.addAll(replaceMatchers
.get(propertyName
));
718 boolean isIgnore
= false;
719 for (MatchMode matchMode
: matchModes
){
720 isIgnore
|= matchMode
.isIgnore(value
);
723 if (propertyType
.isComponentType()){
724 matchComponentType(criteria
, fieldMatcher
, propertyName
, value
, matchModes
);
726 noMatch
= matchNonComponentType(criteria
, fieldMatcher
, propertyName
, value
, matchModes
, propertyType
);
736 private void matchComponentType(Criteria criteria
,
737 FieldMatcher fieldMatcher
, String propertyName
, Object value
,
738 List
<MatchMode
> matchModes
) throws MatchException
, IllegalAccessException
{
740 boolean requiresSecondNull
= requiresSecondNull(matchModes
, null);
741 if (requiresSecondNull
){
742 criteria
.add(Restrictions
.isNull(propertyName
));
744 //this should not happen, should be handled as ignore before
745 logger
.warn("Component type not yet implemented for (null) value: " + propertyName
);
746 throw new MatchException("Component type not yet fully implemented for (null) value. Property: " + propertyName
);
749 Class
<?
> componentClass
= fieldMatcher
.getField().getType();
750 Map
<String
, Field
> fields
= CdmUtils
.getAllFields(componentClass
, Object
.class, false, false, true, false);
751 for (String fieldName
: fields
.keySet()){
752 String restrictionPath
= propertyName
+"."+fieldName
;
753 Object componentValue
= fields
.get(fieldName
).get(value
);
754 //TODO differentiate matchMode
755 createCriterion(criteria
, restrictionPath
, componentValue
, matchModes
);
760 private boolean matchNonComponentType(Criteria criteria
,
761 FieldMatcher fieldMatcher
,
764 List
<MatchMode
> matchModes
,
766 throws HibernateException
, DataAccessException
, MatchException
, IllegalAccessException
{
768 boolean noMatch
= false;
769 if (isRequired(matchModes
) && value
== null){
772 }else if (requiresSecondNull(matchModes
,value
)){
773 criteria
.add(Restrictions
.isNull(propertyName
));
775 if (isMatch(matchModes
)){
776 if (propertyType
.isCollectionType()){
777 //TODO collection not yet handled for match
779 JoinType joinType
= JoinType
.INNER_JOIN
;
780 if (! requiresSecondValue(matchModes
,value
)){
781 joinType
= JoinType
.LEFT_OUTER_JOIN
;
783 Criteria matchCriteria
= criteria
.createCriteria(propertyName
, joinType
).add(Restrictions
.isNotNull("id"));
784 @SuppressWarnings("rawtypes")
785 Class matchClass
= value
.getClass();
786 if (IMatchable
.class.isAssignableFrom(matchClass
)){
787 IMatchStrategy valueMatchStrategy
= fieldMatcher
.getMatchStrategy() != null? fieldMatcher
.getMatchStrategy() : DefaultMatchStrategy
.NewInstance(matchClass
);
788 ClassMetadata valueClassMetaData
= getSession().getSessionFactory().getClassMetadata(matchClass
.getCanonicalName());
789 noMatch
= makeCriteria(value
, valueMatchStrategy
, valueClassMetaData
, matchCriteria
);
791 logger
.error("Class to match (" + matchClass
+ ") is not of type IMatchable");
792 throw new MatchException("Class to match (" + matchClass
+ ") is not of type IMatchable");
795 }else if (isEqual(matchModes
)){
796 createCriterion(criteria
, propertyName
, value
, matchModes
);
798 logger
.warn("Unhandled match mode: " + matchModes
+ ", value: " + (value
==null?
"null":value
));
804 private void createCriterion(Criteria criteria
, String propertyName
,
805 Object value
, List
<MatchMode
> matchModes
) throws MatchException
{
806 Criterion finalRestriction
= null;
807 Criterion equalRestriction
= Restrictions
.eq(propertyName
, value
);
808 Criterion nullRestriction
= Restrictions
.isNull(propertyName
);
809 if (this.requiresSecondValue(matchModes
, value
)){
810 finalRestriction
= equalRestriction
;
811 }else if (requiresSecondNull(matchModes
, value
) ){
812 finalRestriction
= nullRestriction
;
814 finalRestriction
= Restrictions
.or(equalRestriction
, nullRestriction
);
816 //return finalRestriction;
817 criteria
.add(finalRestriction
);
820 private boolean requiresSecondNull(List
<MatchMode
> matchModes
, Object value
) throws MatchException
{
821 boolean result
= true;
822 for (MatchMode matchMode
: matchModes
){
823 result
&= matchMode
.requiresSecondNull(value
);
828 private boolean requiresSecondValue(List
<MatchMode
> matchModes
, Object value
) {
829 boolean result
= true;
830 for (MatchMode matchMode
: matchModes
){
831 result
&= matchMode
.requiresSecondValue(value
);
836 private boolean isRequired(List
<MatchMode
> matchModes
) {
837 boolean result
= true;
838 for (MatchMode matchMode
: matchModes
){
839 result
&= matchMode
.isRequired();
845 * Returns true if at least one match mode is of type MATCH_XXX
849 * @throws MatchException
851 private boolean isMatch(List
<MatchMode
> matchModes
) throws MatchException
{
852 boolean result
= false;
853 for (MatchMode matchMode
: matchModes
){
854 result
|= matchMode
.isMatch();
860 * Returns true if at least one match mode is of typ EQUAL_XXX
864 * @throws MatchException
866 private boolean isEqual(List
<MatchMode
> matchModes
) throws MatchException
{
867 boolean result
= false;
868 for (MatchMode matchMode
: matchModes
){
869 result
|= matchMode
.isEqual();
875 public void saveMetaData(CdmMetaData cdmMetaData
) {
876 getSession().saveOrUpdate(cdmMetaData
);
880 public List
<CdmMetaData
> getMetaData() {
881 Session session
= getSession();
882 Criteria crit
= session
.createCriteria(CdmMetaData
.class);
883 @SuppressWarnings("unchecked")
884 List
<CdmMetaData
> results
= crit
.list();
889 public Object
initializeCollection(UUID ownerUuid
, String fieldName
, List
<String
> appendedPropertyPaths
) {
890 List
<String
> propertyPaths
= new ArrayList
<>();
891 propertyPaths
.add(fieldName
);
892 if(appendedPropertyPaths
!= null && !appendedPropertyPaths
.isEmpty()) {
893 for(String app
: appendedPropertyPaths
) {
894 propertyPaths
.add(fieldName
+ "." + app
);
897 CdmBase cdmBase
= load(ownerUuid
, propertyPaths
);
898 Field field
= ReflectionUtils
.findField(cdmBase
.getClass(), fieldName
);
899 field
.setAccessible(true);
902 obj
= field
.get(cdmBase
);
903 } catch (IllegalAccessException e
) {
904 throw new IllegalArgumentException("Requested object is not accessible");
906 if(obj
instanceof Collection
|| obj
instanceof Map
) {
909 throw new IllegalArgumentException("Field name provided does not correspond to a collection or map");
914 public Object
initializeCollection(UUID ownerUuid
, String fieldName
) {
915 return initializeCollection(ownerUuid
, fieldName
, null);
919 public boolean isEmpty(UUID ownerUuid
, String fieldName
) {
920 Object col
= initializeCollection(ownerUuid
, fieldName
);
921 if(col
instanceof Collection
) {
922 return ((Collection
<?
>)col
).isEmpty();
923 } else if(col
instanceof Map
){
924 return ((Map
<?
,?
>)col
).isEmpty();
931 public int size(UUID ownerUuid
, String fieldName
) {
932 Object col
= initializeCollection(ownerUuid
, fieldName
);
933 if(col
instanceof Collection
) {
934 return ((Collection
<?
>)col
).size();
935 } else if(col
instanceof Map
){
936 return ((Map
<?
,?
>)col
).size();
942 public Object
get(UUID ownerUuid
, String fieldName
, int index
) {
943 Object col
= initializeCollection(ownerUuid
, fieldName
);
944 if(col
instanceof List
) {
945 return ((List
<?
>)col
).get(index
);
947 throw new IllegalArgumentException("Field name provided does not correspond to a list");
952 public boolean contains(UUID ownerUuid
, String fieldName
, Object element
) {
953 Object col
= initializeCollection(ownerUuid
, fieldName
);
954 if(col
instanceof Collection
) {
955 return ((Collection
<?
>)col
).contains(element
);
957 throw new IllegalArgumentException("Field name provided does not correspond to a collection");
962 public boolean containsKey(UUID ownerUuid
, String fieldName
, Object key
) {
963 Object col
= initializeCollection(ownerUuid
, fieldName
);
964 if(col
instanceof Map
) {
965 return ((Map
<?
,?
>)col
).containsKey(key
);
967 throw new IllegalArgumentException("Field name provided does not correspond to a map");
972 public boolean containsValue(UUID ownerUuid
, String fieldName
, Object value
) {
973 Object col
= initializeCollection(ownerUuid
, fieldName
);
974 if(col
instanceof Map
) {
975 return ((Map
<?
,?
>)col
).containsValue(value
);
977 throw new IllegalArgumentException("Field name provided does not correspond to a map");
982 public void createFullSampleData() {
983 FullCoverageDataGenerator dataGenerator
= new FullCoverageDataGenerator();
984 dataGenerator
.fillWithData(getSession());
988 public List
<UUID
> listUuid(Class
<?
extends CdmBase
> clazz
) {
989 String queryString
= "SELECT uuid FROM " + clazz
.getSimpleName();
990 Query query
= getSession().createQuery(queryString
);
991 @SuppressWarnings("unchecked")
992 List
<UUID
> list
= query
.list();