diff --git a/pom.xml b/pom.xml index 9091eeb91..695f3bfe8 100644 --- a/pom.xml +++ b/pom.xml @@ -81,13 +81,13 @@ io.ebean persistence-api - 2.2.2 + 2.2.4 io.ebean ebean-annotation - 6.9 + 6.11 diff --git a/src/main/java/io/ebean/config/AbstractNamingConvention.java b/src/main/java/io/ebean/config/AbstractNamingConvention.java index 96b112ebd..5a900fce8 100644 --- a/src/main/java/io/ebean/config/AbstractNamingConvention.java +++ b/src/main/java/io/ebean/config/AbstractNamingConvention.java @@ -222,8 +222,8 @@ public abstract class AbstractNamingConvention implements NamingConvention { * Return true if this class is part of entity inheritance. */ protected boolean hasInheritance(Class supCls) { - return AnnotationUtil.findAnnotationRecursive(supCls, Inheritance.class) != null - || AnnotationUtil.findAnnotation(supCls, DiscriminatorValue.class) != null; + return AnnotationUtil.typeHas(supCls, Inheritance.class) + || AnnotationUtil.has(supCls, DiscriminatorValue.class); } @@ -255,16 +255,12 @@ public abstract class AbstractNamingConvention implements NamingConvention { * Gets the table name from annotation. */ protected TableName getTableNameFromAnnotation(Class beanClass) { - - final Table t = AnnotationUtil.findAnnotationRecursive(beanClass, Table.class); - - // Take the annotation if defined - if (t != null && !isEmpty(t.name())) { + final Table table = AnnotationUtil.typeGet(beanClass, Table.class); + if (table != null && !isEmpty(table.name())) { // Note: empty catalog and schema are converted to null // Only need to convert quoted identifiers from annotations - return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name())); + return new TableName(quoteIdentifiers(table.catalog()), quoteIdentifiers(table.schema()), quoteIdentifiers(table.name())); } - // No annotation return null; } diff --git a/src/main/java/io/ebean/util/AnnotationUtil.java b/src/main/java/io/ebean/util/AnnotationUtil.java index 536dd9ea4..238a3217d 100644 --- a/src/main/java/io/ebean/util/AnnotationUtil.java +++ b/src/main/java/io/ebean/util/AnnotationUtil.java @@ -1,304 +1,98 @@ package io.ebean.util; -import io.ebean.annotation.Formula; -import io.ebean.annotation.Platform; -import io.ebean.annotation.Where; - import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Method; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.Objects; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; /** - * Annotation utility methods to find annotations recursively - taken from the spring framework. + * Annotation utility methods to find annotations. */ public class AnnotationUtil { /** * Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package. */ - public static boolean isInJavaLangAnnotationPackage(Annotation annotation) { - return annotation.annotationType().getName().startsWith("java.lang.annotation"); + public static boolean notJavaLang(Annotation annotation) { + return !annotation.annotationType().getName().startsWith("java.lang.annotation"); } /** - * Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}. - *

- * Meta-annotations will be searched if the annotation is not directly present on the supplied element. - *

- * Warning: this method operates generically on annotated elements. In other words, this method - * does not execute specialized search algorithms for classes or methods. It only traverses through Annotations! - * It also does not filter out platform dependent annotations! + * Simple get on field or method with no meta-annotations or platform filtering. */ - @SuppressWarnings("unchecked") - public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { - if (annotationType == null) { - return null; - } - // check if directly present, if not, start search for meta-annotations. - Annotation[] anns = annotatedElement.getAnnotations(); - if (anns.length == 0) { - return null; // no annotations present, so searching for meta annotations not required - } - - // As we need the anns array anyway, we iterate over this instead - // of using annotatedElement.getAnnotation(...) which is synchronized internally - for (Annotation ann : anns) { - if (ann.annotationType() == annotationType) { - return (A) ann; - } - } - - return findAnnotation(anns, annotationType, new HashSet<>()); - + public static A get(AnnotatedElement element, Class annotation) { + return element.getAnnotation(annotation); } /** - * Find a single {@link Annotation} of {@code annotationType} on the supplied class. - *

Meta-annotations will be searched if the annotation is not directly present on - * the supplied element. - *

Note: this method searches for annotations at class & superClass(es)! + * Simple has with no meta-annotations or platform filtering. */ - @SuppressWarnings("unchecked") - public static A findAnnotationRecursive(Class clazz, Class annotationType) { - if (annotationType == null) { - return null; - } + public static boolean has(AnnotatedElement element, Class annotation) { + return get(element, annotation) != null; + } + /** + * On class get the annotation - includes inheritance. + */ + public static A typeGet(Class clazz, Class annotationType) { while (clazz != null && clazz != Object.class) { - // check if directly present, if not, start search for meta-annotations. - Annotation[] anns = clazz.getAnnotations(); - if (anns.length != 0) { - for (Annotation ann : anns) { - if (ann.annotationType() == annotationType) { - return (A) ann; - } - } - - A ann = findAnnotation(anns, annotationType, new HashSet<>()); - if (ann != null) { - return ann; - } + final A val = clazz.getAnnotation(annotationType); + if (val != null) { + return val; } - // no meta-annotation present at this class - traverse to superclass clazz = clazz.getSuperclass(); } return null; - } /** - * Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first - * found annotation is returned) + * On class get all the annotations - includes inheritance. */ - public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType, Platform platform) { - if (annotationType == null) { - return null; + public static Set typeGetAll(Class clazz, Class annotationType) { + Set result = new LinkedHashSet<>(); + typeGetAllCollect(clazz, annotationType, result); + return result; + } + + private static void typeGetAllCollect(Class clazz, Class annotationType, Set result) { + while (clazz != null && clazz != Object.class) { + final A val = clazz.getAnnotation(annotationType); + if (val != null) { + result.add(val); + } + clazz = clazz.getSuperclass(); } - Set anns = findAnnotations(annotatedElement, annotationType); - return getPlatformMatchingAnnotation(anns, platform); } /** - * Finds all annotations recusively for a class and its superclasses or interfaces. + * On class simple check for annotation - includes inheritance. */ - public static Set findAnnotationsRecursive(Class clazz, Class annotationType) { - Objects.requireNonNull(annotationType); - Set ret = new LinkedHashSet<>(); + public static boolean typeHas(Class clazz, Class annotation) { + return typeGet(clazz, annotation) != null; + } + + /** + * Find all the annotations for the filter searching meta-annotations. + */ + public static Set metaFindAllFor(AnnotatedElement element, Set> filter) { Set visited = new HashSet<>(); - Set> visitedInterfaces = new HashSet<>(); - while (clazz != null && !clazz.getName().startsWith("java.lang.")) { - findMetaAnnotationsRecursive(clazz, annotationType, ret, visited, visitedInterfaces); - clazz = clazz.getSuperclass(); + Set result = new LinkedHashSet<>(); + for (Annotation ann : element.getAnnotations()) { + metaAdd(ann, filter, visited, result); } - return ret; + return result; } - /** - * Searches the interfaces for annotations. - */ - private static void findMetaAnnotationsRecursive(Class clazz, - Class annotationType, Set ret, - Set visited, Set> visitedInterfaces) { - findMetaAnnotations(clazz, annotationType, ret, visited); - for (Class iface : clazz.getInterfaces()) { - if (!iface.getName().startsWith("java.lang.") && visitedInterfaces.add(iface)) { - findMetaAnnotationsRecursive(iface, annotationType, ret, visited, visitedInterfaces); - } - } - } - - /** - * Perform the search algorithm avoiding endless recursion by tracking which - * annotations have already been visited. - */ - @SuppressWarnings("unchecked") - private static A findAnnotation(Annotation[] anns, Class annotationType, Set visited) { - - - for (Annotation ann : anns) { - if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { - Annotation[] metaAnns = ann.annotationType().getAnnotations(); - for (Annotation metaAnn : metaAnns) { - if (metaAnn.annotationType() == annotationType) { - return (A) metaAnn; - } - } - if (metaAnns.length > 0) { - A annotation = findAnnotation(metaAnns, annotationType, visited); - if (annotation != null) { - return annotation; - } - } - } - } - return null; - } - - /** - * Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}. - *

- * Meta-annotations will be searched if the annotation is not directly present on the supplied element. - *

- * Warning: this method operates generically on annotated elements. In other words, this method - * does not execute specialized search algorithms for classes or methods. It only traverses through Annotations! - */ - public static Set findAnnotations(AnnotatedElement annotatedElement, Class annotationType) { - if (annotationType == null) { - return null; - } - Set ret = new LinkedHashSet<>(); - findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<>()); - return ret; - } - - /** - * Perform the search algorithm avoiding endless recursion by tracking which - * annotations have already been visited. - */ - @SuppressWarnings("unchecked") - private static void findMetaAnnotations(AnnotatedElement annotatedElement, Class annotationType, Set ret, Set visited) { - - Annotation[] anns = annotatedElement.getAnnotations(); - for (Annotation ann : anns) { - if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { - if (ann.annotationType() == annotationType) { - ret.add((A) ann); - } else { - Method repeatableValueMethod = getRepeatableValueMethod(ann, annotationType); - if (repeatableValueMethod != null) { - try { - A[] repeatedAnns = (A[]) repeatableValueMethod.invoke(ann); - for (Annotation repeatedAnn : repeatedAnns) { - ret.add((A) repeatedAnn); - findMetaAnnotations(repeatedAnn.annotationType(), annotationType, ret, visited); - } - } catch (Exception e) { // catch all exceptions (thrown by invoke) - throw new RuntimeException(e); - } - } else { - findMetaAnnotations(ann.annotationType(), annotationType, ret, visited); - } + private static void metaAdd(Annotation ann, Set> filter, Set visited, Set result) { + if (notJavaLang(ann) && visited.add(ann)) { + if (filter.contains(ann.annotationType())) { + result.add(ann); + } else { + for (Annotation metaAnn : ann.annotationType().getAnnotations()) { + metaAdd(metaAnn, filter, visited, result); } } } } - - // caches for getRepeatableValueMethod - private static Method getNullMethod() { - try { - return AnnotationUtil.class.getDeclaredMethod("getNullMethod"); - } catch (NoSuchMethodException e) { - return null; - } - } - - private static final ConcurrentMap valueMethods = new ConcurrentHashMap<>(); - // only a non-null-marker the valueMethods - Cache - private static final Method nullMethod = getNullMethod(); - - - /** - * Returns the value() method for a possible containerAnnotation. - * Method is retuned only, if its signature is array of containingType. - */ - private static Method getRepeatableValueMethod( - Annotation containerAnnotation, Class containingType) { - - Method method = valueMethods.get(containerAnnotation); - if (method == null) { - try { - method = containerAnnotation.annotationType().getMethod("value"); - } catch (NoSuchMethodException e) { - method = nullMethod; - } catch (Exception e) { - throw new RuntimeException(e); - } - Method prev = valueMethods.putIfAbsent(containerAnnotation, method); - method = prev == null ? method : prev; - } - if (method != nullMethod) { - Class retType = method.getReturnType(); - if (retType.isArray() && retType.getComponentType() == containingType) { - return method; - } - } - return null; - } - - - /** - * Finds a suitable annotation from Set anns for this platform. - * To distinguish between platforms, annotation type T must define - * a method withthis signature: - *

- * Class[] platforms() default {}; - *

- * The finding rules are: - *
    - *
  1. Check if T has method "platforms" if not, return ann[0] - *
  2. find the annotation that is defined for databasePlatform
  3. - *
  4. otherwise return the annotation for default platform (platforms = {})
  5. - *
  6. return null - *
- * (This mechanism is currently used by {@link Where} and {@link Formula}) - */ - public static T getPlatformMatchingAnnotation(Set anns, Platform matchPlatform) { - if (anns.isEmpty()) { - return null; - } - Method getPlatformsMethod = null; - T fallback = null; - for (T ann : anns) { - try { - if (getPlatformsMethod == null) { - getPlatformsMethod = ann.getClass().getMethod("platforms"); - } - if (!Platform[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) { - return ann; - } - Platform[] platforms = (Platform[]) getPlatformsMethod.invoke(ann); - if (platforms.length == 0) { - fallback = ann; - } else { - for (Platform platform : platforms) { - if (matchPlatform == platform) { - return ann; - } - } - } - } catch (NoSuchMethodException e) { - return ann; // not platform specific - return first one - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return fallback; - } - } diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java index 620e78a46..968c5f788 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java @@ -134,7 +134,7 @@ class DefaultCacheHolder { } private ServerCacheOptions getQueryOptions(Class cls) { - CacheQueryTuning tuning = AnnotationUtil.findAnnotation(cls, CacheQueryTuning.class); + CacheQueryTuning tuning = AnnotationUtil.get(cls, CacheQueryTuning.class); if (tuning != null) { return new ServerCacheOptions(tuning).applyDefaults(queryDefault); } diff --git a/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java b/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java index eb86e1c11..dc465890c 100644 --- a/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java +++ b/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java @@ -52,7 +52,7 @@ public class DefaultChangeLogRegister implements ChangeLogRegister { * Find and return the ChangeLog annotation in the inheritance hierarchy. */ private ChangeLog getChangeLog(Class beanType) { - return AnnotationUtil.findAnnotationRecursive(beanType, ChangeLog.class); + return AnnotationUtil.typeGet(beanType, ChangeLog.class); } /** diff --git a/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java b/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java index 875b5572a..f73d3b778 100644 --- a/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java +++ b/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java @@ -473,6 +473,6 @@ public class BootupClasses implements ClassFilter { * Returns true if this class has the annotation (or meta annotation). Does not search recursively. */ private boolean has(Class cls, Class ann) { - return AnnotationUtil.findAnnotation(cls, ann) != null; + return AnnotationUtil.has(cls, ann); } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 2a0ea3aba..4a64ff0d1 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -1591,7 +1591,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap { */ private boolean isMappedSuperWithNoProperties(Class beanClass) { // do not search recursive here - MappedSuperclass annotation = AnnotationUtil.findAnnotation(beanClass, MappedSuperclass.class); + MappedSuperclass annotation = AnnotationUtil.get(beanClass, MappedSuperclass.class); if (annotation == null) { return false; } diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 0d715f75e..4b6fb4861 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -4,10 +4,13 @@ import io.ebean.annotation.CreatedTimestamp; import io.ebean.annotation.DocCode; import io.ebean.annotation.DocProperty; import io.ebean.annotation.DocSortable; +import io.ebean.annotation.Formula; +import io.ebean.annotation.Platform; import io.ebean.annotation.SoftDelete; import io.ebean.annotation.UpdatedTimestamp; import io.ebean.annotation.WhenCreated; import io.ebean.annotation.WhenModified; +import io.ebean.annotation.Where; import io.ebean.annotation.WhoCreated; import io.ebean.annotation.WhoModified; import io.ebean.config.ScalarTypeConverter; @@ -31,11 +34,13 @@ import javax.persistence.EmbeddedId; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.Version; +import javax.validation.constraints.Size; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.sql.Types; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -190,11 +195,6 @@ public class DeployBeanProperty { private final DeployDocPropertyOptions docMapping = new DeployDocPropertyOptions(); - /** - * The method used to read the property. - */ - private Method readMethod; - private int propertyIndex; private BeanPropertyGetter getter; @@ -232,6 +232,8 @@ public class DeployBeanProperty { private List dbMigrationInfos; + private Set metaAnnotations; + public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { this.desc = desc; this.propertyType = propertyType; @@ -261,29 +263,29 @@ public class DeployBeanProperty { if (field == null) { return 0; } - if (AnnotationUtil.findAnnotation(field, Id.class) != null) { + if (AnnotationUtil.get(field, Id.class) != null) { return ID_ORDER; - } else if (AnnotationUtil.findAnnotation(field, EmbeddedId.class) != null) { + } else if (AnnotationUtil.get(field, EmbeddedId.class) != null) { return ID_ORDER; } else if (undirectionalShadow) { return UNIDIRECTIONAL_ORDER; } else if (isAuditProperty()) { return AUDITCOLUMN_ORDER; - } else if (AnnotationUtil.findAnnotation(field, Version.class) != null) { + } else if (AnnotationUtil.get(field, Version.class) != null) { return VERSIONCOLUMN_ORDER; - } else if (AnnotationUtil.findAnnotation(field, SoftDelete.class) != null) { + } else if (AnnotationUtil.get(field, SoftDelete.class) != null) { return VERSIONCOLUMN_ORDER; } return 0; } private boolean isAuditProperty() { - return (AnnotationUtil.findAnnotation(field, WhenCreated.class) != null - || AnnotationUtil.findAnnotation(field, WhenModified.class) != null - || AnnotationUtil.findAnnotation(field, WhoModified.class) != null - || AnnotationUtil.findAnnotation(field, WhoCreated.class) != null - || AnnotationUtil.findAnnotation(field, UpdatedTimestamp.class) != null - || AnnotationUtil.findAnnotation(field, CreatedTimestamp.class) != null); + return (AnnotationUtil.has(field, WhenCreated.class) + || AnnotationUtil.has(field, WhenModified.class) + || AnnotationUtil.has(field, WhoModified.class) + || AnnotationUtil.has(field, WhoCreated.class) + || AnnotationUtil.has(field, UpdatedTimestamp.class) + || AnnotationUtil.has(field, CreatedTimestamp.class)); } public String getFullBeanName() { @@ -453,13 +455,6 @@ public class DeployBeanProperty { } } - /** - * Return the getter method. - */ - public Method getReadMethod() { - return readMethod; - } - /** * Set to the owning type form a Inheritance heirarchy. */ @@ -672,7 +667,7 @@ public class DeployBeanProperty { */ private String aggregationJoin(int pos, String dbColumn) { String p0 = aggregation.substring(0, pos + 1); - aggregationParsed = p0 + "${ta}." + dbColumn + aggregation.substring(aggregation.length() - 1); + aggregationParsed = p0 + "${ta}." + dbColumn + aggregation.substring(aggregation.length() - 1); return aggregationParsed; } @@ -906,17 +901,6 @@ public class DeployBeanProperty { this.isTransient = true; } - /** - * Set the bean read method. - *

- * NB: That a BeanReflectGetter is used to actually perform the getting of - * property values from a bean. This is due to performance considerations. - *

- */ - public void setReadMethod(Method readMethod) { - this.readMethod = readMethod; - } - /** * Return the property type. */ @@ -1122,7 +1106,7 @@ public class DeployBeanProperty { */ public Object /*AnnotatedField*/ getJacksonField() { com.fasterxml.jackson.databind.introspect.AnnotatedClass jac = - (com.fasterxml.jackson.databind.introspect.AnnotatedClass) getDesc().getJacksonAnnotatedClass(); + (com.fasterxml.jackson.databind.introspect.AnnotatedClass) getDesc().getJacksonAnnotatedClass(); for (com.fasterxml.jackson.databind.introspect.AnnotatedField candidate : jac.fields()) { if (candidate.getName().equals(getName())) { return candidate; @@ -1130,4 +1114,102 @@ public class DeployBeanProperty { } return null; } + + public void initMetaAnnotations(Set> metaAnnotationsFilter) { + metaAnnotations = AnnotationUtil.metaFindAllFor(field, metaAnnotationsFilter); + } + + @SuppressWarnings("unchecked") + public
A getMetaAnnotation(Class annotationType) { + for (Annotation ann : metaAnnotations) { + if (ann.annotationType() == annotationType) { + return (A) ann; + } + } + return null; + } + + @SuppressWarnings("unchecked") + public List getMetaAnnotations(Class annotationType) { + List result = new ArrayList<>(); + for (Annotation ann : metaAnnotations) { + if (ann.annotationType() == annotationType) { + result.add((A) ann); + } + } + return result; + } + + public List getMetaAnnotationSize() { + final List size = getMetaAnnotations(Size.class); + final List lists = getMetaAnnotations(Size.List.class); + for (Size.List list : lists) { + Collections.addAll(size, list.value()); + } + return size; + } + + public Formula getMetaAnnotationFormula(Platform platform) { + Formula fallback = null; + for (Annotation ann : metaAnnotations) { + if (ann.annotationType() == Formula.class) { + Formula formula = (Formula) ann; + final Platform[] platforms = formula.platforms(); + if (platforms.length == 0) { + fallback = formula; + } else if (matchPlatform(platforms, platform)) { + return formula; + } + + } else if (ann.annotationType() == Formula.List.class) { + Formula.List formulaList = (Formula.List) ann; + for (Formula formula : formulaList.value()) { + final Platform[] platforms = formula.platforms(); + if (platforms.length == 0) { + fallback = formula; + } else if (matchPlatform(platforms, platform)) { + return formula; + } + } + } + } + return fallback; + } + + public Where getMetaAnnotationWhere(Platform platform) { + Where fallback = null; + for (Annotation ann : metaAnnotations) { + if (ann.annotationType() == Where.class) { + Where where = (Where) ann; + final Platform[] platforms = where.platforms(); + if (platforms.length == 0) { + fallback = where; + } else if (matchPlatform(where.platforms(), platform)) { + return where; + } + + } else if (ann.annotationType() == Where.List.class) { + Where.List whereList = (Where.List) ann; + for (Where where : whereList.value()) { + final Platform[] platforms = where.platforms(); + if (platforms.length == 0) { + fallback = where; + } else if (matchPlatform(where.platforms(), platform)) { + return where; + } + } + } + } + return fallback; + } + + private boolean matchPlatform(Platform[] platforms, Platform match) { + for (Platform platform : platforms) { + if (platform == match) { + return true; + } + } + return false; + } + } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 027702c98..730e9c0fa 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -125,7 +125,7 @@ class AnnotationAssocManys extends AnnotationParser { prop.setMapKey(mapKey.name()); } - Where where = get(prop, Where.class); + Where where = prop.getMetaAnnotationWhere(platform); if (where != null) { prop.setExtraWhere(where.clause()); } @@ -138,8 +138,8 @@ class AnnotationAssocManys extends AnnotationParser { // check for manually defined joins BeanTable beanTable = prop.getBeanTable(); - Set joinColumns = getAll(prop, JoinColumn.class); - if (joinColumns != null) { + Set joinColumns = annotationJoinColumns(prop); + if (!joinColumns.isEmpty()) { prop.getTableJoin().addJoinColumn(util, true, joinColumns, beanTable); } @@ -194,7 +194,7 @@ class AnnotationAssocManys extends AnnotationParser { if (!elementCollection.targetClass().equals(void.class)) { prop.setTargetType(elementCollection.targetClass()); } - Column column = get(prop, Column.class); + Column column = prop.getMetaAnnotation(Column.class); if (column != null) { prop.setDbColumn(column.name()); prop.setDbLength(column.length()); diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index b2971413b..8d4d178f1 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -82,7 +82,7 @@ public class AnnotationAssocOnes extends AnnotationParser { prop.setId(); prop.setNullable(false); } - Column column = get(prop, Column.class); + Column column = prop.getMetaAnnotation(Column.class); if (column != null && !isEmpty(column.name())) { // have this in for AssocOnes used on // Sql based beans... @@ -100,7 +100,7 @@ public class AnnotationAssocOnes extends AnnotationParser { prop.setForeignKey(new PropertyForeignKey(dbForeignKey)); } - Where where = get(prop, Where.class); + Where where = prop.getMetaAnnotationWhere(platform); if (where != null) { // not expecting this to be used on assoc one properties prop.setExtraWhere(where.clause()); @@ -131,12 +131,11 @@ public class AnnotationAssocOnes extends AnnotationParser { // check for manually defined joins BeanTable beanTable = prop.getBeanTable(); - for (JoinColumn joinColumn : getAll(prop, JoinColumn.class)) { + for (JoinColumn joinColumn : annotationJoinColumns(prop)) { setFromJoinColumn(prop, beanTable, joinColumn); checkForNoConstraint(prop, joinColumn); } - JoinTable joinTable = get(prop, JoinTable.class); if (joinTable != null) { for (JoinColumn joinColumn : joinTable.joinColumns()) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java index 84fda2072..4cf692468 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java @@ -1,16 +1,27 @@ package io.ebeaninternal.server.deploy.parse; +import io.ebean.annotation.Aggregation; +import io.ebean.annotation.Avg; +import io.ebean.annotation.DbMigration; +import io.ebean.annotation.Index; +import io.ebean.annotation.Indices; +import io.ebean.annotation.Max; +import io.ebean.annotation.Min; import io.ebean.annotation.Platform; +import io.ebean.annotation.Sum; import io.ebean.config.NamingConvention; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.util.AnnotationUtil; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import javax.persistence.AttributeOverride; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.Method; +import java.util.Collections; import java.util.Set; /** @@ -41,7 +52,7 @@ import java.util.Set; abstract class AnnotationBase { final DatabasePlatform databasePlatform; - private final Platform platform; + protected final Platform platform; final NamingConvention namingConvention; final DeployUtil util; @@ -64,68 +75,43 @@ abstract class AnnotationBase { return s == null || s.trim().isEmpty(); } - /** - * Return the annotation for the property. - *

- * Looks first at the field and then at the getter method. It searches for meta-annotations, but not - * recursively in the class hierarchy. - *

- *

- * If a repeatable annotation class is specified and the annotation is platform - * specific then the platform specific annotation is returned. Otherwise the first annotation - * is returned. Note that you need no longer handle "java 1.6 repeatable containers" - * like {@link JoinColumn} / {@link JoinColumns} yourself. - *

- *

- */ T get(DeployBeanProperty prop, Class annClass) { - T a = null; - Field field = prop.getField(); - if (field != null) { - a = AnnotationUtil.findAnnotation(field, annClass, platform); - } - if (a == null) { - Method method = prop.getReadMethod(); - if (method != null) { - a = AnnotationUtil.findAnnotation(method, annClass, platform); - } - } - return a; + return AnnotationUtil.get(prop.getField(), annClass); } - /** - * Return all annotations for this property. Annotations are not filtered by platfrom and you'll get - * really all annotations that are directly, indirectly or meta-present. - */ - Set getAll(DeployBeanProperty prop, Class annClass) { - Set ret = null; - Field field = prop.getField(); - if (field != null) { - ret = AnnotationUtil.findAnnotations(field, annClass); - } - Method method = prop.getReadMethod(); - if (method != null) { - if (ret != null) { - ret.addAll(AnnotationUtil.findAnnotations(method, annClass)); - } else { - ret = AnnotationUtil.findAnnotations(method, annClass); - } - } - return ret; + boolean has(DeployBeanProperty prop, Class annClass) { + return AnnotationUtil.has(prop.getField(), annClass); } - /** - * Return the annotation for the property. - *

- * Looks first at the field and then at the getter method. then at class level. - * (This is used for SequenceGenerator e.g.) - *

- */ - T find(DeployBeanProperty prop, Class annClass) { - T a = get(prop, annClass); - if (a == null) { - a = AnnotationUtil.findAnnotation(prop.getOwningType(), annClass, platform); + Set annotationJoinColumns(DeployBeanProperty prop) { + return AnnotationFind.joinColumns(prop.getField()); + } + + Set annotationAttributeOverrides(DeployBeanProperty prop) { + return AnnotationFind.attributeOverrides(prop.getField()); + } + + Set annotationIndexes(DeployBeanProperty prop) { + return AnnotationFind.indexes(prop.getField()); + } + + Set annotationDbMigrations(DeployBeanProperty prop) { + return AnnotationFind.dbMigrations(prop.getField()); + } + + Set annotationClassIndexes(Class cls) { + Set result = AnnotationUtil.typeGetAll(cls, Index.class); + for (Indices index : AnnotationUtil.typeGetAll(cls, Indices.class)) { + Collections.addAll(result, index.value()); } - return a; + return result; + } + + Set annotationClassNamedQuery(Class cls) { + Set result = AnnotationUtil.typeGetAll(cls, NamedQuery.class); + for (NamedQueries queries : AnnotationUtil.typeGetAll(cls, NamedQueries.class)) { + Collections.addAll(result, queries.value()); + } + return result; } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java index b12c030aa..8985fb4ac 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -31,8 +31,7 @@ import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.UniqueConstraint; -import static io.ebean.util.AnnotationUtil.findAnnotationRecursive; -import static io.ebean.util.AnnotationUtil.findAnnotationsRecursive; +import static io.ebean.util.AnnotationUtil.typeGet; /** * Read the class level deployment annotations. @@ -62,9 +61,8 @@ public class AnnotationClass extends AnnotationParser { * Parse any AttributeOverride set on the class. */ void parseAttributeOverride() { - Class cls = descriptor.getBeanType(); - AttributeOverride override = findAnnotationRecursive(cls, AttributeOverride.class); + AttributeOverride override = typeGet(cls, AttributeOverride.class); if (override != null) { String propertyName = override.name(); Column column = override.column(); @@ -104,16 +102,15 @@ public class AnnotationClass extends AnnotationParser { } private void read(Class cls) { - // maybe doc store only so check for this before @Entity - DocStore docStore = findAnnotationRecursive(cls, DocStore.class); + DocStore docStore = typeGet(cls, DocStore.class); if (docStore != null) { descriptor.readDocStore(docStore); descriptor.setEntityType(EntityType.DOC); descriptor.setName(cls.getSimpleName()); } - Entity entity = findAnnotationRecursive(cls, Entity.class); + Entity entity = typeGet(cls, Entity.class); if (entity != null) { descriptor.setEntityType(EntityType.ORM); if (entity.name().isEmpty()) { @@ -123,37 +120,37 @@ public class AnnotationClass extends AnnotationParser { } } - Identity identity = findAnnotationRecursive(cls, Identity.class); + Identity identity = typeGet(cls, Identity.class); if (identity != null) { descriptor.setIdentityMode(identity); } - IdClass idClass = findAnnotationRecursive(cls, IdClass.class); + IdClass idClass = typeGet(cls, IdClass.class); if (idClass != null) { descriptor.setIdClass(idClass.value()); } - Embeddable embeddable = findAnnotationRecursive(cls, Embeddable.class); + Embeddable embeddable = typeGet(cls, Embeddable.class); if (embeddable != null) { descriptor.setEntityType(EntityType.EMBEDDED); descriptor.setName("Embeddable:" + cls.getSimpleName()); } - for (Index index : findAnnotationsRecursive(cls, Index.class)) { + for (Index index : annotationClassIndexes(cls)) { descriptor.addIndex(new IndexDefinition(convertColumnNames(index.columnNames()), index.name(), index.unique(), index.platforms(), index.concurrent(), index.definition())); } - UniqueConstraint uc = findAnnotationRecursive(cls, UniqueConstraint.class); + UniqueConstraint uc = typeGet(cls, UniqueConstraint.class); if (uc != null) { descriptor.addIndex(new IndexDefinition(convertColumnNames(uc.columnNames()))); } - View view = findAnnotationRecursive(cls, View.class); + View view = typeGet(cls, View.class); if (view != null) { descriptor.setView(view.name(), view.dependentTables()); } - Table table = findAnnotationRecursive(cls, Table.class); + Table table = typeGet(cls, Table.class); if (table != null) { UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); for (UniqueConstraint c : uniqueConstraints) { @@ -161,54 +158,54 @@ public class AnnotationClass extends AnnotationParser { } } - StorageEngine storage = findAnnotationRecursive(cls, StorageEngine.class); + StorageEngine storage = typeGet(cls, StorageEngine.class); if (storage != null) { descriptor.setStorageEngine(storage.value()); } - DbPartition partition = findAnnotationRecursive(cls, DbPartition.class); + DbPartition partition = typeGet(cls, DbPartition.class); if (partition != null) { descriptor.setPartitionMeta(new PartitionMeta(partition.mode(), partition.property())); } - Draftable draftable = findAnnotationRecursive(cls, Draftable.class); + Draftable draftable = typeGet(cls, Draftable.class); if (draftable != null) { descriptor.setDraftable(); } - DraftableElement draftableElement = findAnnotationRecursive(cls, DraftableElement.class); + DraftableElement draftableElement = typeGet(cls, DraftableElement.class); if (draftableElement != null) { descriptor.setDraftableElement(); } - ReadAudit readAudit = findAnnotationRecursive(cls, ReadAudit.class); + ReadAudit readAudit = typeGet(cls, ReadAudit.class); if (readAudit != null) { descriptor.setReadAuditing(); } - History history = findAnnotationRecursive(cls, History.class); + History history = typeGet(cls, History.class); if (history != null) { descriptor.setHistorySupport(); } - DbComment comment = findAnnotationRecursive(cls, DbComment.class); + DbComment comment = typeGet(cls, DbComment.class); if (comment != null) { descriptor.setDbComment(comment.value()); } if (!disableL2Cache) { - Cache cache = findAnnotationRecursive(cls, Cache.class); + Cache cache = typeGet(cls, Cache.class); if (cache != null) { descriptor.setCache(cache); } else { - InvalidateQueryCache invalidateQueryCache = findAnnotationRecursive(cls, InvalidateQueryCache.class); + InvalidateQueryCache invalidateQueryCache = typeGet(cls, InvalidateQueryCache.class); if (invalidateQueryCache != null) { descriptor.setInvalidateQueryCache(invalidateQueryCache.region()); } } } - for (NamedQuery namedQuery : findAnnotationsRecursive(cls, NamedQuery.class)) { + for (NamedQuery namedQuery : annotationClassNamedQuery(cls)) { descriptor.addNamedQuery(namedQuery.name(), namedQuery.query()); } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java index 14a35ac31..195ff6610 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -106,8 +106,8 @@ public class AnnotationFields extends AnnotationParser { */ @Override public void parse() { - for (DeployBeanProperty prop : descriptor.propertiesAll()) { + prop.initMetaAnnotations(readConfig.getMetaAnnotations()); if (prop instanceof DeployBeanPropertyAssoc) { readAssocOne((DeployBeanPropertyAssoc) prop); } else { @@ -123,13 +123,11 @@ public class AnnotationFields extends AnnotationParser { readJsonAnnotations(prop); - Id id = get(prop, Id.class); - if (id != null) { + if (has(prop, Id.class)) { readIdAssocOne(prop); } - EmbeddedId embeddedId = get(prop, EmbeddedId.class); - if (embeddedId != null) { + if (has(prop, EmbeddedId.class)) { prop.setId(); prop.setNullable(false); prop.setEmbedded(); @@ -140,7 +138,7 @@ public class AnnotationFields extends AnnotationParser { if (docEmbedded != null) { prop.setDocStoreEmbedded(docEmbedded.doc()); if (descriptor.isDocStoreOnly()) { - if (get(prop, ManyToOne.class) == null) { + if (has(prop, ManyToOne.class)) { prop.setEmbedded(); prop.setDbInsertable(true); prop.setDbUpdateable(true); @@ -155,20 +153,20 @@ public class AnnotationFields extends AnnotationParser { readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne) prop); } - Formula formula = get(prop, Formula.class); + Formula formula = prop.getMetaAnnotationFormula(platform); if (formula != null) { prop.setSqlFormula(formula.select(), formula.join()); } initWhoProperties(prop); - readDbMigration(prop); + initDbMigration(prop); } private void initWhoProperties(DeployBeanProperty prop) { - if (get(prop, WhoModified.class) != null) { + if (has(prop, WhoModified.class)) { generatedPropFactory.setWhoModified(prop); } - if (get(prop, WhoCreated.class) != null) { + if (has(prop, WhoCreated.class)) { generatedPropFactory.setWhoCreated(prop); } } @@ -188,7 +186,7 @@ public class AnnotationFields extends AnnotationParser { prop.setDbInsertable(true); prop.setDbUpdateable(true); - Column column = get(prop, Column.class); + Column column = prop.getMetaAnnotation(Column.class); if (column != null) { readColumn(column, prop); } @@ -200,6 +198,32 @@ public class AnnotationFields extends AnnotationParser { prop.setDbColumn(namingConvention.getColumnFromProperty(beanType, prop.getName())); } + initIdentity(prop); + initTenantId(prop); + initDbJson(prop); + initFormula(prop); + initVersion(prop); + initWhen(prop); + initWhoProperties(prop); + initDbMigration(prop); + + // Want to process last so we can use with @Formula + if (has(prop, Transient.class)) { + // it is not a persistent property. + prop.setDbRead(false); + prop.setDbInsertable(false); + prop.setDbUpdateable(false); + prop.setTransient(); + } + + initEncrypt(prop); + + for (Index index : annotationIndexes(prop)) { + addIndex(prop, index); + } + } + + private void initIdentity(DeployBeanProperty prop) { Id id = get(prop, Id.class); GeneratedValue gen = get(prop, GeneratedValue.class); if (gen != null) { @@ -219,7 +243,7 @@ public class AnnotationFields extends AnnotationParser { if (temporal != null) { readTemporal(temporal, prop); - } else if (get(prop, Lob.class) != null) { + } else if (has(prop, Lob.class)) { util.setLobType(prop); } @@ -228,53 +252,58 @@ public class AnnotationFields extends AnnotationParser { prop.setDbLength(length.value()); } - io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class); - if (nonNull != null) { + if (has(prop, io.ebean.annotation.NotNull.class)) { + prop.setNullable(false); + } + } + + private void initValidation(DeployBeanProperty prop) { + NotNull notNull = get(prop, NotNull.class); + if (notNull != null && isEbeanValidationGroups(notNull.groups())) { + // Not null on all validation groups so enable + // DDL generation of Not Null Constraint prop.setNullable(false); } - if (validationAnnotations) { - NotNull notNull = get(prop, NotNull.class); - if (notNull != null && isEbeanValidationGroups(notNull.groups())) { - // Not null on all validation groups so enable - // DDL generation of Not Null Constraint - prop.setNullable(false); + if (!prop.isLob()) { + // take the max size of all @Size annotations + int maxSize = -1; + for (Size size : prop.getMetaAnnotationSize()) { + if (size.max() < Integer.MAX_VALUE) { + maxSize = Math.max(maxSize, size.max()); + } } - - if (!prop.isLob()) { - // take the max size of all @Size annotations - int maxSize = -1; - for (Size size : getAll(prop, Size.class)) { - if (size.max() < Integer.MAX_VALUE) { - maxSize = Math.max(maxSize, size.max()); - } - } - if (maxSize != -1) { - prop.setDbLength(maxSize); - } + if (maxSize != -1) { + prop.setDbLength(maxSize); } } + } - if (get(prop, TenantId.class) != null) { + private void initTenantId(DeployBeanProperty prop) { + if (validationAnnotations) { + initValidation(prop); + } + if (has(prop, TenantId.class)) { prop.setTenantId(); } - if (get(prop, Draft.class) != null) { + if (has(prop, Draft.class)) { prop.setDraft(); } - if (get(prop, DraftOnly.class) != null) { + if (has(prop, DraftOnly.class)) { prop.setDraftOnly(); } - if (get(prop, DraftDirty.class) != null) { + if (has(prop, DraftDirty.class)) { prop.setDraftDirty(); } - if (get(prop, DraftReset.class) != null) { + if (has(prop, DraftReset.class)) { prop.setDraftReset(); } - SoftDelete softDelete = get(prop, SoftDelete.class); - if (softDelete != null) { + if (has(prop, SoftDelete.class)) { prop.setSoftDelete(); } + } + private void initDbJson(DeployBeanProperty prop) { DbComment comment = get(prop, DbComment.class); if (comment != null) { prop.setDbComment(comment.value()); @@ -296,7 +325,9 @@ public class AnnotationFields extends AnnotationParser { if (dbArray != null) { util.setDbArray(prop, dbArray); } + } + private void initFormula(DeployBeanProperty prop) { DocCode docCode = get(prop, DocCode.class); if (docCode != null) { prop.setDocCode(docCode); @@ -309,19 +340,19 @@ public class AnnotationFields extends AnnotationParser { if (docProperty != null) { prop.setDocProperty(docProperty); } - - Formula formula = get(prop, Formula.class); + Formula formula = prop.getMetaAnnotationFormula(platform); if (formula != null) { prop.setSqlFormula(formula.select(), formula.join()); } - Aggregation aggregation = get(prop, Aggregation.class); + final Aggregation aggregation = prop.getMetaAnnotation(Aggregation.class); if (aggregation != null) { prop.setAggregation(aggregation.value().replace("$1", prop.getName())); } + } - Version version = get(prop, Version.class); - if (version != null) { + private void initVersion(DeployBeanProperty prop) { + if (has(prop, Version.class)) { // explicitly specify a version column prop.setVersionColumn(); generatedPropFactory.setVersion(prop); @@ -337,35 +368,19 @@ public class AnnotationFields extends AnnotationParser { // use the default Lob fetchType prop.setFetchType(defaultLobFetchType); } + } - if (get(prop, WhenCreated.class) != null || get(prop, CreatedTimestamp.class) != null) { + private void initWhen(DeployBeanProperty prop) { + if (has(prop, WhenCreated.class) || has(prop, CreatedTimestamp.class)) { generatedPropFactory.setInsertTimestamp(prop); } - - if (get(prop, WhenModified.class) != null || get(prop, UpdatedTimestamp.class) != null) { + if (has(prop, WhenModified.class) || has(prop, UpdatedTimestamp.class)) { generatedPropFactory.setUpdateTimestamp(prop); } + } - initWhoProperties(prop); - - if (get(prop, HistoryExclude.class) != null) { - prop.setExcludedFromHistory(); - } - - readDbMigration(prop); - - // Want to process last so we can use with @Formula - Transient t = get(prop, Transient.class); - if (t != null) { - // it is not a persistent property. - prop.setDbRead(false); - prop.setDbInsertable(false); - prop.setDbUpdateable(false); - prop.setTransient(); - } - + private void initEncrypt(DeployBeanProperty prop) { if (!prop.isTransient()) { - EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(), prop.getDbColumn()); if (encryptDeploy == null || encryptDeploy.getMode() == Mode.MODE_ANNOTATION) { Encrypted encrypted = get(prop, Encrypted.class); @@ -376,24 +391,22 @@ public class AnnotationFields extends AnnotationParser { setEncryption(prop, encryptDeploy.isDbEncrypt(), encryptDeploy.getDbLength()); } } - - Set indices = getAll(prop, Index.class); - for (Index index : indices) { - addIndex(prop, index); - } } private void readIdentity(Identity identity) { descriptor.setIdentityMode(identity); } - private void readDbMigration(DeployBeanProperty prop) { + private void initDbMigration(DeployBeanProperty prop) { + if (has(prop, HistoryExclude.class)) { + prop.setExcludedFromHistory(); + } DbDefault dbDefault = get(prop, DbDefault.class); if (dbDefault != null) { prop.setDbColumnDefault(dbDefault.value()); } - Set dbMigration = getAll(prop, DbMigration.class); + Set dbMigration = annotationDbMigrations(prop); dbMigration.forEach(ann -> prop.addDbMigrationInfo( new DbMigrationInfo(ann.preAdd(), ann.postAdd(), ann.preAlter(), ann.postAlter(), ann.platforms()))); } @@ -445,15 +458,13 @@ public class AnnotationFields extends AnnotationParser { prop.setJsonSerialize(jsonIgnore.serialize()); prop.setJsonDeserialize(jsonIgnore.deserialize()); } - if (get(prop, UnmappedJson.class) != null) { + if (has(prop, UnmappedJson.class)) { prop.setUnmappedJson(); } } private boolean hasRelationshipItem(DeployBeanProperty prop) { - return get(prop, OneToMany.class) != null || - get(prop, ManyToOne.class) != null || - get(prop, OneToOne.class) != null; + return has(prop, OneToMany.class) || has(prop, ManyToOne.class) || has(prop, OneToOne.class); } private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) { @@ -528,7 +539,7 @@ public class AnnotationFields extends AnnotationParser { } descriptor.setIdGeneratedValue(); - SequenceGenerator seq = find(prop, SequenceGenerator.class); + SequenceGenerator seq = get(prop, SequenceGenerator.class); if (seq != null) { String seqName = seq.sequenceName(); if (seqName.isEmpty()) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFind.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFind.java new file mode 100644 index 000000000..0faf59837 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFind.java @@ -0,0 +1,75 @@ +package io.ebeaninternal.server.deploy.parse; + +import io.ebean.annotation.DbMigration; +import io.ebean.annotation.Index; +import io.ebean.annotation.Indices; + +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import static io.ebean.util.AnnotationUtil.get; + +public class AnnotationFind { + + public static Set joinColumns(Field field) { + final JoinColumn col = get(field, JoinColumn.class); + if (col != null) { + return Collections.singleton(col); + } + final JoinColumns cols = get(field, JoinColumns.class); + if (cols != null) { + Set result = new LinkedHashSet<>(); + Collections.addAll(result, cols.value()); + return result; + } + return Collections.emptySet(); + } + + public static Set attributeOverrides(Field field) { + final AttributeOverride ann = get(field, AttributeOverride.class); + if (ann != null) { + return Collections.singleton(ann); + } + final AttributeOverrides collection = get(field, AttributeOverrides.class); + if (collection != null) { + Set result = new LinkedHashSet<>(); + Collections.addAll(result, collection.value()); + return result; + } + return Collections.emptySet(); + } + + public static Set indexes(Field field) { + final Index ann = get(field, Index.class); + if (ann != null) { + return Collections.singleton(ann); + } + final Indices collection = get(field, Indices.class); + if (collection != null) { + Set result = new LinkedHashSet<>(); + Collections.addAll(result, collection.value()); + return result; + } + return Collections.emptySet(); + } + + public static Set dbMigrations(Field field) { + final DbMigration ann = get(field, DbMigration.class); + if (ann != null) { + return Collections.singleton(ann); + } + final DbMigration.List collection = get(field, DbMigration.List.class); + if (collection != null) { + Set result = new LinkedHashSet<>(); + Collections.addAll(result, collection.value()); + return result; + } + return Collections.emptySet(); + } +} diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java index d7ced0d5c..bb8158eb0 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java @@ -87,7 +87,7 @@ public abstract class AnnotationParser extends AnnotationBase { */ void readEmbeddedAttributeOverrides(DeployBeanPropertyAssocOne prop) { - Set attrOverrides = getAll(prop, AttributeOverride.class); + Set attrOverrides = annotationAttributeOverrides(prop); if (!attrOverrides.isEmpty()) { HashMap propMap = new HashMap<>(attrOverrides.size()); for (AttributeOverride attrOverride : attrOverrides) { @@ -95,7 +95,6 @@ public abstract class AnnotationParser extends AnnotationBase { } prop.getDeployEmbedded().putAll(propMap); } - } void readColumn(Column columnAnn, DeployBeanProperty prop) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java index df9622b02..5001f2f70 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java @@ -16,8 +16,7 @@ class AnnotationSql extends AnnotationParser { @Override public void parse() { Class cls = descriptor.getBeanType(); - Sql sql = AnnotationUtil.findAnnotationRecursive(cls, Sql.class); - if (sql != null) { + if (AnnotationUtil.typeHas(cls, Sql.class)) { descriptor.setEntityType(BeanDescriptor.EntityType.SQL); } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java index bdaf79f44..e8223abd4 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java @@ -23,7 +23,6 @@ import javax.persistence.ManyToOne; import javax.persistence.PersistenceException; import javax.persistence.Transient; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -53,7 +52,6 @@ public class DeployCreateProperties { * Create the appropriate properties for a bean. */ public void createProperties(DeployBeanDescriptor desc) { - createProperties(desc, desc.getBeanType(), 0); desc.sortProperties(); } @@ -89,22 +87,14 @@ public class DeployCreateProperties { // ignore all fields on model (_$dbName) return; } - boolean scalaObject = desc.isScalaObject(); try { - Method[] declaredMethods = beanType.getDeclaredMethods(); Field[] fields = beanType.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { - Field field = fields[i]; if (!ignoreField(field)) { - String fieldName = getFieldName(field, beanType); - String initFieldName = initCap(fieldName); - - Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject); - - DeployBeanProperty prop = createProp(desc, field, beanType, getter); + DeployBeanProperty prop = createProp(desc, field, beanType); if (prop != null) { // set a order that gives priority to inherited properties // push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down @@ -137,68 +127,6 @@ public class DeployCreateProperties { } } - /** - * Make the first letter of the string upper case. - */ - private String initCap(String str) { - if (str.length() > 1) { - return Character.toUpperCase(str.charAt(0)) + str.substring(1); - } else { - // only a single char - return str.toUpperCase(); - } - } - - /** - * Return the bean spec field name (trim of "is" from boolean types) - */ - private String getFieldName(Field field, Class beanType) { - - String name = field.getName(); - - if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) - && name.startsWith("is") && name.length() > 2) { - - // it is a boolean type field starting with "is" - char c = name.charAt(2); - if (Character.isUpperCase(c)) { - String msg = "trimming off 'is' from boolean field name " + name + " in class " + beanType.getName(); - logger.info(msg); - - return name.substring(2); - } - } - return name; - } - - /** - * Find a public non-static getter method that matches this field (according to bean-spec rules). - */ - private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) { - - String methGetName = "get" + initFieldName; - String methIsName = "is" + initFieldName; - String scalaGet = field.getName(); - - for (Method m : declaredMethods) { - if ((scalaObject && m.getName().equals(scalaGet)) || m.getName().equals(methGetName) - || m.getName().equals(methIsName)) { - - Class[] params = m.getParameterTypes(); - if (params.length == 0) { - if (field.getType().equals(m.getReturnType())) { - int modifiers = m.getModifiers(); - if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { - // we find it... - return m; - } - } - } - } - } - return null; - } - @SuppressWarnings({"unchecked"}) private DeployBeanProperty createManyType(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { @@ -218,7 +146,7 @@ public class DeployCreateProperties { Class propertyType = field.getType(); - ManyToOne manyToOne = AnnotationUtil.findAnnotation(field, ManyToOne.class); + ManyToOne manyToOne = AnnotationUtil.get(field, ManyToOne.class); if (manyToOne != null) { Class tt = manyToOne.targetEntity(); if (!tt.equals(void.class)) { @@ -235,8 +163,7 @@ public class DeployCreateProperties { // List, Set or Map based object Class targetType = determineTargetType(field); if (targetType == null) { - Transient transAnnotation = AnnotationUtil.findAnnotation(field, Transient.class); - if (transAnnotation != null) { + if (AnnotationUtil.has(field, Transient.class)) { // not supporting this field (generic type used) return null; } @@ -271,21 +198,18 @@ public class DeployCreateProperties { * Return true if the field has one of the special mappings. */ private boolean isSpecialScalarType(Field field) { - return (AnnotationUtil.findAnnotation(field, DbJson.class) != null) - || (AnnotationUtil.findAnnotation(field, DbJsonB.class) != null) - || (AnnotationUtil.findAnnotation(field, DbArray.class) != null) - || (AnnotationUtil.findAnnotation(field, DbMap.class) != null) - || (AnnotationUtil.findAnnotation(field, UnmappedJson.class) != null); + return (AnnotationUtil.has(field, DbJson.class)) + || (AnnotationUtil.has(field, DbJsonB.class)) + || (AnnotationUtil.has(field, DbArray.class)) + || (AnnotationUtil.has(field, DbMap.class)) + || (AnnotationUtil.has(field, UnmappedJson.class)); } private boolean isTransientField(Field field) { - - Transient t = AnnotationUtil.findAnnotation(field, Transient.class); - return (t != null); + return AnnotationUtil.has(field, Transient.class); } - private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field, Class beanType, Method getter) { - + private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field, Class beanType) { DeployBeanProperty prop = createProp(desc, field); if (prop == null) { // transient annotation on unsupported type @@ -293,9 +217,6 @@ public class DeployCreateProperties { } else { prop.setOwningType(beanType); prop.setName(field.getName()); - - // interested in the getter for reading annotations - prop.setReadMethod(getter); prop.setField(field); return prop; } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java index e9252c819..fd9e4641b 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java @@ -8,7 +8,6 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorValue; import javax.persistence.Inheritance; -import java.lang.annotation.Annotation; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -110,11 +109,11 @@ public class DeployInherit { info.setParent(parent); } - Inheritance ia = AnnotationUtil.findAnnotationRecursive(cls, Inheritance.class); + Inheritance ia = AnnotationUtil.typeGet(cls, Inheritance.class); if (ia != null) { ia.strategy(); } - DiscriminatorColumn da = AnnotationUtil.findAnnotationRecursive(cls, DiscriminatorColumn.class); + DiscriminatorColumn da = AnnotationUtil.typeGet(cls, DiscriminatorColumn.class); if (da != null) { // lowercase the discriminator column for RawSql and JSON info.setColumnName(da.name().toLowerCase()); @@ -124,7 +123,7 @@ public class DeployInherit { } if (!info.isAbstract()) { - DiscriminatorValue dv = AnnotationUtil.findAnnotation(cls, DiscriminatorValue.class); // do not search recursive + DiscriminatorValue dv = AnnotationUtil.get(cls, DiscriminatorValue.class); // do not search recursive if (dv != null) { info.setDiscriminatorValue(dv.value()); } else { @@ -144,17 +143,7 @@ public class DeployInherit { } private boolean isInheritanceClass(Class cls) { - while (true) { - if (cls.equals(Object.class)) { - return false; - } - Annotation a = AnnotationUtil.findAnnotationRecursive(cls, Inheritance.class); - if (a != null) { - return true; - } - // search up the inheritance heirarchy - cls = cls.getSuperclass(); - } + return AnnotationUtil.typeHas(cls, Inheritance.class); } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJacksonAnnotation.java b/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJacksonAnnotation.java new file mode 100644 index 000000000..08435de62 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJacksonAnnotation.java @@ -0,0 +1,8 @@ +package io.ebeaninternal.server.deploy.parse; + +class InitMetaJacksonAnnotation { + + static void init(ReadAnnotationConfig readConfig) { + readConfig.addMetaAnnotation(com.fasterxml.jackson.annotation.JacksonAnnotation.class); + } +} diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaValidationAnnotation.java b/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaValidationAnnotation.java new file mode 100644 index 000000000..9d5ac9e30 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaValidationAnnotation.java @@ -0,0 +1,11 @@ +package io.ebeaninternal.server.deploy.parse; + +import javax.validation.constraints.Size; + +class InitMetaValidationAnnotation { + + static void init(ReadAnnotationConfig readConfig) { + readConfig.addMetaAnnotation(Size.class); + readConfig.addMetaAnnotation(Size.List.class); + } +} diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java b/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java index 32e5573a5..e1cb4530a 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java @@ -1,8 +1,15 @@ package io.ebeaninternal.server.deploy.parse; +import io.ebean.annotation.Aggregation; +import io.ebean.annotation.Formula; +import io.ebean.annotation.Where; import io.ebean.config.ServerConfig; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; +import javax.persistence.Column; +import java.util.HashSet; +import java.util.Set; + /** * Configuration used when reading the deployment annotations. */ @@ -17,6 +24,8 @@ class ReadAnnotationConfig { private final boolean jacksonAnnotations; private final boolean idGeneratorAutomatic; + private final Set> metaAnnotations = new HashSet<>(); + ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) { this.generatedPropFactory = generatedPropFactory; @@ -28,6 +37,16 @@ class ReadAnnotationConfig { this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent(); this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent(); + this.metaAnnotations.add(Column.class); + this.metaAnnotations.add(Formula.class); + this.metaAnnotations.add(Formula.List.class); + this.metaAnnotations.add(Where.class); + this.metaAnnotations.add(Where.List.class); + this.metaAnnotations.add(Aggregation.class); + } + + public void addMetaAnnotation(Class annotation) { + metaAnnotations.add(annotation); } GeneratedPropertyFactory getGeneratedPropFactory() { @@ -61,4 +80,8 @@ class ReadAnnotationConfig { boolean isJacksonAnnotations() { return jacksonAnnotations; } + + public Set> getMetaAnnotations() { + return metaAnnotations; + } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java b/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java index 534e9cb82..aad6f547e 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java @@ -14,6 +14,12 @@ public class ReadAnnotations { public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) { this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, serverConfig); + if (readConfig.isJavaxValidationAnnotations()) { + InitMetaValidationAnnotation.init(readConfig); + } + if (readConfig.isJacksonAnnotations()) { + InitMetaJacksonAnnotation.init(readConfig); + } } /** diff --git a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index bada29fe1..521721553 100644 --- a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -459,7 +459,7 @@ public final class DefaultTypeManager implements TypeManager { * are annotated with the @JacksonAnnotation meta annotation. So detection is easy. */ private boolean checkJacksonAnnotations(DeployBeanProperty prop) { - return AnnotationUtil.findAnnotation(prop.getField(), com.fasterxml.jackson.annotation.JacksonAnnotation.class) != null; + return prop.getMetaAnnotation(com.fasterxml.jackson.annotation.JacksonAnnotation.class) != null; } private DocPropertyType getDocType(Type genericType) { @@ -606,7 +606,7 @@ public final class DefaultTypeManager implements TypeManager { Field[] fields = enumType.getDeclaredFields(); for (Field field : fields) { - EnumValue enumValue = AnnotationUtil.findAnnotation(field, EnumValue.class); + EnumValue enumValue = AnnotationUtil.get(field, EnumValue.class); if (enumValue != null) { nameValueMap.put(field.getName(), enumValue.value()); if (integerType && !isIntegerType(enumValue.value())) { @@ -680,7 +680,7 @@ public final class DefaultTypeManager implements TypeManager { Method[] methods = enumType.getMethods(); for (Method method : methods) { - DbEnumValue dbValue = AnnotationUtil.findAnnotation(method, DbEnumValue.class); + DbEnumValue dbValue = AnnotationUtil.get(method, DbEnumValue.class); if (dbValue != null) { boolean integerValues = DbEnumType.INTEGER == dbValue.storage(); return createEnumScalarTypeDbValue(enumType, method, integerValues, dbValue.length()); diff --git a/src/test/java/org/tests/basic/TestAnnotationBase.java b/src/test/java/org/tests/basic/TestAnnotationBase.java index f84ec99ab..573199ce2 100644 --- a/src/test/java/org/tests/basic/TestAnnotationBase.java +++ b/src/test/java/org/tests/basic/TestAnnotationBase.java @@ -1,15 +1,18 @@ package org.tests.basic; import io.ebean.BaseTestCase; +import io.ebean.annotation.Index; import io.ebean.annotation.Platform; import io.ebean.annotation.Where; -import io.ebean.util.AnnotationUtil; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.BeanProperty; +import io.ebeaninternal.server.deploy.IndexDefinition; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import org.junit.Test; import org.tests.model.basic.ValidationGroupSomething; import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.lang.annotation.ElementType; @@ -17,6 +20,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -25,6 +30,13 @@ import static org.junit.Assert.assertTrue; public class TestAnnotationBase extends BaseTestCase { + private final Set> metaAnnotationsFilter = new HashSet<>(); + + public TestAnnotationBase() { + metaAnnotationsFilter.add(Where.class); + metaAnnotationsFilter.add(Where.List.class); + } + @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Where(clause = "SELECT 'mysql' from 1", platforms = Platform.MYSQL) @@ -34,8 +46,16 @@ public class TestAnnotationBase extends BaseTestCase { } + @Index(name = "ano_1", columnNames = "direct") + @Index(name = "ano_2", columnNames = "direct") + @MappedSuperclass + public static class MappedBaseEntity { + + } + + @Index(name = "ano_3", columnNames = "direct") @Entity - public static class TestAnnotationBaseEntity { + public static class TestAnnotationBaseEntity extends MappedBaseEntity { @Where(clause = "SELECT 'mysql' from 1", platforms = Platform.MYSQL) @Where(clause = "SELECT 'h2' from 1", platforms = Platform.H2) @@ -45,7 +65,6 @@ public class TestAnnotationBase extends BaseTestCase { @MetaTest private String meta; - @MetaTest @Where(clause = "SELECT 'oracle' from 1", platforms = Platform.ORACLE) private String mixed; @@ -131,6 +150,13 @@ public class TestAnnotationBase extends BaseTestCase { assertEquals(40, bp.getDbLength()); } + @Test + public void annotationClassIndexes() throws SecurityException { + BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class); + final IndexDefinition[] indexDefinitions = descriptor.getIndexDefinitions(); + assertEquals(3, indexDefinitions.length); + } + @Test public void testNotNullWithGroup() throws SecurityException { BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class); @@ -147,45 +173,41 @@ public class TestAnnotationBase extends BaseTestCase { @Test public void testFindAnnotation() throws NoSuchFieldException, SecurityException { - Field fld = TestAnnotationBaseEntity.class.getDeclaredField("direct"); - String s; - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.MYSQL).clause(); - assertEquals("SELECT 'mysql' from 1", s); + Field directFld = TestAnnotationBaseEntity.class.getDeclaredField("direct"); + final DeployBeanProperty direct = createProperty(directFld); - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.H2).clause(); - assertEquals("SELECT 'h2' from 1", s); - - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.POSTGRES).clause(); - assertEquals("SELECT 'other' from 1", s); + assertEquals("SELECT 'mysql' from 1", where(direct, Platform.MYSQL)); + assertEquals("SELECT 'h2' from 1", where(direct, Platform.H2)); + assertEquals("SELECT 'other' from 1", where(direct, Platform.POSTGRES)); // meta - fld = TestAnnotationBaseEntity.class.getDeclaredField("meta"); - - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.MYSQL).clause(); - assertEquals("SELECT 'mysql' from 1", s); - - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.H2).clause(); - assertEquals("SELECT 'h2' from 1", s); - - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.POSTGRES).clause(); - assertEquals("SELECT 'other' from 1", s); + Field metaFld = TestAnnotationBaseEntity.class.getDeclaredField("meta"); + final DeployBeanProperty meta = createProperty(metaFld); + assertEquals("SELECT 'mysql' from 1", where(meta, Platform.MYSQL)); + assertEquals("SELECT 'h2' from 1", where(meta, Platform.H2)); + assertEquals("SELECT 'other' from 1", where(meta, Platform.POSTGRES)); // mixed - fld = TestAnnotationBaseEntity.class.getDeclaredField("mixed"); + Field mixedFld = TestAnnotationBaseEntity.class.getDeclaredField("mixed"); + final DeployBeanProperty mixed = createProperty(mixedFld); - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.MYSQL).clause(); - assertEquals("SELECT 'mysql' from 1", s); + assertEquals("SELECT 'mysql' from 1", where(mixed, Platform.MYSQL)); + assertEquals("SELECT 'h2' from 1", where(mixed, Platform.H2)); + assertEquals("SELECT 'other' from 1", where(mixed, Platform.POSTGRES)); + assertEquals("SELECT 'oracle' from 1", where(mixed, Platform.ORACLE)); + } - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.H2).clause(); - assertEquals("SELECT 'h2' from 1", s); + private String where(DeployBeanProperty property, Platform platform) { + return property.getMetaAnnotationWhere(platform).clause(); + } - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.POSTGRES).clause(); - assertEquals("SELECT 'other' from 1", s); - - s = AnnotationUtil.findAnnotation(fld, Where.class, Platform.ORACLE).clause(); - assertEquals("SELECT 'oracle' from 1", s); + private DeployBeanProperty createProperty(Field fld) { + DeployBeanProperty directProperty = new DeployBeanProperty(null, null, null); + directProperty.setField(fld); + directProperty.initMetaAnnotations(metaAnnotationsFilter); + return directProperty; } } diff --git a/src/test/java/org/tests/basic/TestWhereAnnotation.java b/src/test/java/org/tests/basic/TestWhereAnnotation.java index 5d02dfcab..9bc32faf9 100644 --- a/src/test/java/org/tests/basic/TestWhereAnnotation.java +++ b/src/test/java/org/tests/basic/TestWhereAnnotation.java @@ -12,6 +12,8 @@ import org.junit.Test; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + public class TestWhereAnnotation extends TransactionalTestCase { @Test @@ -29,6 +31,6 @@ public class TestWhereAnnotation extends TransactionalTestCase { q1.findOne(); String s1 = q1.getGeneratedSql(); - Assert.assertTrue(s1.contains("t1.order_date is not null")); + assertThat(s1).contains("t1.order_date is not null"); } } diff --git a/src/test/java/org/tests/compositekeys/TestOnCascadeDeleteChildrenWithCompositeKeys.java b/src/test/java/org/tests/compositekeys/TestOnCascadeDeleteChildrenWithCompositeKeys.java index e0e632db8..358c1c8e0 100644 --- a/src/test/java/org/tests/compositekeys/TestOnCascadeDeleteChildrenWithCompositeKeys.java +++ b/src/test/java/org/tests/compositekeys/TestOnCascadeDeleteChildrenWithCompositeKeys.java @@ -95,8 +95,10 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { @Entity @Table(name = "em_user") public static class User { + @Id private Long id; private String name; + @OneToMany(cascade = CascadeType.REMOVE) private Set userRoles; public User() { @@ -107,7 +109,6 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { this.name = name; } - @Id public Long getId() { return id; } @@ -124,7 +125,6 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { this.name = name; } - @OneToMany(cascade = CascadeType.REMOVE) public Set getUserRoles() { return userRoles; } @@ -138,11 +138,18 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { @Table(name = "em_user_role") public static class UserRole implements Serializable { private static final long serialVersionUID = 1L; - private UserRolePK pk; - private User user; - private Role role; @EmbeddedId + private UserRolePK pk; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false) + private User user; + + @ManyToOne + @JoinColumn(name = "role_id", nullable = false, insertable = false, updatable = false) + private Role role; + public UserRolePK getPk() { return pk; } @@ -151,8 +158,6 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { this.pk = pk; } - @ManyToOne - @JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false) public User getUser() { return user; } @@ -161,8 +166,6 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { this.user = user; } - @ManyToOne - @JoinColumn(name = "role_id", nullable = false, insertable = false, updatable = false) public Role getRole() { return role; } @@ -213,11 +216,12 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { @Entity @Table(name = "em_role") public static class Role { + @Id private Long id; private String name; + @OneToMany(cascade = CascadeType.REMOVE) private Set userRoles; - @Id public Long getId() { return id; } @@ -234,7 +238,6 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase { this.name = name; } - @OneToMany(cascade = CascadeType.REMOVE) public Set getUserRoles() { return userRoles; } diff --git a/src/test/java/org/tests/idkeys/db/GenKeySequence.java b/src/test/java/org/tests/idkeys/db/GenKeySequence.java index eb55ca796..c1bcbb01e 100644 --- a/src/test/java/org/tests/idkeys/db/GenKeySequence.java +++ b/src/test/java/org/tests/idkeys/db/GenKeySequence.java @@ -4,10 +4,8 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import javax.persistence.SequenceGenerator; @Entity -@SequenceGenerator(name = "SEQ_NAME", sequenceName = GenKeySequence.SEQUENCE_NAME) public class GenKeySequence { public final static String SEQUENCE_NAME = "SEQ"; diff --git a/src/test/java/org/tests/idkeys/db/GenKeyTable.java b/src/test/java/org/tests/idkeys/db/GenKeyTable.java deleted file mode 100644 index 0d490881f..000000000 --- a/src/test/java/org/tests/idkeys/db/GenKeyTable.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.tests.idkeys.db; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -@Entity -public class GenKeyTable { - @Id - @GeneratedValue(strategy = GenerationType.TABLE) - private Long id; - - private String description; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } -} diff --git a/src/test/java/org/tests/model/aggregation/DMachineUse.java b/src/test/java/org/tests/model/aggregation/DMachineUse.java index 0ab4915e5..d8015931f 100644 --- a/src/test/java/org/tests/model/aggregation/DMachineUse.java +++ b/src/test/java/org/tests/model/aggregation/DMachineUse.java @@ -28,6 +28,7 @@ public class DMachineUse extends Model { private long timeSecs; + @Decimal93 private BigDecimal fuel; @Version diff --git a/src/test/java/org/tests/model/aggregation/Decimal93.java b/src/test/java/org/tests/model/aggregation/Decimal93.java new file mode 100644 index 000000000..0e18650f6 --- /dev/null +++ b/src/test/java/org/tests/model/aggregation/Decimal93.java @@ -0,0 +1,16 @@ +package org.tests.model.aggregation; + +import javax.persistence.Column; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Example meta annotation for @Column + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Column(precision = 9, scale = 3) +public @interface Decimal93 { +} diff --git a/src/test/java/org/tests/model/basic/Person.java b/src/test/java/org/tests/model/basic/Person.java index 2c2cd00ee..39496a703 100644 --- a/src/test/java/org/tests/model/basic/Person.java +++ b/src/test/java/org/tests/model/basic/Person.java @@ -18,18 +18,24 @@ public class Person implements Serializable { private static final long serialVersionUID = 495045977245770183L; + @Id + @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) + @SequenceGenerator(name = "PERSONS_SEQ", initialValue = 1000, allocationSize = 40) + @Column(name = "ID", unique = true, nullable = false) private Long id; + + @Column(name = "SURNAME", nullable = false, unique = false, columnDefinition = "varchar(64)") private String surname; + + @Column(name = "NAME", nullable = false, unique = false, columnDefinition = "varchar(64)") private String name; + + @OneToMany(targetEntity = Phone.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person") private List phones; public Person() { } - @Id - @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) - @SequenceGenerator(name = "PERSONS_SEQ", initialValue = 1000, allocationSize = 40) - @Column(name = "ID", unique = true, nullable = false) public Long getId() { return id; } @@ -38,7 +44,6 @@ public class Person implements Serializable { this.id = id; } - @Column(name = "SURNAME", nullable = false, unique = false, columnDefinition = "varchar(64)") public String getSurname() { return surname; } @@ -47,7 +52,6 @@ public class Person implements Serializable { this.surname = surname; } - @Column(name = "NAME", nullable = false, unique = false, columnDefinition = "varchar(64)") public String getName() { return name; } @@ -56,7 +60,6 @@ public class Person implements Serializable { this.name = name; } - @OneToMany(targetEntity = Phone.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person") public List getPhones() { return phones; } diff --git a/src/test/java/org/tests/model/basic/Phone.java b/src/test/java/org/tests/model/basic/Phone.java index 94b45624d..3ba7b3ebe 100644 --- a/src/test/java/org/tests/model/basic/Phone.java +++ b/src/test/java/org/tests/model/basic/Phone.java @@ -18,16 +18,21 @@ public class Phone implements Serializable { private static final long serialVersionUID = -326610269092956952L; + @Id + @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) + @Column(name = "id", unique = true, nullable = false) private Long id; + + @Column(name = "phone_number", nullable = false, unique = true, columnDefinition = "varchar(7)") private String phoneNumber; + + @ManyToOne(targetEntity = Person.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "person_id", nullable = false) private Person person; public Phone() { } - @Id - @GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) - @Column(name = "id", unique = true, nullable = false) public Long getId() { return id; } @@ -36,7 +41,6 @@ public class Phone implements Serializable { this.id = id; } - @Column(name = "phone_number", nullable = false, unique = true, columnDefinition = "varchar(7)") public String getPhoneNumber() { return phoneNumber; } @@ -46,8 +50,6 @@ public class Phone implements Serializable { } @NotNull - @ManyToOne(targetEntity = Person.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @JoinColumn(name = "person_id", nullable = false) public Person getPerson() { return person; } diff --git a/src/test/java/org/tests/model/embedded/RevisionId.java b/src/test/java/org/tests/model/embedded/RevisionId.java index b3e32e6bc..dd624cabb 100644 --- a/src/test/java/org/tests/model/embedded/RevisionId.java +++ b/src/test/java/org/tests/model/embedded/RevisionId.java @@ -1,6 +1,5 @@ package org.tests.model.embedded; -import javax.persistence.Column; import javax.persistence.Embeddable; @Embeddable @@ -18,7 +17,6 @@ public class RevisionId { this.primaryId = primaryId; } - @Column(name = "revision") public Integer getRevision() { return revision; }