-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
HibernateTraversableResolver.java
109 lines (99 loc) · 3.92 KB
/
HibernateTraversableResolver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cfg.beanvalidation;
import java.lang.annotation.ElementType;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import jakarta.validation.Path;
import jakarta.validation.TraversableResolver;
import org.hibernate.Hibernate;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
* Use Hibernate metadata to ignore cascade on entities.
* cascade on embeddable objects or collection of embeddable objects are accepted
*
* Also use Hibernate's native isInitialized method call.
*
* @author Emmanuel Bernard
*/
public class HibernateTraversableResolver implements TraversableResolver {
private Set<String> associations;
public HibernateTraversableResolver(
EntityPersister persister,
ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister,
SessionFactoryImplementor factory) {
this.associations = associationsPerEntityPersister.get( persister );
if (this.associations == null) {
this.associations = new HashSet<>();
addAssociationsToTheSetForAllProperties( persister.getPropertyNames(), persister.getPropertyTypes(), "", factory );
associationsPerEntityPersister.put( persister, associations );
}
}
private void addAssociationsToTheSetForAllProperties(String[] names, Type[] types, String prefix, SessionFactoryImplementor factory) {
final int length = names.length;
for( int index = 0 ; index < length; index++ ) {
addAssociationsToTheSetForOneProperty( names[index], types[index], prefix, factory );
}
}
private void addAssociationsToTheSetForOneProperty(String name, Type type, String prefix, SessionFactoryImplementor factory) {
if ( type.isCollectionType() ) {
CollectionType collType = (CollectionType) type;
Type assocType = collType.getElementType( factory );
addAssociationsToTheSetForOneProperty(name, assocType, prefix, factory);
}
//ToOne association
else if ( type.isEntityType() || type.isAnyType() ) {
associations.add( prefix + name );
}
else if ( type.isComponentType() ) {
CompositeType componentType = (CompositeType) type;
addAssociationsToTheSetForAllProperties(
componentType.getPropertyNames(),
componentType.getSubtypes(),
( prefix.isEmpty() ? name : prefix + name) + '.',
factory);
}
}
private String getStringBasedPath(Path.Node traversableProperty, Path pathToTraversableObject) {
StringBuilder path = new StringBuilder( );
for ( Path.Node node : pathToTraversableObject ) {
if (node.getName() != null) {
path.append( node.getName() ).append( '.' );
}
}
if ( traversableProperty.getName() == null ) {
throw new AssertionFailure(
"TraversableResolver being passed a traversableProperty with null name. pathToTraversableObject: "
+ path.toString() );
}
path.append( traversableProperty.getName() );
return path.toString();
}
public boolean isReachable(Object traversableObject,
Path.Node traversableProperty,
Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType) {
//lazy, don't load
return Hibernate.isInitialized( traversableObject )
&& Hibernate.isPropertyInitialized( traversableObject, traversableProperty.getName() );
}
public boolean isCascadable(Object traversableObject,
Path.Node traversableProperty,
Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType) {
String path = getStringBasedPath( traversableProperty, pathToTraversableObject );
return ! associations.contains(path);
}
}