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