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 extends DatabasePlatform>[] platforms() default {};
+ *
+ * The finding rules are:
+ *
+ * - Check if T has method "platforms" if not, return
ann[0]
+ * - find the annotation that is defined for
databasePlatform
+ * - otherwise return the annotation for default platform (platforms = {})
+ * - return null
+ *
+ * (This mechanism is currently used by {@link Where} and {@link Formula})
+ */
+ public static T getPlatformMatchingAnnotation(Set anns, Class extends DatabasePlatform> databasePlatform) {
+ 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 (!Class[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) {
+ return ann;
+ }
+ Class>[] platforms = (Class[]) getPlatformsMethod.invoke(ann);
+ if (platforms.length == 0) {
+ fallback = ann;
+ } else {
+ for (Class> platform : platforms) {
+ if (databasePlatform.isAssignableFrom(platform)) {
+ return ann;
+ }
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ return ann; // not platform specific - return first one
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return fallback;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java
index 3fe8be9a7..32de78996 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java
@@ -18,6 +18,8 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Set;
+
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@@ -119,15 +121,8 @@ public class AnnotationClass extends AnnotationParser {
descriptor.setName("Embeddable:" + cls.getSimpleName());
}
- Indices indices = AnnotationBase.findAnnotation(cls, Indices.class);
- if (indices != null) {
- for (Index index: indices.value()) {
- descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
- }
- }
-
- Index index = AnnotationBase.findAnnotation(cls,Index.class);
- if (index != null) {
+ Set indices = AnnotationBase.findAnnotations(cls, Index.class);
+ for (Index index: indices) {
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
}
@@ -188,15 +183,8 @@ public class AnnotationClass extends AnnotationParser {
descriptor.setCache(cache);
}
- NamedQueries namedQueries = AnnotationBase.findAnnotation(cls,NamedQueries.class);
- if (namedQueries != null) {
- for (NamedQuery namedQuery : namedQueries.value()) {
- descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
- }
- }
-
- NamedQuery namedQuery = AnnotationBase.findAnnotation(cls,NamedQuery.class);
- if (namedQuery != null) {
+ Set namedQueries = AnnotationBase.findAnnotations(cls,NamedQuery.class);
+ for (NamedQuery namedQuery : namedQueries) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
index 022044ec8..7d6b988c5 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
@@ -26,6 +26,7 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.sql.Types;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
/**
@@ -251,18 +252,19 @@ public class AnnotationFields extends AnnotationParser {
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
- if (notNull != null && isNotNullOnAllValidationGroups(notNull.groups())) {
+ if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
// Not null on all validation groups so enable
// DDL generation of Not Null Constraint
prop.setNullable(false);
}
- Size size = get(prop, Size.class);
- if (size != null) {
- if (size.max() < Integer.MAX_VALUE) {
- // explicitly specify a version column
- prop.setDbLength(size.max());
- }
+ // take the max size of all @Size annotations
+ int maxSize = -1;
+ for (Size size : getAll(prop, Size.class)) {
+ maxSize = Math.max(maxSize, size.max());
+ }
+ if (maxSize !=1 && maxSize < Integer.MAX_VALUE) {
+ prop.setDbLength(maxSize);
}
}
@@ -310,15 +312,8 @@ public class AnnotationFields extends AnnotationParser {
}
}
- Indices indices = get(prop, Indices.class);
- if (indices != null) {
- for (Index index: indices.value()) {
- addIndex(prop, index);
- }
- }
-
- Index index = get(prop, Index.class);
- if (index != null) {
+ Set indices = getAll(prop, Index.class);
+ for (Index index: indices) {
addIndex(prop, index);
}
}
@@ -378,14 +373,6 @@ public class AnnotationFields extends AnnotationParser {
get(prop, OneToOne.class) != null;
}
- /**
- * Return true if the validation is on all validation groups and hence
- * can be applied to DDL generation.
- */
- private boolean isNotNullOnAllValidationGroups(Class>[] groups) {
- return groups.length == 0 || groups.length == 1 && javax.validation.groups.Default.class.isAssignableFrom(groups[0]);
- }
-
private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) {
util.checkEncryptKeyManagerDefined(prop.getFullBeanName());
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java
index 031681a5b..b56f1ee3c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java
@@ -1,12 +1,14 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.HashMap;
+import java.util.Set;
import javax.persistence.AttributeOverride;
-import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
+import javax.validation.groups.Default;
+import com.avaje.ebean.annotation.EbeanDDL;
import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
@@ -52,17 +54,12 @@ public abstract class AnnotationParser extends AnnotationBase {
*/
protected void readEmbeddedAttributeOverrides(DeployBeanPropertyAssocOne> prop) {
- AttributeOverrides attrOverrides = get(prop, AttributeOverrides.class);
- if (attrOverrides != null) {
- HashMap propMap = new HashMap();
- AttributeOverride[] aoArray = attrOverrides.value();
- for (int i = 0; i < aoArray.length; i++) {
- String propName = aoArray[i].name();
- String columnName = aoArray[i].column().name();
-
- propMap.put(propName, columnName);
+ Set attrOverrides = getAll(prop, AttributeOverride.class);
+ if (!attrOverrides.isEmpty()) {
+ HashMap propMap = new HashMap<>();
+ for (AttributeOverride attrOverride : attrOverrides) {
+ propMap.put(attrOverride.name(), attrOverride.column().name());
}
-
prop.getDeployEmbedded().putAll(propMap);
}
@@ -95,4 +92,22 @@ public abstract class AnnotationParser extends AnnotationBase {
prop.setSecondaryTable(tableName);
}
}
+
+ /**
+ * Return true if the validation groups are {@link Default} (respectively empty) or contains {@link EbeanDDL}
+ * can be applied to DDL generation.
+ */
+ protected boolean isEbeanValidationGroups(Class>[] groups) {
+ if (groups.length == 0
+ || groups.length == 1 && javax.validation.groups.Default.class.isAssignableFrom(groups[0])) {
+ return true;
+ } else {
+ for (Class> group : groups) {
+ if (EbeanDDL.class.isAssignableFrom(group)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
From 06e265ec476263b89d2a9114ad6aea780977df8a Mon Sep 17 00:00:00 2001
From: Roland Praml
Date: Tue, 1 Nov 2016 00:02:44 +0100
Subject: [PATCH 2/4] Provide test case
---
.../server/deploy/parse/AnnotationFields.java | 6 +-
.../avaje/tests/basic/TestAnnotationBase.java | 197 ++++++++++++++++++
2 files changed, 201 insertions(+), 2 deletions(-)
create mode 100644 src/test/java/com/avaje/tests/basic/TestAnnotationBase.java
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
index 7d6b988c5..1ebefdec8 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
@@ -261,9 +261,11 @@ public class AnnotationFields extends AnnotationParser {
// take the max size of all @Size annotations
int maxSize = -1;
for (Size size : getAll(prop, Size.class)) {
- maxSize = Math.max(maxSize, size.max());
+ if (size.max() < Integer.MAX_VALUE) {
+ maxSize = Math.max(maxSize, size.max());
+ }
}
- if (maxSize !=1 && maxSize < Integer.MAX_VALUE) {
+ if (maxSize != -1) {
prop.setDbLength(maxSize);
}
}
diff --git a/src/test/java/com/avaje/tests/basic/TestAnnotationBase.java b/src/test/java/com/avaje/tests/basic/TestAnnotationBase.java
new file mode 100644
index 000000000..64565a9cc
--- /dev/null
+++ b/src/test/java/com/avaje/tests/basic/TestAnnotationBase.java
@@ -0,0 +1,197 @@
+package com.avaje.tests.basic;
+
+import static org.junit.Assert.*;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+
+import javax.persistence.Entity;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.junit.Test;
+
+import com.avaje.ebean.BaseTestCase;
+import com.avaje.ebean.annotation.EbeanDDL;
+import com.avaje.ebean.annotation.Where;
+import com.avaje.ebean.config.dbplatform.H2Platform;
+import com.avaje.ebean.config.dbplatform.MySqlPlatform;
+import com.avaje.ebean.config.dbplatform.OraclePlatform;
+import com.avaje.ebean.config.dbplatform.PostgresPlatform;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+import com.avaje.ebeaninternal.server.deploy.parse.AnnotationBase;
+import com.avaje.tests.model.basic.ValidationGroupSomething;
+
+
+public class TestAnnotationBase extends BaseTestCase {
+
+ @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Where(clause = "SELECT 'mysql' from 1", platforms = MySqlPlatform.class)
+ @Where(clause = "SELECT 'h2' from 1", platforms = H2Platform.class)
+ @Where(clause = "SELECT 'other' from 1")
+ public @interface MetaTest {
+
+ }
+
+ @Entity
+ public static class TestAnnotationBaseEntity {
+
+ @Where(clause = "SELECT 'mysql' from 1", platforms = MySqlPlatform.class)
+ @Where(clause = "SELECT 'h2' from 1", platforms = H2Platform.class)
+ @Where(clause = "SELECT 'other' from 1")
+ private String direct;
+
+ @MetaTest
+ private String meta;
+
+
+ @MetaTest
+ @Where(clause = "SELECT 'oracle' from 1", platforms = OraclePlatform.class)
+ private String mixed;
+
+ @Size.List({
+ @Size(max=10, message="max length for you is 10"),
+ @Size(min=1),
+ @Size(max=40, message="max value for you is 40", groups = ValidationGroupSomething.class)
+ })
+ private String constraintAnnotation;
+
+ @NotNull
+ private String null1;
+
+
+ @NotNull(groups = ValidationGroupSomething.class)
+ private String null2;
+
+ @NotNull(groups = {ValidationGroupSomething.class, EbeanDDL.class})
+ private String null3;
+
+
+ public String getDirect() {
+ return direct;
+ }
+
+ public void setDirect(String direct) {
+ this.direct = direct;
+ }
+
+ public String getMeta() {
+ return meta;
+ }
+
+ public void setMeta(String meta) {
+ this.meta = meta;
+ }
+
+ public String getMixed() {
+ return mixed;
+ }
+
+ public void setMixed(String mixed) {
+ this.mixed = mixed;
+ }
+
+ public String getConstraintAnnotation() {
+ return constraintAnnotation;
+ }
+
+ public void setConstraintAnnotation(String constraintAnnotation) {
+ this.constraintAnnotation = constraintAnnotation;
+ }
+
+ public String getNull1() {
+ return null1;
+ }
+
+ public void setNull1(String null1) {
+ this.null1 = null1;
+ }
+
+ public String getNull2() {
+ return null2;
+ }
+
+ public void setNull2(String null2) {
+ this.null2 = null2;
+ }
+
+ public String getNull3() {
+ return null3;
+ }
+
+ public void setNull3(String null3) {
+ this.null3 = null3;
+ }
+ }
+
+
+ @Test
+ public void testFindMaxSize() throws NoSuchFieldException, SecurityException {
+ BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class);
+ BeanProperty bp = descriptor.findBeanProperty("constraintAnnotation");
+ assertEquals(40, bp.getDbLength());
+ }
+
+ @Test
+ public void testNotNullWithGroup() throws NoSuchFieldException, SecurityException {
+ BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class);
+
+ BeanProperty bp = descriptor.findBeanProperty("null1");
+ assertFalse(bp.isNullable());
+
+ bp = descriptor.findBeanProperty("null2");
+ assertTrue(bp.isNullable());
+
+ bp = descriptor.findBeanProperty("null3");
+ assertFalse(bp.isNullable());
+ }
+
+ @Test
+ public void testFindAnnotation() throws NoSuchFieldException, SecurityException {
+ Field fld = TestAnnotationBaseEntity.class.getDeclaredField("direct");
+ String s;
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, MySqlPlatform.class).clause();
+ assertEquals("SELECT 'mysql' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, H2Platform.class).clause();
+ assertEquals("SELECT 'h2' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, PostgresPlatform.class).clause();
+ assertEquals("SELECT 'other' from 1",s);
+
+ // meta
+ fld = TestAnnotationBaseEntity.class.getDeclaredField("meta");
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, MySqlPlatform.class).clause();
+ assertEquals("SELECT 'mysql' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, H2Platform.class).clause();
+ assertEquals("SELECT 'h2' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, PostgresPlatform.class).clause();
+ assertEquals("SELECT 'other' from 1",s);
+
+
+ // mixed
+ fld = TestAnnotationBaseEntity.class.getDeclaredField("mixed");
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, MySqlPlatform.class).clause();
+ assertEquals("SELECT 'mysql' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, H2Platform.class).clause();
+ assertEquals("SELECT 'h2' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, PostgresPlatform.class).clause();
+ assertEquals("SELECT 'other' from 1",s);
+
+ s= AnnotationBase.findAnnotation(fld, Where.class, OraclePlatform.class).clause();
+ assertEquals("SELECT 'oracle' from 1",s);
+ }
+
+}
From 6142dd34f43a765c5f62381b26ec1c9085040e67 Mon Sep 17 00:00:00 2001
From: Roland Praml
Date: Tue, 1 Nov 2016 00:27:12 +0100
Subject: [PATCH 3/4] No effective code change - removed unused imports
---
.../avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java | 1 -
.../ebeaninternal/server/deploy/parse/AnnotationClass.java | 2 --
2 files changed, 3 deletions(-)
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java
index 9048a3d69..f0d8d9b45 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java
@@ -18,7 +18,6 @@ import java.util.concurrent.ConcurrentMap;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
-import javax.validation.constraints.Size;
/**
* Provides some base methods for processing deployment annotations.
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java
index 32de78996..22a9c11ed 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java
@@ -7,7 +7,6 @@ import com.avaje.ebean.annotation.Draftable;
import com.avaje.ebean.annotation.DraftableElement;
import com.avaje.ebean.annotation.History;
import com.avaje.ebean.annotation.Index;
-import com.avaje.ebean.annotation.Indices;
import com.avaje.ebean.annotation.ReadAudit;
import com.avaje.ebean.annotation.UpdateMode;
import com.avaje.ebean.annotation.View;
@@ -24,7 +23,6 @@ import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
-import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
From 64c67e37556040fec3e0068e4514b9fee83cc43a Mon Sep 17 00:00:00 2001
From: Roland Praml
Date: Tue, 1 Nov 2016 00:43:17 +0100
Subject: [PATCH 4/4] removed debug code and completed documentation
---
.../server/deploy/parse/AnnotationBase.java | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java
index f0d8d9b45..21df2ac49 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java
@@ -64,22 +64,20 @@ public abstract class AnnotationBase {
Field field = prop.getField();
if (field != null) {
a = findAnnotation(field, annClass);
- if (a != findAnnotation(field, annClass, databasePlatform.getClass())) {
- System.out.println("Difference (field) " + field + ", " + annClass);
- }
}
if (a == null) {
Method method = prop.getReadMethod();
if (method != null) {
a = findAnnotation(method, annClass);
- if (a != findAnnotation(method, annClass, databasePlatform.getClass())) {
- System.out.println("Difference (method) " + method + ", " + annClass);
- }
}
}
return a;
}
+ /**
+ * 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.
+ */
protected Set getAll(DeployBeanProperty prop, Class annClass) {
Set ret = null;
Field field = prop.getField();