|
16 | 16 | package org.springframework.data.jpa.util; |
17 | 17 |
|
18 | 18 | import java.util.Collection; |
| 19 | +import java.util.HashSet; |
19 | 20 | import java.util.Map; |
20 | 21 | import java.util.Objects; |
21 | 22 | import java.util.Optional; |
| 23 | +import java.util.Set; |
22 | 24 | import java.util.concurrent.ConcurrentHashMap; |
23 | 25 |
|
24 | 26 | import jakarta.persistence.Embeddable; |
|
43 | 45 | */ |
44 | 46 | public class JpaMetamodel { |
45 | 47 |
|
46 | | - private static final Map<Metamodel, JpaMetamodel> CACHE = new ConcurrentHashMap<>(4); |
47 | | - |
48 | | - private final Metamodel metamodel; |
49 | | - |
50 | | - private final Lazy<Collection<Class<?>>> managedTypes; |
51 | | - private final Lazy<Collection<Class<?>>> jpaEmbeddables; |
52 | | - |
53 | | - /** |
54 | | - * Creates a new {@link JpaMetamodel} for the given JPA {@link Metamodel}. |
55 | | - * |
56 | | - * @param metamodel must not be {@literal null}. |
57 | | - */ |
58 | | - private JpaMetamodel(Metamodel metamodel) { |
59 | | - |
60 | | - Assert.notNull(metamodel, "Metamodel must not be null"); |
61 | | - |
62 | | - this.metamodel = metamodel; |
63 | | - |
64 | | - this.managedTypes = Lazy.of(() -> metamodel.getManagedTypes().stream() // |
65 | | - .map(ManagedType::getJavaType) // |
66 | | - .filter(Objects::nonNull) // |
67 | | - .collect(StreamUtils.toUnmodifiableSet())); |
68 | | - |
69 | | - this.jpaEmbeddables = Lazy.of(() -> metamodel.getEmbeddables().stream() // |
70 | | - .map(ManagedType::getJavaType) |
71 | | - .filter(Objects::nonNull) |
72 | | - .filter(it -> AnnotatedElementUtils.isAnnotated(it, Embeddable.class)) |
73 | | - .collect(StreamUtils.toUnmodifiableSet())); |
74 | | - } |
75 | | - |
76 | | - public static JpaMetamodel of(Metamodel metamodel) { |
77 | | - return CACHE.computeIfAbsent(metamodel, JpaMetamodel::new); |
78 | | - } |
79 | | - |
80 | | - /** |
81 | | - * Returns whether the given type is managed by the backing JPA {@link Metamodel}. |
82 | | - * |
83 | | - * @param type must not be {@literal null}. |
84 | | - * @return |
85 | | - */ |
86 | | - public boolean isJpaManaged(Class<?> type) { |
87 | | - |
88 | | - Assert.notNull(type, "Type must not be null"); |
89 | | - |
90 | | - return managedTypes.get().contains(type); |
91 | | - } |
92 | | - |
93 | | - /** |
94 | | - * Returns whether the attribute of given name and type is the single identifier attribute of the given entity. |
95 | | - * |
96 | | - * @param entity must not be {@literal null}. |
97 | | - * @param name must not be {@literal null}. |
98 | | - * @param attributeType must not be {@literal null}. |
99 | | - * @return |
100 | | - */ |
101 | | - public boolean isSingleIdAttribute(Class<?> entity, String name, Class<?> attributeType) { |
102 | | - |
103 | | - return metamodel.getEntities().stream() // |
104 | | - .filter(it -> entity.equals(it.getJavaType())) // |
105 | | - .findFirst() // |
106 | | - .flatMap(JpaMetamodel::getSingularIdAttribute) // |
107 | | - .filter(it -> it.getJavaType().equals(attributeType)) // |
108 | | - .map(it -> it.getName().equals(name)) // |
109 | | - .orElse(false); |
110 | | - } |
111 | | - |
112 | | - /** |
113 | | - * Returns whether the given type is considered a mapped type, i.e. an actually JPA persisted entity, mapped |
114 | | - * superclass or native JPA embeddable. |
115 | | - * |
116 | | - * @param entity must not be {@literal null}. |
117 | | - * @return |
118 | | - */ |
119 | | - public boolean isMappedType(Class<?> entity) { |
120 | | - |
121 | | - Assert.notNull(entity, "Type must not be null"); |
122 | | - |
123 | | - if (!isJpaManaged(entity)) { |
124 | | - return false; |
125 | | - } |
126 | | - |
127 | | - ManagedType<?> managedType = metamodel.managedType(entity); |
128 | | - |
129 | | - return !managedType.getPersistenceType().equals(PersistenceType.EMBEDDABLE) |
130 | | - || jpaEmbeddables.get().contains(entity); |
131 | | - } |
132 | | - |
133 | | - /** |
134 | | - * Wipes the static cache of {@link Metamodel} to {@link JpaMetamodel}. |
135 | | - */ |
136 | | - static void clear() { |
137 | | - CACHE.clear(); |
138 | | - } |
139 | | - |
140 | | - /** |
141 | | - * Returns the {@link SingularAttribute} representing the identifier of the given {@link EntityType} if it contains a |
142 | | - * singular one. |
143 | | - * |
144 | | - * @param entityType must not be {@literal null}. |
145 | | - * @return |
146 | | - */ |
147 | | - private static Optional<? extends SingularAttribute<?, ?>> getSingularIdAttribute(EntityType<?> entityType) { |
148 | | - |
149 | | - if (!entityType.hasSingleIdAttribute()) { |
150 | | - return Optional.empty(); |
151 | | - } |
152 | | - |
153 | | - return entityType.getSingularAttributes().stream() // |
154 | | - .filter(SingularAttribute::isId) // |
155 | | - .findFirst(); |
156 | | - } |
| 48 | + private static final Map<Metamodel, JpaMetamodel> CACHE = new ConcurrentHashMap<>(4); |
| 49 | + |
| 50 | + private final Metamodel metamodel; |
| 51 | + |
| 52 | + private final Lazy<Collection<Class<?>>> managedTypes; |
| 53 | + private final Lazy<Collection<Class<?>>> jpaEmbeddables; |
| 54 | + private final Lazy<Collection<Class<?>>> basicTypes; |
| 55 | + |
| 56 | + /** |
| 57 | + * Creates a new {@link JpaMetamodel} for the given JPA {@link Metamodel}. |
| 58 | + * |
| 59 | + * @param metamodel must not be {@literal null}. |
| 60 | + */ |
| 61 | + private JpaMetamodel(Metamodel metamodel) { |
| 62 | + |
| 63 | + Assert.notNull(metamodel, "Metamodel must not be null"); |
| 64 | + |
| 65 | + this.metamodel = metamodel; |
| 66 | + |
| 67 | + this.managedTypes = Lazy.of(() -> metamodel.getManagedTypes().stream() // |
| 68 | + .map(ManagedType::getJavaType) // |
| 69 | + .filter(Objects::nonNull) // |
| 70 | + .collect(StreamUtils.toUnmodifiableSet())); |
| 71 | + |
| 72 | + this.jpaEmbeddables = Lazy.of(() -> metamodel.getEmbeddables().stream() // |
| 73 | + .map(ManagedType::getJavaType) |
| 74 | + .filter(Objects::nonNull) |
| 75 | + .filter(it -> AnnotatedElementUtils.isAnnotated(it, Embeddable.class)) |
| 76 | + .collect(StreamUtils.toUnmodifiableSet())); |
| 77 | + |
| 78 | + this.basicTypes = Lazy.of(() -> { |
| 79 | + if (metamodel instanceof org.hibernate.metamodel.model.domain.JpaMetamodel hibernateMetamodel) { |
| 80 | + Set<Class<?>> result = new HashSet<>(); |
| 81 | + hibernateMetamodel.getTypeConfiguration() |
| 82 | + .getJavaTypeRegistry() |
| 83 | + .forEachDescriptor(d -> result.add(d.getJavaTypeClass())); |
| 84 | + return result; |
| 85 | + } |
| 86 | + return Set.of(); |
| 87 | + }); |
| 88 | + } |
| 89 | + |
| 90 | + public static JpaMetamodel of(Metamodel metamodel) { |
| 91 | + return CACHE.computeIfAbsent(metamodel, JpaMetamodel::new); |
| 92 | + } |
| 93 | + |
| 94 | + /** |
| 95 | + * Returns whether the given type is managed by the backing JPA {@link Metamodel}. |
| 96 | + * |
| 97 | + * @param type must not be {@literal null}. |
| 98 | + * @return |
| 99 | + */ |
| 100 | + public boolean isJpaManaged(Class<?> type) { |
| 101 | + |
| 102 | + Assert.notNull(type, "Type must not be null"); |
| 103 | + |
| 104 | + return managedTypes.get().contains(type); |
| 105 | + } |
| 106 | + |
| 107 | + /** |
| 108 | + * Returns whether the attribute of given name and type is the single identifier attribute of the given entity. |
| 109 | + * |
| 110 | + * @param entity must not be {@literal null}. |
| 111 | + * @param name must not be {@literal null}. |
| 112 | + * @param attributeType must not be {@literal null}. |
| 113 | + * @return |
| 114 | + */ |
| 115 | + public boolean isSingleIdAttribute(Class<?> entity, String name, Class<?> attributeType) { |
| 116 | + |
| 117 | + return metamodel.getEntities().stream() // |
| 118 | + .filter(it -> entity.equals(it.getJavaType())) // |
| 119 | + .findFirst() // |
| 120 | + .flatMap(JpaMetamodel::getSingularIdAttribute) // |
| 121 | + .filter(it -> it.getJavaType().equals(attributeType)) // |
| 122 | + .map(it -> it.getName().equals(name)) // |
| 123 | + .orElse(false); |
| 124 | + } |
| 125 | + |
| 126 | + /** |
| 127 | + * Returns whether the given type is considered a mapped type, i.e. an actually JPA persisted entity, mapped |
| 128 | + * superclass or native JPA embeddable. |
| 129 | + * |
| 130 | + * @param entity must not be {@literal null}. |
| 131 | + * @return |
| 132 | + */ |
| 133 | + public boolean isMappedType(Class<?> entity) { |
| 134 | + |
| 135 | + Assert.notNull(entity, "Type must not be null"); |
| 136 | + |
| 137 | + if (!isJpaManaged(entity)) { |
| 138 | + return false; |
| 139 | + } |
| 140 | + |
| 141 | + ManagedType<?> managedType = metamodel.managedType(entity); |
| 142 | + |
| 143 | + return !managedType.getPersistenceType().equals(PersistenceType.EMBEDDABLE) |
| 144 | + || jpaEmbeddables.get().contains(entity); |
| 145 | + } |
| 146 | + |
| 147 | + /** |
| 148 | + * Wipes the static cache of {@link Metamodel} to {@link JpaMetamodel}. |
| 149 | + */ |
| 150 | + static void clear() { |
| 151 | + CACHE.clear(); |
| 152 | + } |
| 153 | + |
| 154 | + /** |
| 155 | + * Returns the {@link SingularAttribute} representing the identifier of the given {@link EntityType} if it contains a |
| 156 | + * singular one. |
| 157 | + * |
| 158 | + * @param entityType must not be {@literal null}. |
| 159 | + * @return |
| 160 | + */ |
| 161 | + private static Optional<? extends SingularAttribute<?, ?>> getSingularIdAttribute(EntityType<?> entityType) { |
| 162 | + |
| 163 | + if (!entityType.hasSingleIdAttribute()) { |
| 164 | + return Optional.empty(); |
| 165 | + } |
| 166 | + |
| 167 | + return entityType.getSingularAttributes().stream() // |
| 168 | + .filter(SingularAttribute::isId) // |
| 169 | + .findFirst(); |
| 170 | + } |
| 171 | + |
| 172 | + public boolean isBasicType(Class<?> type) { |
| 173 | + Assert.notNull(type, "Type must not be null"); |
| 174 | + return basicTypes.get().contains(type); |
| 175 | + } |
157 | 176 | } |
0 commit comments