diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/AbstractGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/AbstractGenerator.java index aae10c76b5..cbbe7d825c 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/AbstractGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/AbstractGenerator.java @@ -66,7 +66,9 @@ abstract class AbstractGenerator implements Generator { boolean generateInstanceFields = true; boolean generateGeneratedAnnotation = false; GeneratedAnnotationType generatedGeneratedAnnotationType = GeneratedAnnotationType.DETECT_FROM_JDK; + boolean generateNonnullAnnotation = false; String generatedNonnullAnnotationType = "javax.annotation.Nonnull"; + boolean generateNullableAnnotation = false; String generatedNullableAnnotationType = "javax.annotation.Nullable"; boolean useSchemaVersionProvider = false; boolean useCatalogVersionProvider = false; @@ -299,6 +301,16 @@ abstract class AbstractGenerator implements Generator { this.generatedGeneratedAnnotationType = generateGeneratedAnnotationType; } + @Override + public boolean generateNonnullAnnotation() { + return generateNonnullAnnotation; + } + + @Override + public void setGenerateNonnullAnnotation(boolean generateNonnullAnnotation) { + this.generateNonnullAnnotation = generateNonnullAnnotation; + } + @Override public String generatedNonnullAnnotationType() { return generatedNonnullAnnotationType; @@ -309,6 +321,16 @@ abstract class AbstractGenerator implements Generator { this.generatedNonnullAnnotationType = generatedNonnullAnnotationType; } + @Override + public boolean generateNullableAnnotation() { + return generateNullableAnnotation; + } + + @Override + public void setGenerateNullableAnnotation(boolean generateNullableAnnotation) { + this.generateNullableAnnotation = generateNullableAnnotation; + } + @Override public String generatedNullableAnnotationType() { return generatedNullableAnnotationType; diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/GenerationTool.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/GenerationTool.java index 0dd4fd380a..63a98ef559 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/GenerationTool.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/GenerationTool.java @@ -669,10 +669,14 @@ public class GenerationTool { generator.setGenerateGeneratedAnnotation(g.getGenerate().isGeneratedAnnotation()); if (g.getGenerate().getGeneratedAnnotationType() != null) generator.setGenerateGeneratedAnnotationType(g.getGenerate().getGeneratedAnnotationType()); - if (g.getGenerate().getGeneratedNonnullAnnotationType() != null) - generator.setGeneratedNonnullAnnotationType(g.getGenerate().getGeneratedNonnullAnnotationType()); - if (g.getGenerate().getGeneratedNullableAnnotationType() != null) - generator.setGeneratedNullableAnnotationType(g.getGenerate().getGeneratedNullableAnnotationType()); + if (g.getGenerate().isNonnullAnnotation() != null) + generator.setGenerateNonnullAnnotation(g.getGenerate().isNonnullAnnotation()); + if (g.getGenerate().getNonnullAnnotationType() != null) + generator.setGeneratedNonnullAnnotationType(g.getGenerate().getNonnullAnnotationType()); + if (g.getGenerate().isNullableAnnotation() != null) + generator.setGenerateNullableAnnotation(g.getGenerate().isNullableAnnotation()); + if (g.getGenerate().getNullableAnnotationType() != null) + generator.setGeneratedNullableAnnotationType(g.getGenerate().getNullableAnnotationType()); if (g.getGenerate().isRoutines() != null) generator.setGenerateRoutines(g.getGenerate().isRoutines()); if (g.getGenerate().isSequences() != null) diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/Generator.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/Generator.java index b67b9b7312..1eb358e70d 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/Generator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/Generator.java @@ -157,6 +157,24 @@ public interface Generator { */ void setGenerateGeneratedAnnotationType(GeneratedAnnotationType generateGeneratedAnnotationType); + /** + * Whether Nonnull annotations should be generated. + *

+ * In SQL and by consequence in jOOQ, non-nullability cannot be guaranteed + * statically. There may still be some cases (e.g. after unions, outer + * joins, etc.) where a normally non-null value turns out to be null! + */ + boolean generateNonnullAnnotation(); + + /** + * Whether Nonnull annotations should be generated. + *

+ * In SQL and by consequence in jOOQ, non-nullability cannot be guaranteed + * statically. There may still be some cases (e.g. after unions, outer + * joins, etc.) where a normally non-null value turns out to be null! + */ + void setGenerateNonnullAnnotation(boolean generateNonnullAnnotation); + /** * Which type of Nonnull annotation should be generated. */ @@ -167,6 +185,24 @@ public interface Generator { */ void setGeneratedNonnullAnnotationType(String generatedNonnullAnnotationType); + /** + * Whether Nullable annotations should be generated. + *

+ * Unlike {@link #generateNonnullAnnotation()}, nullability can be + * guaranteed as in SQL, and by consequence in jOOQ, every column expression + * can be made nullable using some SQL operation. + */ + boolean generateNullableAnnotation(); + + /** + * Whether Nullable annotations should be generated. + *

+ * Unlike {@link #generateNonnullAnnotation()}, nullability can be + * guaranteed as in SQL, and by consequence in jOOQ, every column expression + * can be made nullable using some SQL operation. + */ + void setGenerateNullableAnnotation(boolean generateNullableAnnotation); + /** * Which type of Nullable annotation should be generated. */ diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java index 53c3a9cda4..a7519253e7 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java @@ -1441,6 +1441,8 @@ public class JavaGenerator extends AbstractGenerator { else out.tab(1).overrideInherit(); + printNullableOrNonnullAnnotation(out, column); + out.tab(1).println("public %s component%s() {", colType, i); out.tab(2).println("return %s();", colGetter); out.tab(1).println("}"); @@ -1465,6 +1467,8 @@ public class JavaGenerator extends AbstractGenerator { else out.tab(1).overrideInherit(); + printNullableOrNonnullAnnotation(out, column); + out.tab(1).println("public %s value%s() {", colType, i); out.tab(2).println("return %s();", colGetter); out.tab(1).println("}"); @@ -1488,13 +1492,14 @@ public class JavaGenerator extends AbstractGenerator { out.tab(1).println("}"); } else { + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); if (printDeprecationIfUnknownType(out, colTypeFull)) out.tab(1).override(); else out.tab(1).overrideInherit(); - out.tab(1).println("public %s value%s(%s value) {", className, i, varargsIfArray(colType)); + out.tab(1).println("public %s value%s([[before=@][after= ][%s]]%s value) {", className, i, list(nullableAnnotation), varargsIfArray(colType)); out.tab(2).println("%s(value);", colSetter); out.tab(2).println("return this;"); out.tab(1).println("}"); @@ -1513,7 +1518,9 @@ public class JavaGenerator extends AbstractGenerator { calls.add("this.value" + i + "(value" + i + ")"); } else { - arguments.add(colType + " value" + i); + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); + + arguments.add((nullableAnnotation == null ? "" : "@" + nullableAnnotation + " ") + colType + " value" + i); calls.add("value" + i + "(value" + i + ");"); } } @@ -1582,10 +1589,14 @@ public class JavaGenerator extends AbstractGenerator { final String columnMember = getStrategy().getJavaMemberName(column, Mode.DEFAULT); final String type = out.ref(getJavaType(column.getType(resolver()))); - if (scala) + if (scala) { arguments.add(columnMember + " : " + type); - else - arguments.add(type + " " + columnMember); + } + else { + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); + + arguments.add((nullableAnnotation == null ? "" : "@" + nullableAnnotation + " ") + type + " " + columnMember); + } } out.tab(1).javadoc("Create a detached, initialised %s", className); @@ -1675,8 +1686,10 @@ public class JavaGenerator extends AbstractGenerator { out.tab(1).println("}"); } else { + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); + out.tab(1).overrideIf(generateInterfaces() && !generateImmutableInterfaces() && !isUDT); - out.tab(1).println("public %s %s(%s value) {", setterReturnType, setter, varargsIfArray(type)); + out.tab(1).println("public %s %s([[before=@][after= ][%s]]%s value) {", setterReturnType, setter, list(nullableAnnotation), varargsIfArray(type)); out.tab(2).println("set(%s, value);", index); if (generateFluentSetters()) out.tab(2).println("return this;"); @@ -1708,7 +1721,9 @@ public class JavaGenerator extends AbstractGenerator { out.tab(1).println("}"); } else { - out.tab(1).println("public %s %s(%s value) {", setterReturnType, setter, varargsIfArray(columnTypeInterface)); + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); + + out.tab(1).println("public %s %s([[before=@][after= ][%s]]%s value) {", setterReturnType, setter, list(nullableAnnotation), varargsIfArray(columnTypeInterface)); out.tab(2).println("if (value == null)"); out.tab(3).println("set(%s, null);", index); @@ -1778,6 +1793,7 @@ public class JavaGenerator extends AbstractGenerator { if (column instanceof ColumnDefinition) printColumnJPAAnnotation(out, (ColumnDefinition) column); printValidationAnnotation(out, column); + printNullableOrNonnullAnnotation(out, column); if (scala) { out.tab(1).println("def %s : %s = {", getter, type); @@ -1980,10 +1996,14 @@ public class JavaGenerator extends AbstractGenerator { if (!printDeprecationIfUnknownType(out, typeFull)) out.tab(1).javadoc("Setter for %s.%s", name, columnComment(column, comment)); - if (scala) + if (scala) { out.tab(1).println("def %s(value : %s) : %s", setter, type, setterReturnType); - else - out.tab(1).println("public %s %s(%s value);", setterReturnType, setter, varargsIfArray(type)); + } + else { + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); + + out.tab(1).println("public %s %s([[before=@][after= ][%s]]%s value);", setterReturnType, setter, list(nullableAnnotation), varargsIfArray(type)); + } } /** @@ -2014,6 +2034,7 @@ public class JavaGenerator extends AbstractGenerator { printColumnJPAAnnotation(out, (ColumnDefinition) column); printValidationAnnotation(out, column); + printNullableOrNonnullAnnotation(out, column); if (scala) out.tab(1).println("def %s : %s", getter, type); @@ -3375,9 +3396,11 @@ public class JavaGenerator extends AbstractGenerator { String separator1 = ""; for (TypedElementDefinition column : getTypedElements(tableOrUDT)) { - out.println(separator1); + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); - out.tab(2).print("%s %s", + out.println(separator1); + out.tab(2).print("[[before=@][after= ][%s]]%s %s", + list(nullableAnnotation), StringUtils.rightPad(out.ref(getJavaType(column.getType(resolver(Mode.POJO)), Mode.POJO)), maxLength), getStrategy().getJavaMemberName(column, Mode.POJO)); separator1 = ","; @@ -3506,6 +3529,7 @@ public class JavaGenerator extends AbstractGenerator { printColumnJPAAnnotation(out, (ColumnDefinition) column); printValidationAnnotation(out, column); + printNullableOrNonnullAnnotation(out, column); if (scala) { out.tab(1).println("def %s : %s = {", columnGetter, columnType); @@ -3558,8 +3582,10 @@ public class JavaGenerator extends AbstractGenerator { out.tab(1).println("}"); } else { + final String nullableAnnotation = nullableOrNonnullAnnotation(out, column); + out.tab(1).overrideIf(generateInterfaces() && !generateImmutableInterfaces() && !isUDT); - out.tab(1).println("public %s %s(%s %s) {", columnSetterReturnType, columnSetter, varargsIfArray(columnType), columnMember); + out.tab(1).println("public %s %s([[before=@][after= ][%s]]%s %s) {", columnSetterReturnType, columnSetter, list(nullableAnnotation), varargsIfArray(columnType), columnMember); out.tab(2).println("this.%s = %s;", columnMember, columnMember); if (generateFluentSetters()) out.tab(2).println("return this;"); @@ -5253,6 +5279,31 @@ public class JavaGenerator extends AbstractGenerator { } } + private String nullableOrNonnullAnnotation(JavaWriter out, TypedElementDefinition column) { + return column.getType().isNullable() && generateNullableAnnotation() + ? out.ref(generatedNullableAnnotationType()) + : !column.getType().isNullable() && generateNonnullAnnotation() + ? out.ref(generatedNonnullAnnotationType()) + : null; + } + + private void printNullableOrNonnullAnnotation(JavaWriter out, TypedElementDefinition column) { + if (column.getType().isNullable()) + printNullableAnnotation(out); + else + printNonnullAnnotation(out); + } + + protected void printNullableAnnotation(JavaWriter out) { + if (generateNullableAnnotation()) + out.tab(1).println("@%s", out.ref(generatedNullableAnnotationType())); + } + + protected void printNonnullAnnotation(JavaWriter out) { + if (generateNonnullAnnotation()) + out.tab(1).println("@%s", out.ref(generatedNonnullAnnotationType())); + } + private boolean printDeprecationIfUnknownTypes(JavaWriter out, Collection params) { for (ParameterDefinition param : params) if (printDeprecationIfUnknownType(out, getJavaType(param.getType(resolver())))) diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/jaxb/Generate.java b/jOOQ-meta/src/main/java/org/jooq/meta/jaxb/Generate.java index 194cf219f4..49fc61be2c 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/jaxb/Generate.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/jaxb/Generate.java @@ -49,12 +49,16 @@ public class Generate implements Serializable, XMLAppendable @XmlElement(defaultValue = "DETECT_FROM_JDK") @XmlSchemaType(name = "string") protected GeneratedAnnotationType generatedAnnotationType = GeneratedAnnotationType.DETECT_FROM_JDK; + @XmlElement(defaultValue = "false") + protected Boolean nonnullAnnotation = false; @XmlElement(defaultValue = "javax.annotation.Nonnull") @XmlJavaTypeAdapter(StringAdapter.class) - protected String generatedNonnullAnnotationType = "javax.annotation.Nonnull"; + protected String nonnullAnnotationType = "javax.annotation.Nonnull"; + @XmlElement(defaultValue = "false") + protected Boolean nullableAnnotation = false; @XmlElement(defaultValue = "javax.annotation.Nullable") @XmlJavaTypeAdapter(StringAdapter.class) - protected String generatedNullableAnnotationType = "javax.annotation.Nullable"; + protected String nullableAnnotationType = "javax.annotation.Nullable"; @XmlElement(defaultValue = "true") protected Boolean routines = true; @XmlElement(defaultValue = "true") @@ -393,35 +397,83 @@ public class Generate implements Serializable, XMLAppendable } /** - * Specify the qualified annotation name for all non-nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nonnull} type. + * Whether non-nullable items should be annotated with the annotation type specified in {@link #nonnullAnnotationType}. In SQL and by consequence in jOOQ, non-nullability cannot be guaranteed statically. There may still be some cases (e.g. after unions, outer joins, etc.) where a normally non-null value turns out to be null! + * + * @return + * possible object is + * {@link Boolean } * */ - public String getGeneratedNonnullAnnotationType() { - return generatedNonnullAnnotationType; + public Boolean isNonnullAnnotation() { + return nonnullAnnotation; + } + + /** + * Sets the value of the nonnullAnnotation property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNonnullAnnotation(Boolean value) { + this.nonnullAnnotation = value; } /** * Specify the qualified annotation name for all non-nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nonnull} type. * */ - public void setGeneratedNonnullAnnotationType(String value) { - this.generatedNonnullAnnotationType = value; + public String getNonnullAnnotationType() { + return nonnullAnnotationType; + } + + /** + * Specify the qualified annotation name for all non-nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nonnull} type. + * + */ + public void setNonnullAnnotationType(String value) { + this.nonnullAnnotationType = value; + } + + /** + * Whether nullable items should be annotated with the annotation type specified in {@link #nullableAnnotationType}. Unlike {@link #nonnullAnnotation}, nullability can be guaranteed as in SQL, and by consequence in jOOQ, every column expression can be made nullable using some SQL operation. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNullableAnnotation() { + return nullableAnnotation; + } + + /** + * Sets the value of the nullableAnnotation property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNullableAnnotation(Boolean value) { + this.nullableAnnotation = value; } /** * Specify the qualified annotation name for all nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nullable} type. * */ - public String getGeneratedNullableAnnotationType() { - return generatedNullableAnnotationType; + public String getNullableAnnotationType() { + return nullableAnnotationType; } /** * Specify the qualified annotation name for all nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nullable} type. * */ - public void setGeneratedNullableAnnotationType(String value) { - this.generatedNullableAnnotationType = value; + public void setNullableAnnotationType(String value) { + this.nullableAnnotationType = value; } /** @@ -1888,12 +1940,22 @@ public class Generate implements Serializable, XMLAppendable return this; } + public Generate withNonnullAnnotation(Boolean value) { + setNonnullAnnotation(value); + return this; + } + /** * Specify the qualified annotation name for all non-nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nonnull} type. * */ - public Generate withGeneratedNonnullAnnotationType(String value) { - setGeneratedNonnullAnnotationType(value); + public Generate withNonnullAnnotationType(String value) { + setNonnullAnnotationType(value); + return this; + } + + public Generate withNullableAnnotation(Boolean value) { + setNullableAnnotation(value); return this; } @@ -1901,8 +1963,8 @@ public class Generate implements Serializable, XMLAppendable * Specify the qualified annotation name for all nullable items in generated code, defaulting to the JSR-305 {@link javax.annotation.Nullable} type. * */ - public Generate withGeneratedNullableAnnotationType(String value) { - setGeneratedNullableAnnotationType(value); + public Generate withNullableAnnotationType(String value) { + setNullableAnnotationType(value); return this; } @@ -2232,8 +2294,10 @@ public class Generate implements Serializable, XMLAppendable builder.append("instanceFields", instanceFields); builder.append("generatedAnnotation", generatedAnnotation); builder.append("generatedAnnotationType", generatedAnnotationType); - builder.append("generatedNonnullAnnotationType", generatedNonnullAnnotationType); - builder.append("generatedNullableAnnotationType", generatedNullableAnnotationType); + builder.append("nonnullAnnotation", nonnullAnnotation); + builder.append("nonnullAnnotationType", nonnullAnnotationType); + builder.append("nullableAnnotation", nullableAnnotation); + builder.append("nullableAnnotationType", nullableAnnotationType); builder.append("routines", routines); builder.append("sequences", sequences); builder.append("udts", udts); @@ -2395,21 +2459,39 @@ public class Generate implements Serializable, XMLAppendable return false; } } - if (generatedNonnullAnnotationType == null) { - if (other.generatedNonnullAnnotationType!= null) { + if (nonnullAnnotation == null) { + if (other.nonnullAnnotation!= null) { return false; } } else { - if (!generatedNonnullAnnotationType.equals(other.generatedNonnullAnnotationType)) { + if (!nonnullAnnotation.equals(other.nonnullAnnotation)) { return false; } } - if (generatedNullableAnnotationType == null) { - if (other.generatedNullableAnnotationType!= null) { + if (nonnullAnnotationType == null) { + if (other.nonnullAnnotationType!= null) { return false; } } else { - if (!generatedNullableAnnotationType.equals(other.generatedNullableAnnotationType)) { + if (!nonnullAnnotationType.equals(other.nonnullAnnotationType)) { + return false; + } + } + if (nullableAnnotation == null) { + if (other.nullableAnnotation!= null) { + return false; + } + } else { + if (!nullableAnnotation.equals(other.nullableAnnotation)) { + return false; + } + } + if (nullableAnnotationType == null) { + if (other.nullableAnnotationType!= null) { + return false; + } + } else { + if (!nullableAnnotationType.equals(other.nullableAnnotationType)) { return false; } } @@ -2960,8 +3042,10 @@ public class Generate implements Serializable, XMLAppendable result = ((prime*result)+((instanceFields == null)? 0 :instanceFields.hashCode())); result = ((prime*result)+((generatedAnnotation == null)? 0 :generatedAnnotation.hashCode())); result = ((prime*result)+((generatedAnnotationType == null)? 0 :generatedAnnotationType.hashCode())); - result = ((prime*result)+((generatedNonnullAnnotationType == null)? 0 :generatedNonnullAnnotationType.hashCode())); - result = ((prime*result)+((generatedNullableAnnotationType == null)? 0 :generatedNullableAnnotationType.hashCode())); + result = ((prime*result)+((nonnullAnnotation == null)? 0 :nonnullAnnotation.hashCode())); + result = ((prime*result)+((nonnullAnnotationType == null)? 0 :nonnullAnnotationType.hashCode())); + result = ((prime*result)+((nullableAnnotation == null)? 0 :nullableAnnotation.hashCode())); + result = ((prime*result)+((nullableAnnotationType == null)? 0 :nullableAnnotationType.hashCode())); result = ((prime*result)+((routines == null)? 0 :routines.hashCode())); result = ((prime*result)+((sequences == null)? 0 :sequences.hashCode())); result = ((prime*result)+((udts == null)? 0 :udts.hashCode())); diff --git a/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.13.0.xsd b/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.13.0.xsd index aaa6630b0d..b0730f3cfe 100644 --- a/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.13.0.xsd +++ b/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.13.0.xsd @@ -1140,11 +1140,19 @@ jOOQ version used for source code.]]> - + + + + + - + + + + + diff --git a/pom.xml b/pom.xml index e11d68058c..62eff326c2 100644 --- a/pom.xml +++ b/pom.xml @@ -517,6 +517,8 @@ + +