Merge pull request #847 from FOCONIS/feature/platform-dependent-annotations

platform dependent @Formula and @Where annotations
This commit is contained in:
Rob Bygrave
2016-11-20 20:56:27 +13:00
committed by GitHub
11 changed files with 507 additions and 82 deletions
@@ -0,0 +1,11 @@
package com.avaje.ebean.annotation;
import javax.validation.constraints.NotNull;
/**
* special validation group for &#64;NotNull annotation to enforce <code>NOT NULL</code> generation on DDL.
* Normally if you put the {@link NotNull} annotation on a property, Ebean will only generate a
* <code>NOT NULL</code> in DDL if you do not change the validation-groups!
*/
public interface EbeanDDL {
}
@@ -1,8 +1,10 @@
package com.avaje.ebean.annotation;
import com.avaje.ebean.Query;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -66,6 +68,7 @@ import java.lang.annotation.Target;
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Formula.List.class)
public @interface Formula {
/**
@@ -100,5 +103,17 @@ public @interface Formula {
* }</pre>
*/
String join() default "";
Class<? extends DatabasePlatform>[] platforms() default {};
/**
* Repeatable support for {@link Formula}.
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface List {
Formula[] value() default {};
}
}
@@ -1,10 +1,13 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
/**
* Add an Literal to add to the where clause when a many property (List, Set or
* Map) is loaded or refreshed.
@@ -36,6 +39,7 @@ import java.lang.annotation.Target;
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Where.List.class)
public @interface Where {
/**
@@ -46,5 +50,19 @@ public @interface Where {
* </p>
*/
String clause();
/**
* The platform where this annotation is active. Default: any platform
*/
Class<? extends DatabasePlatform>[] platforms() default {};
/**
* Repeatable support for {@link Where}.
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface List {
Where[] value() default {};
}
}
@@ -6,6 +6,7 @@ import com.avaje.ebeaninternal.server.query.SqlJoinType;
import javax.persistence.JoinColumn;
import java.util.ArrayList;
import java.util.Set;
/**
* Represents a join to another table during deployment phase.
@@ -91,6 +92,15 @@ public class DeployTableJoin {
}
}
/**
* Add a JoinColumn set.
*/
public void addJoinColumn(boolean order, Set<JoinColumn> jcSet, BeanTable beanTable) {
for (JoinColumn jc: jcSet) {
addJoinColumn(order, jc, beanTable);
}
}
/**
* Return the join columns.
*/
@@ -1,7 +1,8 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.Set;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
@@ -87,14 +88,10 @@ public class AnnotationAssocManys extends AnnotationParser {
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable);
}
JoinColumns joinColumns = get(prop, JoinColumns.class);
Set<JoinColumn> joinColumns = getAll(prop, JoinColumn.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable);
prop.getTableJoin().addJoinColumn(true, joinColumns, beanTable);
}
JoinTable joinTable = get(prop, JoinTable.class);
@@ -1,13 +1,13 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
@@ -93,7 +93,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null) {
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
prop.setNullable(false);
// overrides optional attribute of ManyToOne etc
prop.getTableJoin().setType(SqlJoinType.INNER);
@@ -102,8 +102,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
for (JoinColumn joinColumn : getAll(prop, JoinColumn.class)) {
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
@@ -113,14 +112,18 @@ public class AnnotationAssocOnes extends AnnotationParser {
}
}
JoinColumns joinColumns = get(prop, JoinColumns.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable);
}
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable);
for (JoinColumn joinColumn : joinTable.joinColumns()) {
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
}
if (!joinColumn.nullable()) {
prop.setNullable(false);
}
}
}
info.setBeanJoinType(prop, prop.isNullable());
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import com.avaje.ebean.annotation.Formula;
import com.avaje.ebean.annotation.Where;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
@@ -9,7 +11,13 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
/**
* Provides some base methods for processing deployment annotations.
@@ -38,12 +46,18 @@ public abstract class AnnotationBase {
return s == null || s.trim().isEmpty();
}
/**
* Return the annotation for the property.
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method.
* </p>
* <p>
* If a <code>repeatable</code> annotation class is specified and the annotation is platform
* specific(see {@link #getPlatformMatchingAnnotation(Set, Class)}), then the platform specific
* annotation is returned. Otherwise the first annotation is retured. Note that you must no longer
* handle "java 1.6 repeatable containers" like {@link JoinColumn} / {@link JoinColumns} yourself.
* </p>
* <p>
*/
protected <T extends Annotation> T get(DeployBeanProperty prop, Class<T> annClass) {
T a = null;
@@ -52,14 +66,36 @@ public abstract class AnnotationBase {
a = findAnnotation(field, annClass);
}
if (a == null) {
Method m = prop.getReadMethod();
if (m != null) {
a = findAnnotation(m, annClass);
Method method = prop.getReadMethod();
if (method != null) {
a = findAnnotation(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 <T extends Annotation> Set<T> getAll(DeployBeanProperty prop, Class<T> annClass) {
Set<T> ret = null;
Field field = prop.getField();
if (field != null) {
ret = findAnnotations(field, annClass);
}
Method method = prop.getReadMethod();
if (method != null) {
if (ret != null) {
ret.addAll(findAnnotations(method, annClass));
} else {
ret = findAnnotations(method, annClass);
}
}
return ret;
}
/**
* Return the annotation for the property.
* <p>
@@ -69,7 +105,7 @@ public abstract class AnnotationBase {
protected <T extends Annotation> T find(DeployBeanProperty prop, Class<T> annClass) {
T a = get(prop, annClass);
if (a == null) {
a = findAnnotation(prop.getOwningType(), annClass);
a = findAnnotation(prop.getOwningType(), annClass, databasePlatform.getClass());
}
return a;
}
@@ -91,6 +127,7 @@ public abstract class AnnotationBase {
* <p>
* <strong>Warning</strong>: 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!
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
@@ -132,6 +169,19 @@ public abstract class AnnotationBase {
}
}
/**
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* found annotation is returned)
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType,
Class<? extends DatabasePlatform> databasePlatform) {
if (annotationType == null) {
return null;
}
Set<A> anns = findAnnotations(annotatedElement, annotationType);
return getPlatformMatchingAnnotation(anns, databasePlatform);
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
@@ -139,7 +189,7 @@ public abstract class AnnotationBase {
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
Annotation[] anns = annotatedElement.getDeclaredAnnotations();
Annotation[] anns = annotatedElement.getAnnotations(); // directly annotatated or inherited
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
@@ -155,4 +205,144 @@ public abstract class AnnotationBase {
}
return null;
}
}
/**
* Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: 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 <A extends Annotation> Set<A> findAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
Set<A> ret = new LinkedHashSet<A>();
findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<Annotation>());
return ret;
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> void findMetaAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType, Set<A> ret, Set<Annotation> 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);
}
}
}
}
}
// caches for getRepeatableValueMethod
private static final Method getNullMethod() {
try {
return AnnotationBase.class.getDeclaredMethod("getNullMethod");
} catch (NoSuchMethodException e) {
return null;
}
}
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<Annotation, Method>();
private static final Method nullMethod = getNullMethod();
/**
* Returns the <code>value()</code> method for a possible containerAnnotation.
* Method is retuned only, if its signature is <code>array of containingType</code>.
*/
private static <A extends Annotation> Method getRepeatableValueMethod(
Annotation containerAnnotation, Class<A> 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 <code>Set<T> anns</code> for this platform.
* To distinguish between platforms, annotation type <code>T</code> must define
* a method withthis signature:
* <p>
* <code>Class<? extends DatabasePlatform>[] platforms() default {};</code>
* </p>
* The finding rules are:
* <ol>
* <li>Check if T has method "platforms" if not, return <code>ann[0]</code></code>
* <li>find the annotation that is defined for <code>databasePlatform</code></li>
* <li>otherwise return the annotation for default platform (platforms = {})</li>
* <li>return null
* </ol>
* (This mechanism is currently used by {@link Where} and {@link Formula})
*/
public static <T extends Annotation> T getPlatformMatchingAnnotation(Set<T> 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;
}
}
@@ -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;
@@ -19,11 +18,12 @@ 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;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@@ -123,15 +123,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<Index> indices = AnnotationBase.findAnnotations(cls, Index.class);
for (Index index: indices) {
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
}
@@ -192,15 +185,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<NamedQuery> namedQueries = AnnotationBase.findAnnotations(cls,NamedQuery.class);
for (NamedQuery namedQuery : namedQueries) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
@@ -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;
/**
@@ -261,19 +262,22 @@ 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) {
// take the max size of all @Size annotations
int maxSize = -1;
for (Size size : getAll(prop, Size.class)) {
if (size.max() < Integer.MAX_VALUE) {
// explicitly specify a version column
prop.setDbLength(size.max());
maxSize = Math.max(maxSize, size.max());
}
}
if (maxSize != -1) {
prop.setDbLength(maxSize);
}
}
EmbeddedColumns columns = get(prop, EmbeddedColumns.class);
@@ -320,15 +324,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<Index> indices = getAll(prop, Index.class);
for (Index index: indices) {
addIndex(prop, index);
}
}
@@ -388,14 +385,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());
@@ -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,26 +54,15 @@ public abstract class AnnotationParser extends AnnotationBase {
*/
protected void readEmbeddedAttributeOverrides(DeployBeanPropertyAssocOne<?> prop) {
AttributeOverrides attrOverrides = get(prop, AttributeOverrides.class);
if (attrOverrides != null) {
Set<AttributeOverride> attrOverrides = getAll(prop, AttributeOverride.class);
if (!attrOverrides.isEmpty()) {
HashMap<String, String> propMap = new HashMap<>();
AttributeOverride[] aoArray = attrOverrides.value();
for (AttributeOverride anAoArray : aoArray) {
String propName = anAoArray.name();
String columnName = anAoArray.column().name();
propMap.put(propName, columnName);
for (AttributeOverride attrOverride : attrOverrides) {
propMap.put(attrOverride.name(), attrOverride.column().name());
}
prop.getDeployEmbedded().putAll(propMap);
}
AttributeOverride attrOverride = get(prop, AttributeOverride.class);
if (attrOverride != null) {
HashMap<String, String> propMap = new HashMap<>();
propMap.put(attrOverride.name(), attrOverride.column().name());
prop.getDeployEmbedded().putAll(propMap);
}
}
protected void readColumn(Column columnAnn, DeployBeanProperty prop) {
@@ -101,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;
}
}
@@ -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<TestAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class);
BeanProperty bp = descriptor.findBeanProperty("constraintAnnotation");
assertEquals(40, bp.getDbLength());
}
@Test
public void testNotNullWithGroup() throws NoSuchFieldException, SecurityException {
BeanDescriptor<TestAnnotationBaseEntity> 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);
}
}