[jOOQ/jOOQ#11838] Add Settings.namePathSeparator

This commit includes some work on:

- [jOOQ/jOOQ#2620] Add BigQuery support (WIP)
This commit is contained in:
Lukas Eder 2021-05-04 16:19:29 +02:00
parent 8ddc36485e
commit 6fa32f50dd
9 changed files with 106 additions and 21 deletions

View File

@ -95,6 +95,8 @@ public class Settings
protected Boolean renderOutputForSQLServerReturningClause = true;
@XmlElement(defaultValue = "false")
protected Boolean renderParenthesisAroundSetOperationQueries = false;
@XmlElement(defaultValue = ".")
protected String namePathSeparator = ".";
@XmlElement(defaultValue = "false")
protected Boolean bindOffsetDateTimeType = false;
@XmlElement(defaultValue = "false")
@ -867,6 +869,44 @@ public class Settings
this.renderParenthesisAroundSetOperationQueries = value;
}
/**
* The character(s) to be used as a separator in paths encoded in a {@link Name}
* <p>
* A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as:
* <p>
* <code><pre>
* SELECT
* a.first_name AS "book.author.firstName"
* a.last_name AS "book.author.lastName"
* FROM ...
* </pre></code>
* <p>
* Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__".
*
*/
public String getNamePathSeparator() {
return namePathSeparator;
}
/**
* The character(s) to be used as a separator in paths encoded in a {@link Name}
* <p>
* A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as:
* <p>
* <code><pre>
* SELECT
* a.first_name AS "book.author.firstName"
* a.last_name AS "book.author.lastName"
* FROM ...
* </pre></code>
* <p>
* Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__".
*
*/
public void setNamePathSeparator(String value) {
this.namePathSeparator = value;
}
/**
* Whether the <code>java.time</code> (JSR 310) type {@link java.time.OffsetDateTime} should be bound natively to JDBC.
* <p>
@ -2987,6 +3027,26 @@ public class Settings
return this;
}
/**
* The character(s) to be used as a separator in paths encoded in a {@link Name}
* <p>
* A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as:
* <p>
* <code><pre>
* SELECT
* a.first_name AS "book.author.firstName"
* a.last_name AS "book.author.lastName"
* FROM ...
* </pre></code>
* <p>
* Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__".
*
*/
public Settings withNamePathSeparator(String value) {
setNamePathSeparator(value);
return this;
}
public Settings withBindOffsetDateTimeType(Boolean value) {
setBindOffsetDateTimeType(value);
return this;
@ -3752,6 +3812,7 @@ public class Settings
builder.append("renderOrderByRownumberForEmulatedPagination", renderOrderByRownumberForEmulatedPagination);
builder.append("renderOutputForSQLServerReturningClause", renderOutputForSQLServerReturningClause);
builder.append("renderParenthesisAroundSetOperationQueries", renderParenthesisAroundSetOperationQueries);
builder.append("namePathSeparator", namePathSeparator);
builder.append("bindOffsetDateTimeType", bindOffsetDateTimeType);
builder.append("bindOffsetTimeType", bindOffsetTimeType);
builder.append("fetchTriggerValuesAfterSQLServerOutput", fetchTriggerValuesAfterSQLServerOutput);
@ -4080,6 +4141,15 @@ public class Settings
return false;
}
}
if (namePathSeparator == null) {
if (other.namePathSeparator!= null) {
return false;
}
} else {
if (!namePathSeparator.equals(other.namePathSeparator)) {
return false;
}
}
if (bindOffsetDateTimeType == null) {
if (other.bindOffsetDateTimeType!= null) {
return false;
@ -4930,6 +5000,7 @@ public class Settings
result = ((prime*result)+((renderOrderByRownumberForEmulatedPagination == null)? 0 :renderOrderByRownumberForEmulatedPagination.hashCode()));
result = ((prime*result)+((renderOutputForSQLServerReturningClause == null)? 0 :renderOutputForSQLServerReturningClause.hashCode()));
result = ((prime*result)+((renderParenthesisAroundSetOperationQueries == null)? 0 :renderParenthesisAroundSetOperationQueries.hashCode()));
result = ((prime*result)+((namePathSeparator == null)? 0 :namePathSeparator.hashCode()));
result = ((prime*result)+((bindOffsetDateTimeType == null)? 0 :bindOffsetDateTimeType.hashCode()));
result = ((prime*result)+((bindOffsetTimeType == null)? 0 :bindOffsetTimeType.hashCode()));
result = ((prime*result)+((fetchTriggerValuesAfterSQLServerOutput == null)? 0 :fetchTriggerValuesAfterSQLServerOutput.hashCode()));

View File

@ -69,6 +69,7 @@ final class ConstantSortField<T> extends CustomField<T> {
case DERBY:
case HSQLDB:
case POSTGRES:

View File

@ -1524,7 +1524,7 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> {
Field<?> f = field instanceof Coerce ? ((Coerce<?>) field).field : field;
if (f instanceof RowField && NO_NATIVE_SUPPORT.contains(ctx.dialect())) {
nested = ((RowField<?, ?>) f).emulatedFields();
nested = ((RowField<?, ?>) f).emulatedFields(configuration);
recordType = Tools.recordType(nested.size());
}
else if (f.getDataType().isEmbeddable()) {

View File

@ -316,6 +316,7 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
* This configuration can be used for caching reflection information.
*/
private final Configuration configuration;
private final String namePathSeparator;
/**
* A delegate mapper created from type information in <code>type</code>.
@ -351,6 +352,7 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
this.fields = rowType.fields();
this.type = type;
this.configuration = configuration != null ? configuration : new DefaultConfiguration();
this.namePathSeparator = this.configuration.settings().getNamePathSeparator();
init(instance);
}
@ -798,11 +800,11 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
// No annotations are present
else {
int dot = name.indexOf('.');
int separator = name.indexOf(namePathSeparator);
// A nested mapping is applied
if (dot > -1) {
String prefix = name.substring(0, dot);
if (separator > -1) {
String prefix = name.substring(0, separator);
if (nestedMappedFields == null)
nestedMappedFields = new HashMap<>();
@ -1026,8 +1028,8 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
for (int i = 0; i < fields.length; i++) {
Field<?> field = fields[i];
String name = field.getName();
int dot = name.indexOf('.');
propertyIndexes[i] = prefixes().get(dot > -1 ? name.substring(0, dot) : name);
int separator = name.indexOf(namePathSeparator);
propertyIndexes[i] = prefixes().get(separator > -1 ? name.substring(0, separator) : name);
}
}
}
@ -1061,7 +1063,7 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
}
for (int j = 0; j < propertyNames.size(); j++) {
if (name.startsWith(propertyNames.get(j) + ".")) {
if (name.startsWith(propertyNames.get(j) + namePathSeparator)) {
propertyIndexes[i] = j;
continue fieldLoop;
}
@ -1090,7 +1092,7 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
}
for (int j = 0; j < fields.length; j++) {
if (fields[j].getName().startsWith(prefix + ".")) {
if (fields[j].getName().startsWith(prefix + namePathSeparator)) {
hasNestedFields = true;
if (nestedMappedFields[i] == null)
@ -1211,8 +1213,8 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
for (Field<?> field : fields) {
String name = field.getName();
int dot = name.indexOf('.');
prefixes.computeIfAbsent(dot > -1 ? name.substring(0, dot) : name, k -> i[0]++);
int separator = name.indexOf(namePathSeparator);
prefixes.computeIfAbsent(separator > -1 ? name.substring(0, separator) : name, k -> i[0]++);
}
}

View File

@ -76,6 +76,7 @@ import static org.jooq.JoinType.RIGHT_OUTER_JOIN;
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.CUBRID;
// ...
// ...

View File

@ -301,12 +301,6 @@ final class Limit extends AbstractQueryPart {
default: {
acceptDefault(ctx, castMode);
break;

View File

@ -75,6 +75,7 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED;
import java.util.Set;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.Name;
@ -90,7 +91,6 @@ final class RowField<ROW extends Row, REC extends Record> extends AbstractField<
static final Set<SQLDialect> NO_NATIVE_SUPPORT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, IGNITE, MARIADB, MYSQL, SQLITE);
private final ROW row;
private final AbstractRow<REC> emulatedFields;
RowField(ROW row) {
this(row, N_ROW);
@ -113,11 +113,11 @@ final class RowField<ROW extends Row, REC extends Record> extends AbstractField<
)));
this.row = row;
this.emulatedFields = (AbstractRow<REC>) row0(map(row.fields(), x -> x.as(as.unquotedName() + "." + x.getName()), Field[]::new));
}
AbstractRow<REC> emulatedFields() {
return emulatedFields;
@SuppressWarnings("unchecked")
AbstractRow<REC> emulatedFields(Configuration configuration) {
return (AbstractRow<REC>) row0(map(row.fields(), x -> x.as(getUnqualifiedName().unquotedName() + configuration.settings().getNamePathSeparator() + x.getName()), Field[]::new));
}
ROW row() {
@ -127,7 +127,7 @@ final class RowField<ROW extends Row, REC extends Record> extends AbstractField<
@Override
public final void accept(Context<?> ctx) {
if (NO_NATIVE_SUPPORT.contains(ctx.dialect()))
ctx.data(DATA_LIST_ALREADY_INDENTED, true, c -> c.visit(new SelectFieldList<>(emulatedFields.fields.fields)));
ctx.data(DATA_LIST_ALREADY_INDENTED, true, c -> c.visit(new SelectFieldList<>(emulatedFields(ctx.configuration()).fields.fields)));

View File

@ -1761,6 +1761,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
case CUBRID:

View File

@ -195,6 +195,21 @@ When this setting is set to <code>true</code> the queries combined with set oper
For details, see <a href="https://github.com/jOOQ/jOOQ/issues/3676">https://github.com/jOOQ/jOOQ/issues/3676</a> and <a href="https://github.com/jOOQ/jOOQ/issues/9751">https://github.com/jOOQ/jOOQ/issues/9751</a>.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="namePathSeparator" type="string" minOccurs="0" maxOccurs="1" default=".">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The character(s) to be used as a separator in paths encoded in a {@link Name}
<p>
A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as:
<p>
<code><pre>
SELECT
a.first_name AS "book.author.firstName"
a.last_name AS "book.author.lastName"
FROM ...
</pre></code>
<p>
Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__".]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="bindOffsetDateTimeType" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether the <code>java.time</code> (JSR 310) type {@link java.time.OffsetDateTime} should be bound natively to JDBC.
<p>