2 * Copyright 2005-2010 the original author or authors.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.wamblee.persistence;
18 import java.io.Serializable;
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Method;
22 import java.util.List;
24 import java.util.concurrent.ConcurrentHashMap;
26 import javax.persistence.Id;
27 import javax.persistence.Version;
29 import org.wamblee.reflection.Accessor;
30 import org.wamblee.reflection.FieldAccessor;
31 import org.wamblee.reflection.PropertyAccessor;
32 import org.wamblee.reflection.ReflectionUtils;
35 * Factory which creates a {@link Persistent} object for a given JPA entity for
36 * interfacing with the primary key and version of the entity.
38 * This utility only treats primary keys and fields that are annotated with @Id
39 * and @Version. In case ORM files are used for the definition of primary key
40 * and or version, then those fields are ignored.
42 * @author Erik Brakkee
45 public class PersistentFactory {
48 * Cache of a mapping of class names to entity accessors.
50 private static Map<String, EntityAccessor> CACHE = new ConcurrentHashMap<String, EntityAccessor>();
52 static class EntityAccessor {
54 private Accessor version;
56 public EntityAccessor(Accessor<?> aPk, Accessor<?> aVersion) {
61 public Accessor getPk() {
65 public Accessor getVersion() {
70 public static class EntityObjectAccessor implements Persistent {
71 private EntityAccessor accessor;
72 private Object entity;
74 public EntityObjectAccessor(Object aEntity, EntityAccessor aAccessor) {
79 public EntityAccessor getAccessor() {
84 public Serializable getPrimaryKey() {
85 if (accessor == null || accessor.getPk() == null) {
88 return (Serializable) accessor.getPk().get(entity);
92 public void setPrimaryKey(Serializable aKey) {
93 if (accessor == null || accessor.getPk() == null) {
96 accessor.getPk().set(entity, aKey);
100 public Number getPersistedVersion() {
101 if (accessor == null || accessor.getVersion() == null) {
104 return (Number) accessor.getVersion().get(entity);
108 public void setPersistedVersion(Number aVersion) {
109 if (accessor == null || accessor.getVersion() == null) {
112 accessor.getVersion().set(entity, aVersion);
117 * Create the entity accessor for a given class or returns a cached instance
118 * if one already exists.
122 * @return Entity accessor for the given class or null of the given object
125 public static EntityAccessor createEntityAccessor(Class aClass) {
126 EntityAccessor accessor = CACHE.get(aClass.getName());
127 if (accessor == null) {
128 accessor = analyse(aClass);
129 if (accessor != null) {
130 CACHE.put(aClass.getName(), accessor);
136 private static EntityAccessor analyse(Class aClass) {
137 Accessor<Serializable> pk = analyse(aClass, Id.class);
138 Accessor<Integer> version = analyse(aClass, Version.class);
139 if (pk != null || version != null) {
140 return new EntityAccessor(pk, version);
146 * Returns the accessor for a given annotation.
151 * Annotation that must be present.
152 * @return Accessor to use or null if the annotation is not present.
154 // TODO move generic analysis part to the reflection package.
155 public static Accessor analyse(Class aClass,
156 Class<? extends Annotation> aAnnotation) {
157 List<Field> fields = ReflectionUtils.getAllFields(aClass);
158 for (Field field : fields) {
159 if (field.isAnnotationPresent(aAnnotation)) {
160 return new FieldAccessor(field);
163 List<Method> methods = ReflectionUtils.getAllMethods(aClass,
165 for (Method method : methods) {
166 if (method.isAnnotationPresent(aAnnotation)) {
167 String setterName = null;
168 if (method.getName().startsWith("get")) {
169 setterName = method.getName().replaceFirst("get", "set");
170 } else if (method.getName().startsWith("is")) {
171 setterName = method.getName().replaceFirst("is", "set");
174 Class returnType = method.getReturnType();
175 Method setter = method.getDeclaringClass()
176 .getDeclaredMethod(setterName, returnType);
177 return new PropertyAccessor(method, setter);
178 } catch (NoSuchMethodException e) {
179 throw new RuntimeException("Error obtaining setter for " +
180 method.getName() + " in class " + aClass.getName(), e);
188 * Creates the {@link Persistent} wrapper for interfacing with primary key
189 * and version of the entity.
193 * @return Persistent object or null if this is not an entity.
195 public static Persistent create(Object aEntity) {
196 EntityAccessor accessor = createEntityAccessor(aEntity.getClass());
197 if (accessor == null) {
200 return new EntityObjectAccessor(aEntity, accessor);