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