1 /*
2 * Copyright 2012-2020 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.springframework.data.jpa.mapping;
17
18 import java.util.Set;
19 import java.util.function.Predicate;
20
21 import javax.persistence.metamodel.ManagedType;
22 import javax.persistence.metamodel.Metamodel;
23
24 import org.springframework.data.jpa.provider.PersistenceProvider;
25 import org.springframework.data.jpa.util.JpaMetamodel;
26 import org.springframework.data.mapping.PersistentPropertyPaths;
27 import org.springframework.data.mapping.context.AbstractMappingContext;
28 import org.springframework.data.mapping.context.MappingContext;
29 import org.springframework.data.mapping.model.Property;
30 import org.springframework.data.mapping.model.SimpleTypeHolder;
31 import org.springframework.data.util.TypeInformation;
32 import org.springframework.lang.Nullable;
33 import org.springframework.util.Assert;
34
35 /**
36 * {@link MappingContext} implementation based on a Jpa {@link Metamodel}.
37 *
38 * @author Oliver Gierke
39 * @author Christoph Strobl
40 * @author Mark Paluch
41 * @author David Madden
42 * @since 1.3
43 */
44 public class JpaMetamodelMappingContext
45 extends AbstractMappingContext<JpaPersistentEntityImpl<?>, JpaPersistentProperty> {
46
47 private final Metamodels models;
48 private final PersistenceProvider persistenceProvider;
49
50 /**
51 * Creates a new JPA {@link Metamodel} based {@link MappingContext}.
52 *
53 * @param models must not be {@literal null} or empty.
54 */
55 public JpaMetamodelMappingContext(Set<Metamodel> models) {
56
57 Assert.notNull(models, "JPA metamodel must not be null!");
58 Assert.notEmpty(models, "JPA metamodel must not be empty!");
59
60 this.models = new Metamodels(models);
61 this.persistenceProvider = PersistenceProvider.fromMetamodel(models.iterator().next());
62 }
63
64 /*
65 * (non-Javadoc)
66 * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
67 */
68 @Override
69 protected <T> JpaPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
70 return new JpaPersistentEntityImpl<T>(typeInformation, persistenceProvider, models.getMetamodel(typeInformation));
71 }
72
73 /*
74 * (non-Javadoc)
75 * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
76 */
77 @Override
78 protected JpaPersistentProperty createPersistentProperty(Property property, JpaPersistentEntityImpl<?> owner,
79 SimpleTypeHolder simpleTypeHolder) {
80 return new JpaPersistentPropertyImpl(owner.getMetamodel(), property, owner, simpleTypeHolder);
81 }
82
83 /*
84 * (non-Javadoc)
85 * @see org.springframework.data.mapping.context.AbstractMappingContext#shouldCreatePersistentEntityFor(org.springframework.data.util.TypeInformation)
86 */
87 @Override
88 protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
89 return models.isMetamodelManagedType(type);
90 }
91
92 /**
93 * We customize the lookup of {@link PersistentPropertyPaths} by also traversing properties that are embeddables.
94 *
95 * @see org.springframework.data.mapping.context.AbstractMappingContext#findPersistentPropertyPaths(java.lang.Class,
96 * java.util.function.Predicate)
97 */
98 @Override
99 public <T> PersistentPropertyPaths<T, JpaPersistentProperty> findPersistentPropertyPaths(Class<T> type,
100 Predicate<? super JpaPersistentProperty> predicate) {
101 return doFindPersistentPropertyPaths(type, predicate, it -> it.isEmbeddable());
102 }
103
104 /*
105 * (non-Javadoc)
106 * @see org.springframework.data.mapping.context.AbstractMappingContext#hasPersistentEntityFor(java.lang.Class)
107 */
108 @Override
109 public boolean hasPersistentEntityFor(Class<?> type) {
110 return super.hasPersistentEntityFor(type) || models.isMetamodelManagedType(type);
111 }
112
113 /**
114 * A wrapper for a set of JPA {@link Metamodel} instances to simplify lookups of {@link JpaMetamodel} instances and
115 * managed type checks.
116 *
117 * @author Oliver Gierke
118 */
119 private static class Metamodels {
120
121 private final Set<Metamodel> metamodels;
122
123 private Metamodels(Set<Metamodel> metamodels) {
124 this.metamodels = metamodels;
125 }
126
127 /**
128 * Returns the {@link JpaMetamodel} for the given type.
129 *
130 * @param type must not be {@literal null}.
131 * @return
132 */
133 @Nullable
134 public JpaMetamodel getMetamodel(TypeInformation<?> type) {
135
136 Metamodel metamodel = getMetamodelFor(type.getType());
137
138 return metamodel == null ? null : JpaMetamodel.of(metamodel);
139 }
140
141 /**
142 * Returns whether the given type is managed by one of the underlying {@link Metamodel} instances.
143 *
144 * @param type must not be {@literal null}.
145 * @return
146 */
147 public boolean isMetamodelManagedType(TypeInformation<?> type) {
148 return isMetamodelManagedType(type.getType());
149 }
150
151 /**
152 * Returns whether the given type is managed by one of the underlying {@link Metamodel} instances.
153 *
154 * @param type must not be {@literal null}.
155 * @return
156 */
157 public boolean isMetamodelManagedType(Class<?> type) {
158 return getMetamodelFor(type) != null;
159 }
160
161 /**
162 * Returns the {@link Metamodel} aware of the given type.
163 *
164 * @param type must not be {@literal null}.
165 * @return can be {@literal null}.
166 */
167 @Nullable
168 private Metamodel getMetamodelFor(Class<?> type) {
169
170 for (Metamodel model : metamodels) {
171
172 try {
173 model.managedType(type);
174 return model;
175 } catch (IllegalArgumentException o_O) {
176
177 // Fall back to inspect *all* managed types manually as Metamodel.managedType(…) only
178 // returns for entities, embeddables and managed supperclasses.
179
180 for (ManagedType<?> managedType : model.getManagedTypes()) {
181 if (type.equals(managedType.getJavaType())) {
182 return model;
183 }
184 }
185 }
186 }
187
188 return null;
189 }
190 }
191 }
192