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.Comparator;
19
20 import org.springframework.data.annotation.Version;
21 import org.springframework.data.jpa.provider.ProxyIdAccessor;
22 import org.springframework.data.jpa.util.JpaMetamodel;
23 import org.springframework.data.mapping.IdentifierAccessor;
24 import org.springframework.data.mapping.model.BasicPersistentEntity;
25 import org.springframework.data.mapping.model.IdPropertyIdentifierAccessor;
26 import org.springframework.data.util.TypeInformation;
27 import org.springframework.util.Assert;
28
29 /**
30  * Implementation of {@link JpaPersistentEntity}.
31  *
32  * @author Oliver Gierke
33  * @author Greg Turnquist
34  * @author Christoph Strobl
35  * @author Mark Paluch
36  * @author Michael J. Simons
37  * @since 1.3
38  */

39 class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentProperty>
40         implements JpaPersistentEntity<T> {
41
42     private static final String INVALID_VERSION_ANNOTATION = "%s is annotated with "
43             + org.springframework.data.annotation.Version.class.getName() + " but needs to use "
44             + javax.persistence.Version.class.getName() + " to trigger optimistic locking correctly!";
45
46     private final ProxyIdAccessor proxyIdAccessor;
47     private final JpaMetamodel metamodel;
48
49     /**
50      * Creates a new {@link JpaPersistentEntityImpl} using the given {@link TypeInformation} and {@link Comparator}.
51      *
52      * @param information must not be {@literal null}.
53      * @param proxyIdAccessor must not be {@literal null}.
54      * @param metamodel must not be {@literal null}.
55      */

56     public JpaPersistentEntityImpl(TypeInformation<T> information, ProxyIdAccessor proxyIdAccessor,
57             JpaMetamodel metamodel) {
58
59         super(information, null);
60
61         Assert.notNull(proxyIdAccessor, "ProxyIdAccessor must not be null!");
62         this.proxyIdAccessor = proxyIdAccessor;
63         this.metamodel = metamodel;
64     }
65
66     /*
67      * (non-Javadoc)
68      * @see org.springframework.data.mapping.model.BasicPersistentEntity#returnPropertyIfBetterIdPropertyCandidateOrNull(org.springframework.data.mapping.PersistentProperty)
69      */

70     @Override
71     protected JpaPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(JpaPersistentProperty property) {
72         return property.isIdProperty() ? property : null;
73     }
74
75     /*
76      * (non-Javadoc)
77      * @see org.springframework.data.mapping.model.BasicPersistentEntity#getIdentifierAccessor(java.lang.Object)
78      */

79     @Override
80     public IdentifierAccessor getIdentifierAccessor(Object bean) {
81         return new JpaProxyAwareIdentifierAccessor(this, bean, proxyIdAccessor);
82     }
83
84     /*
85      * (non-Javadoc)
86      * @see org.springframework.data.mapping.model.BasicPersistentEntity#verify()
87      */

88     @Override
89     public void verify() {
90
91         super.verify();
92
93         JpaPersistentProperty versionProperty = getVersionProperty();
94
95         if (versionProperty != null && versionProperty.isAnnotationPresent(Version.class)) {
96             throw new IllegalArgumentException(String.format(INVALID_VERSION_ANNOTATION, versionProperty));
97         }
98     }
99
100     JpaMetamodel getMetamodel() {
101         return metamodel;
102     }
103
104     /**
105      * {@link IdentifierAccessor} that tries to use a {@link ProxyIdAccessor} for id access to potentially avoid the
106      * initialization of JPA proxies. We're falling back to the default behavior of {@link IdPropertyIdentifierAccessor}
107      * if that's not possible.
108      *
109      * @author Oliver Gierke
110      */

111     private static class JpaProxyAwareIdentifierAccessor extends IdPropertyIdentifierAccessor {
112
113         private final Object bean;
114         private final ProxyIdAccessor proxyIdAccessor;
115
116         /**
117          * Creates a new {@link JpaProxyAwareIdentifierAccessor} for the given {@link JpaPersistentEntity}, target bean and
118          * {@link ProxyIdAccessor}.
119          *
120          * @param entity must not be {@literal null}.
121          * @param bean must not be {@literal null}.
122          * @param proxyIdAccessor must not be {@literal null}.
123          */

124         JpaProxyAwareIdentifierAccessor(JpaPersistentEntity<?> entity, Object bean, ProxyIdAccessor proxyIdAccessor) {
125
126             super(entity, bean);
127
128             Assert.notNull(proxyIdAccessor, "Proxy identifier accessor must not be null!");
129
130             this.proxyIdAccessor = proxyIdAccessor;
131             this.bean = bean;
132         }
133
134         /*
135          * (non-Javadoc)
136          * @see org.springframework.data.mapping.IdentifierAccessor#getIdentifier()
137          */

138         @Override
139         public Object getIdentifier() {
140
141             return proxyIdAccessor.shouldUseAccessorFor(bean) //
142                     ? proxyIdAccessor.getIdentifierFrom(bean)//
143                     : super.getIdentifier();
144         }
145     }
146 }
147