#2169 Support for jakarta.validation.constraints.NotNull and (#2170)

jakarta.validation.constraints.Size
This commit is contained in:
André Camilo
2021-02-17 18:04:43 +13:00
committed by GitHub
parent de439e7695
commit 3bb4126e26
15 changed files with 191 additions and 30 deletions
+1 -1
View File
@@ -293,6 +293,6 @@ ebean.tenant.schemaProvider
ebean.updateAllPropertiesInBatch
ebean.updateChangesOnly
ebean.updatesDeleteMissingChildren
ebean.useJavaxValidationNotNull
ebean.useValidationNotNull
ebean.useJtaTransactionManager
+6
View File
@@ -109,6 +109,12 @@
<version>1.1.0.Final</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
@@ -40,6 +40,13 @@ public class ClassLoadConfig {
return isPresent("javax.validation.constraints.NotNull");
}
/**
* Return true if jakarta validation annotations like Size and NotNull are present.
*/
public boolean isJakartaValidationAnnotationsPresent() {
return isPresent("jakarta.validation.constraints.NotNull");
}
/**
* Return true if javax PostConstruct annotation is present (maybe not in java9).
* If not we don't support PostConstruct lifecycle events.
@@ -489,7 +489,7 @@ public class DatabaseConfig {
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
*/
private boolean useJavaxValidationNotNull = true;
private boolean useValidationNotNull = true;
/**
* Generally we want to perform L2 cache notification in the background and not impact
@@ -2809,7 +2809,7 @@ public class DatabaseConfig {
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
useValidationNotNull = p.getBoolean("useValidationNotNull", useValidationNotNull);
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
@@ -3056,20 +3056,21 @@ public class DatabaseConfig {
/**
* Returns if we use javax.validation.constraints.NotNull
*/
public boolean isUseJavaxValidationNotNull() {
return useJavaxValidationNotNull;
public boolean isUseValidationNotNull() {
return useValidationNotNull;
}
/**
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code>
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code> or
* <code>&x64;jakarta.validation.contstraints.NotNull</code>
* with respect to generating a <code>NOT NULL</code> column.
* <p>
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
* Set this to <code>false</code> and the javax NotNull annotation is effectively ignored (and
* we instead use Ebean's own NotNull annotation or JPA Column(nullable=false) annotation.
*/
public void setUseJavaxValidationNotNull(boolean useJavaxValidationNotNull) {
this.useJavaxValidationNotNull = useJavaxValidationNotNull;
public void setUseValidationNotNull(boolean useValidationNotNull) {
this.useValidationNotNull = useValidationNotNull;
}
/**
+6
View File
@@ -138,6 +138,12 @@
<version>1.1.0.Final</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>joda-time</groupId>
@@ -1140,7 +1140,7 @@ public class DeployBeanProperty {
return result;
}
public List<Size> getMetaAnnotationSize() {
public List<Size> getMetaAnnotationJavaxSize() {
final List<Size> size = getMetaAnnotations(Size.class);
final List<Size.List> lists = getMetaAnnotations(Size.List.class);
for (Size.List list : lists) {
@@ -1149,6 +1149,15 @@ public class DeployBeanProperty {
return size;
}
public List<jakarta.validation.constraints.Size> getMetaAnnotationJakartaSize() {
final List<jakarta.validation.constraints.Size> size = getMetaAnnotations(jakarta.validation.constraints.Size.class);
final List<jakarta.validation.constraints.Size.List> lists = getMetaAnnotations(jakarta.validation.constraints.Size.List.class);
for (jakarta.validation.constraints.Size.List list : lists) {
Collections.addAll(size, list.value());
}
return size;
}
public Formula getMetaAnnotationFormula(Platform platform) {
Formula fallback = null;
for (Annotation ann : metaAnnotations) {
@@ -4,13 +4,11 @@ import io.ebean.annotation.DbForeignKey;
import io.ebean.annotation.FetchPreference;
import io.ebean.annotation.TenantId;
import io.ebean.annotation.Where;
import io.ebean.config.BeanNotRegisteredException;
import io.ebean.config.NamingConvention;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import io.ebeaninternal.server.query.SqlJoinType;
@@ -117,7 +115,7 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
if (nonNull != null) {
prop.setNullable(false);
}
if (validationAnnotations) {
if (javaxValidationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
prop.setNullable(false);
@@ -125,6 +123,14 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
prop.getTableJoin().setType(SqlJoinType.INNER);
}
}
if (jakartaValidationAnnotations) {
jakarta.validation.constraints.NotNull notNull = get(prop, jakarta.validation.constraints.NotNull.class);
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
prop.setNullable(false);
// overrides optional attribute of ManyToOne etc
prop.getTableJoin().setType(SqlJoinType.INNER);
}
}
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
@@ -258,19 +258,38 @@ public class AnnotationFields extends AnnotationParser {
}
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 (javaxValidationAnnotations) {
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 (jakartaValidationAnnotations) {
jakarta.validation.constraints.NotNull notNull = get(prop, jakarta.validation.constraints.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 (javaxValidationAnnotations) {
for (Size size : prop.getMetaAnnotationJavaxSize()) {
if (size.max() < Integer.MAX_VALUE) {
maxSize = Math.max(maxSize, size.max());
}
}
}
if (jakartaValidationAnnotations) {
for (jakarta.validation.constraints.Size size : prop.getMetaAnnotationJakartaSize()) {
if (size.max() < Integer.MAX_VALUE) {
maxSize = Math.max(maxSize, size.max());
}
}
}
if (maxSize != -1) {
@@ -280,7 +299,7 @@ public class AnnotationFields extends AnnotationParser {
}
private void initTenantId(DeployBeanProperty prop) {
if (validationAnnotations) {
if (javaxValidationAnnotations || jakartaValidationAnnotations) {
initValidation(prop);
}
if (has(prop, TenantId.class)) {
@@ -26,14 +26,17 @@ public abstract class AnnotationParser extends AnnotationBase {
final Class<?> beanType;
final boolean validationAnnotations;
final boolean javaxValidationAnnotations;
final boolean jakartaValidationAnnotations;
final ReadAnnotationConfig readConfig;
AnnotationParser(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
super(info.getUtil());
this.readConfig = readConfig;
this.validationAnnotations = readConfig.isJavaxValidationAnnotations();
this.javaxValidationAnnotations = readConfig.isJavaxValidationAnnotations();
this.jakartaValidationAnnotations = readConfig.isJakartaValidationAnnotations();
this.info = info;
this.beanType = info.getDescriptor().getBeanType();
this.descriptor = info.getDescriptor();
@@ -132,7 +135,7 @@ public abstract class AnnotationParser extends AnnotationBase {
* can be applied to DDL generation.
*/
boolean isEbeanValidationGroups(Class<?>[] groups) {
if (!util.isUseJavaxValidationNotNull()) {
if (!util.isUseValidationNotNull()) {
return false;
}
return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]);
@@ -57,7 +57,7 @@ public class DeployUtil {
private final Encryptor bytesEncryptor;
private final boolean useJavaxValidationNotNull;
private final boolean useValidationNotNull;
public DeployUtil(TypeManager typeMgr, DatabaseConfig config) {
this.typeManager = typeMgr;
@@ -67,7 +67,7 @@ public class DeployUtil {
this.encryptKeyManager = config.getEncryptKeyManager();
Encryptor be = config.getEncryptor();
this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor();
this.useJavaxValidationNotNull = config.isUseJavaxValidationNotNull();
this.useValidationNotNull = config.isUseValidationNotNull();
}
public TypeManager getTypeManager() {
@@ -286,8 +286,8 @@ public class DeployUtil {
return type.equals(String.class);
}
boolean isUseJavaxValidationNotNull() {
return useJavaxValidationNotNull;
boolean isUseValidationNotNull() {
return useValidationNotNull;
}
/**
@@ -0,0 +1,11 @@
package io.ebeaninternal.server.deploy.parse;
import jakarta.validation.constraints.Size;
class InitMetaJakartaValidationAnnotation {
static void init(ReadAnnotationConfig readConfig) {
readConfig.addMetaAnnotation(Size.class);
readConfig.addMetaAnnotation(Size.List.class);
}
}
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
import javax.validation.constraints.Size;
class InitMetaValidationAnnotation {
class InitMetaJavaxValidationAnnotation {
static void init(ReadAnnotationConfig readConfig) {
readConfig.addMetaAnnotation(Size.class);
@@ -21,6 +21,7 @@ class ReadAnnotationConfig {
private final boolean disableL2Cache;
private final boolean eagerFetchLobs;
private final boolean javaxValidationAnnotations;
private final boolean jakartaValidationAnnotations;
private final boolean jacksonAnnotations;
private final boolean idGeneratorAutomatic;
@@ -34,6 +35,7 @@ class ReadAnnotationConfig {
this.eagerFetchLobs = config.isEagerFetchLobs();
this.idGeneratorAutomatic = config.isIdGeneratorAutomatic();
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
this.jakartaValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJakartaValidationAnnotationsPresent();
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
this.metaAnnotations.add(Column.class);
this.metaAnnotations.add(Formula.class);
@@ -75,6 +77,10 @@ class ReadAnnotationConfig {
return javaxValidationAnnotations;
}
boolean isJakartaValidationAnnotations() {
return jakartaValidationAnnotations;
}
boolean isJacksonAnnotations() {
return jacksonAnnotations;
}
@@ -15,7 +15,10 @@ public class ReadAnnotations {
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) {
this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, config);
if (readConfig.isJavaxValidationAnnotations()) {
InitMetaValidationAnnotation.init(readConfig);
InitMetaJavaxValidationAnnotation.init(readConfig);
}
if (readConfig.isJakartaValidationAnnotations()) {
InitMetaJakartaValidationAnnotation.init(readConfig);
}
if (readConfig.isJacksonAnnotations()) {
InitMetaJacksonAnnotation.init(readConfig);
@@ -144,6 +144,69 @@ public class TestAnnotationBase extends BaseTestCase {
}
}
@Entity
public static class TestJakartaAnnotationBaseEntity extends MappedBaseEntity {
@Where(clause = "SELECT 'mysql' from 1", platforms = Platform.MYSQL)
@Where(clause = "SELECT 'h2' from 1", platforms = H2)
@Where(clause = "SELECT 'other' from 1")
private String direct;
@MetaTest
private String meta;
@MetaTest
@Where(clause = "SELECT 'oracle' from 1", platforms = Platform.ORACLE)
private String mixed;
@jakarta.validation.constraints.Size.List({
@jakarta.validation.constraints.Size(max = 10, message = "max length for you is 10"),
@jakarta.validation.constraints.Size(min = 1),
@jakarta.validation.constraints.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;
private String null3;
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 SecurityException {
@@ -152,6 +215,13 @@ public class TestAnnotationBase extends BaseTestCase {
assertEquals(40, bp.getDbLength());
}
@Test
public void testFindJakartaMaxSize() throws SecurityException {
BeanDescriptor<TestJakartaAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class);
BeanProperty bp = descriptor.findProperty("constraintAnnotation");
assertEquals(40, bp.getDbLength());
}
@Test
public void annotationClassIndexes() throws SecurityException {
BeanDescriptor<TestAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class);
@@ -173,6 +243,20 @@ public class TestAnnotationBase extends BaseTestCase {
assertTrue(bp.isNullable());
}
@Test
public void testJakartaNotNullWithGroup() throws SecurityException {
BeanDescriptor<TestJakartaAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class);
BeanProperty bp = descriptor.findProperty("null1");
assertFalse(bp.isNullable());
bp = descriptor.findProperty("null2");
assertTrue(bp.isNullable());
bp = descriptor.findProperty("null3");
assertTrue(bp.isNullable());
}
@Test
public void testFindAnnotation() throws NoSuchFieldException, SecurityException {