[#4846] Add SPI to allow for injecting meta data ordering

This commit is contained in:
lukaseder 2017-05-30 12:16:57 +02:00
parent 47c2549909
commit 100b2dee35
8 changed files with 190 additions and 8 deletions

View File

@ -52,6 +52,7 @@ import java.io.StringWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.List;
import java.util.Properties;
@ -478,6 +479,15 @@ public class GenerationTool {
database.setSchemaVersionProvider(svp);
database.setCatalogVersionProvider(cvp);
if (!StringUtils.isBlank(d.getOrderProvider())) {
Class<?> orderProvider = Class.forName(d.getOrderProvider());
if (Comparator.class.isAssignableFrom(orderProvider))
database.setOrderProvider((Comparator<Definition>) orderProvider.newInstance());
else
log.warn("Order provider must be of type java.util.Comparator: " + orderProvider);
}
if (d.getEnumTypes().size() > 0)
log.warn("DEPRECATED", "The configuration property /configuration/generator/database/enumTypes is experimental and deprecated and will be removed in the future.");
if (Boolean.TRUE.equals(d.isDateAsTimestamp()))

View File

@ -15710,6 +15710,94 @@ configuration {
</html></content>
</section>
<section id="codegen-config-order-provider">
<title>Custom ordering of generated code</title>
<content><html>
<p>
By default, the jOOQ code generator maintains the following ordering of objects:
</p>
<ul>
<li>Catalogs, schemas, tables, user-defined types, packages, routines, sequences, constraints are ordered alphabetically</li>
<li>Table columns, user-defined type attributes, routine parameters are ordered in their order of definition</li>
</ul>
<p>
Sometimes, it may be desireable to override this default ordering to a custom ordering. In particular, the default ordering may be case-sensitive, when case-insensitive ordering is really more desireable at times. Users may define an order provider by specifying a fully qualified class on the code generator's class path, which must implement <reference class="java.util.Comparator" title="java.util.Comparator&lt;org.jooq.util.Definition&gt;"/> as follows:
</p>
<p>
<strong>XML configuration (standalone and Maven)</strong>
</p>
</html><xml><![CDATA[<configuration>
<generator>
<database>
<orderProvider>com.example.CaseInsensitiveOrderProvider</orderProvider>
</database>
</generator>
</configuration>]]></xml><html>
<p>
<strong>Programmatic configuration</strong>
</p>
</html><java><![CDATA[configuration
.withGenerator(new Generator(
.withDatabase(new Database()
.withOrderProvider("com.example.CaseInsensitiveOrderProvider");]]></java><html>
<p>
<strong>Gradle configuration</strong>
</p>
</html><java><![CDATA[configuration {
generator {
database {
orderProvider = 'com.example.CaseInsensitiveOrderProvider'
}
}
}]]></java><html>
<p>
This order provider may then look as follows:
</p>
</html><java><![CDATA[package com.example;
import java.util.Comparator;
import org.jooq.util.Definition;
public class CaseInsensitiveOrderProvider implements Comparator<Definition> {
@Override
public int compare(Definition o1, Definition o2) {
return o1.getQualifiedInputName().compareToIgnoreCase(o2.getQualifiedInputName());
}
}]]></java><html>
<p>
While changing the order of "top level types" (like tables) is irrelevant to the jOOQ runtime, there may be some side-effects to changing the order of table columns, user-defined type attributes, routine parameters, as the database might expect the exact same order as is defined in the database. In order to only change the ordering for tables, the following order provider can be implemented instead:
</p>
</html><java><![CDATA[package com.example;
import java.util.Comparator;
import org.jooq.util.Definition;
import org.jooq.util.TableDefinition;
public class CaseInsensitiveOrderProvider implements Comparator<Definition> {
@Override
public int compare(Definition o1, Definition o2) {
if (o1 instanceof TableDefinition && o2 instanceof TableDefinition)
return o1.getQualifiedInputName().compareToIgnoreCase(o2.getQualifiedInputName());
else
return 0; // Retain input ordering
}
}]]></java><html>
</html></content>
</section>
<section id="codegen-config-forced-types">
<title>Forced types</title>
<content><html>

View File

@ -77,12 +77,18 @@
<bindings if-exists="true" scd="~tns:CustomTypes">
<class ref="org.jooq.util.jaxb.CustomTypes"/>
</bindings>
<bindings if-exists="true" scd="~tns:EnumTypes">
<class ref="org.jooq.util.jaxb.EnumTypes"/>
</bindings>
<bindings if-exists="true" scd="~tns:ForcedTypes">
<class ref="org.jooq.util.jaxb.ForcedTypes"/>
</bindings>
<bindings if-exists="true" scd="~tns:CustomType">
<class ref="org.jooq.util.jaxb.CustomType"/>
</bindings>
<bindings if-exists="true" scd="~tns:EnumType">
<class ref="org.jooq.util.jaxb.EnumType"/>
</bindings>
<bindings if-exists="true" scd="~tns:ForcedType">
<class ref="org.jooq.util.jaxb.ForcedType"/>
</bindings>

View File

@ -46,6 +46,7 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -128,6 +129,7 @@ public abstract class AbstractDatabase implements Database {
private List<ForcedType> configuredForcedTypes;
private SchemaVersionProvider schemaVersionProvider;
private CatalogVersionProvider catalogVersionProvider;
private Comparator<Definition> orderProvider;
// -------------------------------------------------------------------------
// Loaded definitions
@ -998,6 +1000,16 @@ public abstract class AbstractDatabase implements Database {
this.catalogVersionProvider = catalogVersionProvider;
}
@Override
public final Comparator<Definition> getOrderProvider() {
return orderProvider;
}
@Override
public final void setOrderProvider(Comparator<Definition> provider) {
this.orderProvider = provider;
}
@Override
public final void setSupportsUnsignedTypes(boolean supportsUnsignedTypes) {
this.supportsUnsignedTypes = supportsUnsignedTypes;
@ -1057,7 +1069,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<SequenceDefinition> s = getSequences0();
sequences = filterExcludeInclude(s);
sequences = sort(filterExcludeInclude(s));
log.info("Sequences fetched", fetchedSize(s, sequences));
}
catch (Exception e) {
@ -1159,7 +1171,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<TableDefinition> t = getTables0();
tables = filterExcludeInclude(t);
tables = sort(filterExcludeInclude(t));
log.info("Tables fetched", fetchedSize(t, tables));
}
catch (Exception e) {
@ -1204,7 +1216,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<EnumDefinition> e = getEnums0();
enums = filterExcludeInclude(e);
enums = sort(filterExcludeInclude(e));
enums.addAll(getConfiguredEnums());
log.info("Enums fetched", fetchedSize(e, enums));
@ -1310,7 +1322,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<DomainDefinition> e = getDomains0();
domains = filterExcludeInclude(e);
domains = sort(filterExcludeInclude(e));
log.info("Domains fetched", fetchedSize(e, domains));
}
catch (Exception e) {
@ -1350,7 +1362,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<ArrayDefinition> a = getArrays0();
arrays = filterExcludeInclude(a);
arrays = sort(filterExcludeInclude(a));
log.info("ARRAYs fetched", fetchedSize(a, arrays));
}
catch (Exception e) {
@ -1395,7 +1407,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<UDTDefinition> u = getUDTs0();
udts = filterExcludeInclude(u);
udts = sort(filterExcludeInclude(u));
log.info("UDTs fetched", fetchedSize(u, udts));
}
catch (Exception e) {
@ -1481,7 +1493,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<RoutineDefinition> r = getRoutines0();
routines = filterExcludeInclude(r);
routines = sort(filterExcludeInclude(r));
log.info("Routines fetched", fetchedSize(r, routines));
}
catch (Exception e) {
@ -1507,7 +1519,7 @@ public abstract class AbstractDatabase implements Database {
try {
List<PackageDefinition> p = getPackages0();
packages = filterExcludeInclude(p);
packages = sort(filterExcludeInclude(p));
log.info("Packages fetched", fetchedSize(p, packages));
}
catch (Exception e) {
@ -1595,6 +1607,14 @@ public abstract class AbstractDatabase implements Database {
return result;
}
@Override
public final <T extends Definition> List<T> sort(List<T> definitions) {
if (orderProvider != null)
Collections.sort(definitions, orderProvider);
return definitions;
}
@Override
public final List<Definition> getIncluded() {
return Collections.unmodifiableList(included);

View File

@ -127,6 +127,8 @@ extends AbstractDefinition {
else {
elements = e;
}
db.sort(elements);
}
catch (Exception e) {
log.error("Error while initialising type", e);

View File

@ -36,6 +36,7 @@
package org.jooq.util;
import java.sql.Connection;
import java.util.Comparator;
import java.util.List;
import java.util.Properties;
@ -463,6 +464,11 @@ public interface Database {
*/
<D extends Definition> List<D> filterExcludeInclude(List<D> definitions);
/**
* Sort a list of definitions according to the {@link #getOrderProvider()} defined in this database.
*/
<D extends Definition> List<D> sort(List<D> definitions);
/**
* Retrieve all included objects.
*/
@ -612,6 +618,16 @@ public interface Database {
*/
void setCatalogVersionProvider(CatalogVersionProvider provider);
/**
* The database's order provider.
*/
Comparator<Definition> getOrderProvider();
/**
* The database's order provider.
*/
void setOrderProvider(Comparator<Definition> provider);
/**
* Database objects matching any of these field names will be generated as
* forced types.

View File

@ -111,6 +111,9 @@ public class Database implements Serializable
@XmlElement(defaultValue = "")
@XmlJavaTypeAdapter(StringAdapter.class)
protected String catalogVersionProvider = "";
@XmlElement(defaultValue = "")
@XmlJavaTypeAdapter(StringAdapter.class)
protected String orderProvider = "";
protected Boolean tableValuedFunctions;
@XmlElementWrapper(name = "properties")
@XmlElement(name = "property")
@ -973,6 +976,32 @@ public class Database implements Serializable
this.catalogVersionProvider = value;
}
/**
* A custom {@link java.util.Comparator} that can compare two {@link org.jooq.util.Definition} objects to determine their order.
* <p>
* This comparator can be used to influence the order of any object that is produced by jOOQ meta, and thus, indirectly, the order of declared objects in generated code.
*
* @return
* possible object is
* {@link String }
*
*/
public String getOrderProvider() {
return orderProvider;
}
/**
* Sets the value of the orderProvider property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setOrderProvider(String value) {
this.orderProvider = value;
}
/**
* Whether table valued functions should be reported as tables.
* <p>
@ -1227,6 +1256,11 @@ public class Database implements Serializable
return this;
}
public Database withOrderProvider(String value) {
setOrderProvider(value);
return this;
}
public Database withTableValuedFunctions(Boolean value) {
setTableValuedFunctions(value);
return this;

View File

@ -634,6 +634,12 @@ Catalog versions will be generated into the {@link javax.annotation.Generated} a
generated artefacts.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="orderProvider" type="string" default="" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A custom {@link java.util.Comparator} that can compare two {@link org.jooq.util.Definition} objects to determine their order.
<p>
This comparator can be used to influence the order of any object that is produced by jOOQ meta, and thus, indirectly, the order of declared objects in generated code.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="customTypes" type="tns:CustomTypes" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[@deprecated Use {@link #getForcedTypes()} only]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>