[jOOQ/jOOQ#10756] Generate @Transactional on DAOImpl and generated DAOs

query-methods
This commit is contained in:
Lukas Eder 2022-03-11 11:04:14 +01:00
parent cbb7a7681c
commit 69f4fa3066
6 changed files with 358 additions and 1 deletions

View File

@ -112,6 +112,7 @@ abstract class AbstractGenerator implements Generator {
String generateJPAVersion = "";
boolean generateValidationAnnotations = false;
boolean generateSpringAnnotations = false;
boolean generateSpringDao = false;
boolean generateKotlinSetterJvmNameAnnotationsOnIsPrefix = true;
GeneratedSerialVersionUID generatedSerialVersionUID = GeneratedSerialVersionUID.CONSTANT;
int maxMembersPerInitialiser = 500;
@ -733,6 +734,16 @@ abstract class AbstractGenerator implements Generator {
this.generateSpringAnnotations = generateSpringAnnotations;
}
@Override
public boolean generateSpringDao() {
return generateSpringDao;
}
@Override
public void setGenerateSpringDao(boolean generateSpringDao) {
this.generateSpringDao = generateSpringDao;
}
@Override
public boolean generateKotlinSetterJvmNameAnnotationsOnIsPrefix() {
return generateKotlinSetterJvmNameAnnotationsOnIsPrefix;

View File

@ -791,6 +791,8 @@ public class GenerationTool {
generator.setGenerateValidationAnnotations(g.getGenerate().isValidationAnnotations());
if (g.getGenerate().isSpringAnnotations() != null)
generator.setGenerateSpringAnnotations(g.getGenerate().isSpringAnnotations());
if (g.getGenerate().isSpringDao() != null)
generator.setGenerateSpringDao(g.getGenerate().isSpringDao());
if (g.getGenerate().isKotlinSetterJvmNameAnnotationsOnIsPrefix() != null)
generator.setGenerateKotlinSetterJvmNameAnnotationsOnIsPrefix(g.getGenerate().isKotlinSetterJvmNameAnnotationsOnIsPrefix());
if (g.getGenerate().getGeneratedSerialVersionUID() != null)

View File

@ -46,6 +46,7 @@ import org.jooq.JSON;
import org.jooq.JSONB;
import org.jooq.Spatial;
import org.jooq.XML;
import org.jooq.impl.DAOImpl;
import org.jooq.meta.Database;
import org.jooq.meta.jaxb.GeneratedAnnotationType;
import org.jooq.meta.jaxb.GeneratedSerialVersionUID;
@ -587,6 +588,22 @@ public interface Generator {
*/
void setGenerateSpringAnnotations(boolean generateSpringAnnotations);
/**
* Whether a Spring specific {@link DAOImpl} subclass should be generated,
* which may contain Spring specific stuff, such as the
* <code>@Transactional</code> annotation (if
* {@link #generateSpringAnnotations()} is set).
*/
boolean generateSpringDao();
/**
* Whether a Spring specific {@link DAOImpl} subclass should be generated,
* which may contain Spring specific stuff, such as the
* <code>@Transactional</code> annotation (if
* {@link #generateSpringAnnotations()} is set).
*/
void setGenerateSpringDao(boolean generateSpringDao);
/**
* Whether kotlin mutable properties should be annotated with
* <code>set:JvmName</code> as a workaround for problems occurring when

View File

@ -87,6 +87,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -129,6 +130,7 @@ import org.jooq.TableOptions;
import org.jooq.UDT;
import org.jooq.UDTField;
import org.jooq.UniqueKey;
import org.jooq.UpdatableRecord;
import org.jooq.codegen.GeneratorStrategy.Mode;
import org.jooq.codegen.GeneratorWriter.CloseResult;
import org.jooq.exception.SQLDialectNotSupportedException;
@ -571,6 +573,9 @@ public class JavaGenerator extends AbstractGenerator {
generateCatalog(catalog);
if (generateSpringDao() && catalog.getSchemata().stream().anyMatch(s -> !s.getTables().isEmpty()))
generateSpringDao(catalog);
log.info("Generating schemata", "Total: " + catalog.getSchemata().size());
for (SchemaDefinition schema : catalog.getSchemata()) {
try {
@ -4150,6 +4155,280 @@ public class JavaGenerator extends AbstractGenerator {
watch.splitInfo("Table DAOs generated");
}
protected void generateSpringDao(CatalogDefinition catalog) {
// [#10756] Optionally, this class could be implemented in a new jooq-spring-extensions module
JavaWriter out = newJavaWriter(new File(getStrategy().getFile(catalog).getParentFile(), "AbstractSpringDAOImpl.java"));
log.info("Generating AbstractSpringDAOImpl", out.file().getName());
generateSpringDao(catalog, out);
closeJavaWriter(out);
}
protected void generateSpringDao(CatalogDefinition catalog, JavaWriter out) {
printPackage(out, catalog);
printClassJavadoc(out, "Spring specific {@" + out.ref(DAOImpl.class) + "} override.");
printClassAnnotations(out, catalog, Mode.DEFAULT);
String transactional = generateSpringAnnotations()
? out.ref("org.springframework.transaction.annotation.Transactional")
: null;
String className = "AbstractSpringDAOImpl";
if (scala) {
if (generateSpringAnnotations())
out.println("@%s(readOnly = true)", transactional);
out.println("%sabstract class %s[R <: %s[R], P, T](table: %s[R], klass: java.lang.Class[P], configuration: %s) extends %s[R, P, T](table, klass, configuration) {",
visibility(), className, UpdatableRecord.class, Table.class, Configuration.class, DAOImpl.class);
out.println();
out.println("%sdef this(table: %s[R], klass: java.lang.Class[P]) = this(table, klass, null)", visibility(), Table.class);
}
else if (kotlin) {
if (generateSpringAnnotations())
out.println("@%s(readOnly = true)", transactional);
out.println("%sabstract class %s<R : %s<R>, P, T>(table: %s<R>, type: %s<P>, configuration: %s?) : %s<R, P, T>(table, type, configuration) {",
visibility(), className, UpdatableRecord.class, Table.class, Class.class, Configuration.class, DAOImpl.class);
out.println();
out.println("%sconstructor(table: %s<R>, type: %s<P>) : this(table, type, null)", visibility(), Table.class, Class.class);
}
else {
if (generateSpringAnnotations())
out.println("@%s(readOnly = true)", transactional);
out.println("%sabstract class %s<R extends %s<R>, P, T> extends %s<R, P, T> {", visibility(), className, UpdatableRecord.class, DAOImpl.class);
out.println();
out.println("protected %s(%s<R> table, %s<P> type) {", className, Table.class, Class.class);
out.println("super(table, type);");
out.println("}");
out.println();
out.println("protected %s(%s<R> table, %s<P> type, %s configuration) {", className, Table.class, Class.class, Configuration.class);
out.println("super(table, type, configuration);");
out.println("}");
}
Consumer<Boolean> printTransactionalHeader = readOnly -> {
out.println();
if (readOnly)
out.println("@%s(readOnly = true)", transactional);
else
out.println("@%s", transactional);
};
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def count(): Long = super.count()");
}
else if (kotlin) {
out.println("public override fun count(): Long = super.count()");
}
else {
out.override();
out.println("public long count() {");
out.println("return super.count();");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def exists(obj: P): Boolean = super.exists(obj)");
}
else if (kotlin) {
out.println("public override fun exists(obj: P): Boolean = super.exists(obj)");
}
else {
out.override();
out.println("public boolean exists(P object) {");
out.println("return super.exists(object);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def existsById(id: T): Boolean = super.existsById(id)");
}
else if (kotlin) {
out.println("public override fun existsById(id: T): Boolean = super.existsById(id)");
}
else {
out.override();
out.println("public boolean existsById(T id) {");
out.println("return super.existsById(id);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def fetch[Z](field: %s[Z], values: %s[_ <: Z]): %s[P] = super.fetch(field, values)", Field.class, Collection.class, List.class);
}
else if (kotlin) {
out.println("public override fun <Z> fetch(field: %s<Z>, values: %s<Z>): %s<P> = super.fetch(field, values)", Field.class, out.ref("kotlin.collections.Collection"), out.ref("kotlin.collections.List"));
}
else {
out.override();
out.println("public <Z> %s<P> fetch(%s<Z> field, %s<? extends Z> values) {", List.class, Field.class, Collection.class);
out.println("return super.fetch(field, values);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def fetch[Z](field: %s[Z], values: Z*): %s[P] = super.fetch(field, values:_*)", Field.class, List.class);
}
else if (kotlin) {
out.println("public override fun <Z> fetch(field: %s<Z>, vararg values: Z): %s<P> = super.fetch(field, *values)", Field.class, out.ref("kotlin.collections.List"));
}
else {
out.override();
out.println("public <Z> %s<P> fetch(%s<Z> field, Z... values) {", List.class, Field.class);
out.println("return super.fetch(field, values);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def fetchOne[Z](field: %s[Z], value: Z): P = super.fetchOne(field, value)", Field.class);
}
else if (kotlin) {
out.println("public override fun <Z> fetchOne(field: %s<Z>, value: Z): P? = super.fetchOne(field, value)", Field.class);
}
else {
out.override();
out.println("public <Z> P fetchOne(%s<Z> field, Z value) {", Field.class);
out.println("return super.fetchOne(field, value);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def fetchOptional[Z](field: %s[Z], value: Z): %s[P] = super.fetchOptional(field, value)", Field.class, Optional.class);
}
else if (kotlin) {
out.println("public override fun <Z> fetchOptional(field: %s<Z>, value: Z): %s<P> = super.fetchOptional(field, value)", Field.class, Optional.class);
}
else {
out.override();
out.println("public <Z> %s<P> fetchOptional(%s<Z> field, Z value) {", Optional.class, Field.class);
out.println("return super.fetchOptional(field, value);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def fetchRange[Z](field: %s[Z], lowerInclusive: Z, upperInclusive: Z): %s[P] = super.fetchRange(field, lowerInclusive, upperInclusive)", Field.class, List.class);
}
else if (kotlin) {
out.println("public override fun <Z> fetchRange(field: %s<Z>, lowerInclusive: Z, upperInclusive: Z): %s<P> = super.fetchRange(field, lowerInclusive, upperInclusive)", Field.class, out.ref("kotlin.collections.List"));
}
else {
out.override();
out.println("public <Z> %s<P> fetchRange(%s<Z> field, Z lowerInclusive, Z upperInclusive) {", List.class, Field.class);
out.println("return super.fetchRange(field, lowerInclusive, upperInclusive);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def findAll(): %s[P] = super.findAll()", List.class);
}
else if (kotlin) {
out.println("public override fun findAll(): %s<P> = super.findAll()", out.ref("kotlin.collections.List"));
}
else {
out.override();
out.println("public %s<P> findAll() {", List.class);
out.println("return super.findAll();");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def findById(id: T): P = super.findById(id)");
}
else if (kotlin) {
out.println("public override fun findById(id: T): P? = super.findById(id)");
}
else {
out.override();
out.println("public P findById(T id) {");
out.println("return super.findById(id);");
out.println("}");
}
printTransactionalHeader.accept(true);
if (scala) {
out.println("override def findOptionalById(id: T): %s[P] = super.findOptionalById(id)", Optional.class);
}
else if (kotlin) {
out.println("public override fun findOptionalById(id: T): %s<P> = super.findOptionalById(id)", Optional.class);
}
else {
out.override();
out.println("public %s<P> findOptionalById(T id) {", Optional.class);
out.println("return super.findOptionalById(id);");
out.println("}");
}
for (String name : asList("insert", "update", "merge", "delete", "deleteById")) {
String argType = name.endsWith("ById") ? "T" : "P";
String argName = name.endsWith("ById") ? "id" : "obj";
printTransactionalHeader.accept(false);
if (scala) {
out.println("override def %s(%s: %s): Unit = super.%s(%s)", name, argName, argType, name, argName);
}
else if (kotlin) {
out.println("public override fun %s(%s: %s): Unit = super.%s(%s)", name, argName, argType, name, argName);
}
else {
out.override();
out.println("public void %s(%s<%s> %ss) {", name, Collection.class, argType, argName);
out.println("super.%s(%ss);", name, argName);
out.println("}");
}
printTransactionalHeader.accept(false);
if (scala) {
out.println("override def %s(%ss: %s*): Unit = super.%s(%ss:_*)", name, argName, argType, name, argName);
}
else if (kotlin) {
out.println("public override fun %s(vararg %ss: %s): Unit = super.%s(*%ss)", name, argName, argType, name, argName);
}
else {
out.override();
out.println("public void %s(%s %s) {", name, argType, argName);
out.println("super.%s(%s);", name, argName);
out.println("}");
}
printTransactionalHeader.accept(false);
if (scala) {
out.println("override def %s(%ss: %s[%s]): Unit = super.%s(%ss)", name, argName, Collection.class, argType, name, argName);
}
else if (kotlin) {
out.println("public override fun %s(%ss: %s<%s>): Unit = super.%s(%ss)", name, argName, out.ref("kotlin.collections.Collection"), argType, name, argName);
}
else {
out.override();
out.println("public void %s(%s... %ss) {", name, argType, argName);
out.println("super.%s(%ss);", name, argName);
out.println("}");
}
}
generateSpringDaoClassFooter(catalog, out);
out.println("}");
}
/**
* Subclasses may override this method to provide table references class footer code.
*/
@SuppressWarnings("unused")
protected void generateSpringDaoClassFooter(CatalogDefinition catalog, JavaWriter out) {}
protected void generateDao(TableDefinition table) {
JavaWriter out = newJavaWriter(getFile(table, Mode.DAO));
log.info("Generating DAO", out.file().getName());
@ -4167,7 +4446,9 @@ public class JavaGenerator extends AbstractGenerator {
final String className = getStrategy().getJavaClassName(table, Mode.DAO);
final List<String> interfaces = out.ref(getStrategy().getJavaClassImplements(table, Mode.DAO));
final String tableRecord = out.ref(getStrategy().getFullJavaClassName(table, Mode.RECORD));
final String daoImpl = out.ref(DAOImpl.class);
final String daoImpl = generateSpringDao()
? out.ref(getStrategy().getJavaPackageName(table.getSchema(), Mode.DAO) + ".AbstractSpringDAOImpl")
: out.ref(DAOImpl.class);
final String tableIdentifier = out.ref(getStrategy().getFullJavaIdentifier(table), 2);
String tType = (scala || kotlin ? "Unit" : "Void");

View File

@ -130,6 +130,8 @@ public class Generate implements Serializable, XMLAppendable
protected Boolean validationAnnotations = false;
@XmlElement(defaultValue = "false")
protected Boolean springAnnotations = false;
@XmlElement(defaultValue = "false")
protected Boolean springDao = false;
@XmlElement(defaultValue = "true")
protected Boolean kotlinSetterJvmNameAnnotationsOnIsPrefix = true;
@XmlElement(defaultValue = "true")
@ -1380,6 +1382,30 @@ public class Generate implements Serializable, XMLAppendable
this.springAnnotations = value;
}
/**
* Generate an AbstractSpringDAOImpl as a base class for other DAO classes, containing @Transactional annotations, etc.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isSpringDao() {
return springDao;
}
/**
* Sets the value of the springDao property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setSpringDao(Boolean value) {
this.springDao = value;
}
/**
* Workaround for Kotlin generating <code>setX()</code> setters instead of <code>setIsX()</code> in byte code for mutable properties called <code>isX</code>.
*
@ -2756,6 +2782,11 @@ public class Generate implements Serializable, XMLAppendable
return this;
}
public Generate withSpringDao(Boolean value) {
setSpringDao(value);
return this;
}
public Generate withKotlinSetterJvmNameAnnotationsOnIsPrefix(Boolean value) {
setKotlinSetterJvmNameAnnotationsOnIsPrefix(value);
return this;
@ -3070,6 +3101,7 @@ public class Generate implements Serializable, XMLAppendable
builder.append("jpaVersion", jpaVersion);
builder.append("validationAnnotations", validationAnnotations);
builder.append("springAnnotations", springAnnotations);
builder.append("springDao", springDao);
builder.append("kotlinSetterJvmNameAnnotationsOnIsPrefix", kotlinSetterJvmNameAnnotationsOnIsPrefix);
builder.append("globalObjectReferences", globalObjectReferences);
builder.append("globalCatalogReferences", globalCatalogReferences);
@ -3579,6 +3611,15 @@ public class Generate implements Serializable, XMLAppendable
return false;
}
}
if (springDao == null) {
if (other.springDao!= null) {
return false;
}
} else {
if (!springDao.equals(other.springDao)) {
return false;
}
}
if (kotlinSetterJvmNameAnnotationsOnIsPrefix == null) {
if (other.kotlinSetterJvmNameAnnotationsOnIsPrefix!= null) {
return false;
@ -4058,6 +4099,7 @@ public class Generate implements Serializable, XMLAppendable
result = ((prime*result)+((jpaVersion == null)? 0 :jpaVersion.hashCode()));
result = ((prime*result)+((validationAnnotations == null)? 0 :validationAnnotations.hashCode()));
result = ((prime*result)+((springAnnotations == null)? 0 :springAnnotations.hashCode()));
result = ((prime*result)+((springDao == null)? 0 :springDao.hashCode()));
result = ((prime*result)+((kotlinSetterJvmNameAnnotationsOnIsPrefix == null)? 0 :kotlinSetterJvmNameAnnotationsOnIsPrefix.hashCode()));
result = ((prime*result)+((globalObjectReferences == null)? 0 :globalObjectReferences.hashCode()));
result = ((prime*result)+((globalCatalogReferences == null)? 0 :globalCatalogReferences.hashCode()));

View File

@ -1793,6 +1793,10 @@ jOOQ version used for source code.]]></jxb:javadoc></jxb:property></appinfo></an
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Annotate DAOs with useful spring annotations such as @Repository or @Autowired.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="springDao" type="boolean" default="false" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Generate an AbstractSpringDAOImpl as a base class for other DAO classes, containing @Transactional annotations, etc.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="kotlinSetterJvmNameAnnotationsOnIsPrefix" type="boolean" default="true" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Workaround for Kotlin generating <code>setX()</code> setters instead of <code>setIsX()</code> in byte code for mutable properties called <code>isX</code>.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>