1
16 package org.springframework.data.jpa.mapping;
17
18 import java.lang.annotation.Annotation;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashSet;
22 import java.util.Set;
23
24 import javax.persistence.*;
25 import javax.persistence.metamodel.Metamodel;
26
27 import org.springframework.core.annotation.AnnotationUtils;
28 import org.springframework.data.annotation.AccessType.Type;
29 import org.springframework.data.jpa.util.JpaMetamodel;
30 import org.springframework.data.mapping.Association;
31 import org.springframework.data.mapping.PersistentEntity;
32 import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
33 import org.springframework.data.mapping.model.Property;
34 import org.springframework.data.mapping.model.SimpleTypeHolder;
35 import org.springframework.data.util.ClassTypeInformation;
36 import org.springframework.data.util.Lazy;
37 import org.springframework.data.util.TypeInformation;
38 import org.springframework.lang.Nullable;
39 import org.springframework.util.Assert;
40
41
51 class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPersistentProperty>
52 implements JpaPersistentProperty {
53
54 private static final Collection<Class<? extends Annotation>> ASSOCIATION_ANNOTATIONS;
55 private static final Collection<Class<? extends Annotation>> ID_ANNOTATIONS;
56 private static final Collection<Class<? extends Annotation>> UPDATEABLE_ANNOTATIONS;
57
58 static {
59
60 Set<Class<? extends Annotation>> annotations = new HashSet<Class<? extends Annotation>>();
61 annotations.add(OneToMany.class);
62 annotations.add(OneToOne.class);
63 annotations.add(ManyToMany.class);
64 annotations.add(ManyToOne.class);
65
66 ASSOCIATION_ANNOTATIONS = Collections.unmodifiableSet(annotations);
67
68 annotations = new HashSet<Class<? extends Annotation>>();
69 annotations.add(Id.class);
70 annotations.add(EmbeddedId.class);
71
72 ID_ANNOTATIONS = Collections.unmodifiableSet(annotations);
73
74 annotations = new HashSet<Class<? extends Annotation>>();
75 annotations.add(Column.class);
76 annotations.add(OrderColumn.class);
77
78 UPDATEABLE_ANNOTATIONS = Collections.unmodifiableSet(annotations);
79 }
80
81 private final @Nullable Boolean usePropertyAccess;
82 private final @Nullable TypeInformation<?> associationTargetType;
83 private final boolean updateable;
84
85 private final Lazy<Boolean> isIdProperty;
86 private final Lazy<Boolean> isAssociation;
87 private final Lazy<Boolean> isEntity;
88
89
97 public JpaPersistentPropertyImpl(JpaMetamodel metamodel, Property property,
98 PersistentEntity<?, JpaPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
99
100 super(property, owner, simpleTypeHolder);
101
102 Assert.notNull(metamodel, "Metamodel must not be null!");
103
104 this.isAssociation = Lazy.of(() -> ASSOCIATION_ANNOTATIONS.stream().anyMatch(this::isAnnotationPresent));
105 this.usePropertyAccess = detectPropertyAccess();
106 this.associationTargetType = detectAssociationTargetType();
107 this.updateable = detectUpdatability();
108
109 this.isIdProperty = Lazy.of(() -> ID_ANNOTATIONS.stream().anyMatch(it -> isAnnotationPresent(it))
110 || metamodel.isSingleIdAttribute(getOwner().getType(), getName(), getType()));
111 this.isEntity = Lazy.of(() -> metamodel.isJpaManaged(getActualType()));
112 }
113
114
118 @Override
119 public Class<?> getActualType() {
120 return associationTargetType != null ? associationTargetType.getType() : super.getActualType();
121 }
122
123
127 @Override
128 public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
129
130 return associationTargetType != null
131 ? Collections.singleton(associationTargetType)
132 : super.getPersistentEntityTypes();
133 }
134
135
139 @Override
140 public boolean isIdProperty() {
141 return isIdProperty.get();
142 }
143
144
148 @Override
149 public boolean isEntity() {
150 return isEntity.get();
151 }
152
153
157 @Override
158 public boolean isAssociation() {
159 return isAssociation.get();
160 }
161
162
166 @Override
167 public boolean isTransient() {
168 return isAnnotationPresent(Transient.class) || super.isTransient();
169 }
170
171
175 @Override
176 protected Association<JpaPersistentProperty> createAssociation() {
177 return new Association<JpaPersistentProperty>(this, null);
178 }
179
180
184 @Override
185 public boolean usePropertyAccess() {
186 return usePropertyAccess != null ? usePropertyAccess : super.usePropertyAccess();
187 }
188
189
193 @Override
194 public boolean isVersionProperty() {
195 return isAnnotationPresent(Version.class);
196 }
197
198
202 @Override
203 public boolean isWritable() {
204 return updateable && super.isWritable();
205 }
206
207
211 @Override
212 public boolean isEmbeddable() {
213 return isAnnotationPresent(Embedded.class) || hasActualTypeAnnotation(Embeddable.class);
214 }
215
216
224 @Nullable
225 private Boolean detectPropertyAccess() {
226
227 org.springframework.data.annotation.AccessType accessType = findAnnotation(
228 org.springframework.data.annotation.AccessType.class);
229
230 if (accessType != null) {
231 return Type.PROPERTY.equals(accessType.value());
232 }
233
234 Access access = findAnnotation(Access.class);
235
236 if (access != null) {
237 return AccessType.PROPERTY.equals(access.value());
238 }
239
240 accessType = findPropertyOrOwnerAnnotation(org.springframework.data.annotation.AccessType.class);
241
242 if (accessType != null) {
243 return Type.PROPERTY.equals(accessType.value());
244 }
245
246 access = findPropertyOrOwnerAnnotation(Access.class);
247
248 if (access != null) {
249 return AccessType.PROPERTY.equals(access.value());
250 }
251
252 return null;
253 }
254
255
260 @Nullable
261 private TypeInformation<?> detectAssociationTargetType() {
262
263 if (!isAssociation()) {
264 return null;
265 }
266
267 for (Class<? extends Annotation> annotationType : ASSOCIATION_ANNOTATIONS) {
268
269 Annotation annotation = findAnnotation(annotationType);
270
271 if (annotation == null) {
272 continue;
273 }
274
275 Object entityValue = AnnotationUtils.getValue(annotation, "targetEntity");
276
277 if (entityValue == null || entityValue.equals(void.class)) {
278 continue;
279 }
280
281 return ClassTypeInformation.from((Class<?>) entityValue);
282 }
283
284 return null;
285 }
286
287
293 private boolean detectUpdatability() {
294
295 for (Class<? extends Annotation> annotationType : UPDATEABLE_ANNOTATIONS) {
296
297 Annotation annotation = findAnnotation(annotationType);
298
299 if (annotation == null) {
300 continue;
301 }
302
303 return (boolean) AnnotationUtils.getValue(annotation, "updatable");
304 }
305
306 return true;
307 }
308 }
309