Skip to content

Commit

Permalink
Allow CharSequence rather than String to pass sql statements to the l…
Browse files Browse the repository at this point in the history
…ibrary.

Add class org.jdbi.v3.core.Sql to write easy-to-read inline sql statements.
  • Loading branch information
spannm committed Jul 27, 2022
1 parent 18dbd43 commit c2d3cc4
Show file tree
Hide file tree
Showing 31 changed files with 272 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public StringSubstitutorTemplateEngine(Consumer<StringSubstitutor> customizer) {
}

@Override
public String render(String template, StatementContext ctx) {
public String render(CharSequence template, StatementContext ctx) {
StringSubstitutor substitutor = new StringSubstitutor(ctx.getAttributes());
customizer.accept(substitutor);
return substitutor.replace(template);
Expand Down
14 changes: 7 additions & 7 deletions core/src/main/java/org/jdbi/v3/core/Handle.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public boolean isClosed() {
* @param args arguments to bind positionally
* @return query object
*/
public Query select(String sql, Object... args) {
public Query select(CharSequence sql, Object... args) {
Query query = this.createQuery(sql);
int position = 0;
for (Object arg : args) {
Expand All @@ -246,7 +246,7 @@ public Query select(String sql, Object... args) {
*
* @return the number of rows affected
*/
public int execute(String sql, Object... args) {
public int execute(CharSequence sql, Object... args) {
try (Update stmt = createUpdate(sql)) {
int position = 0;
for (Object arg : args) {
Expand All @@ -272,7 +272,7 @@ public Batch createBatch() {
* @param sql the batch SQL
* @return a batch which can have "statements" added
*/
public PreparedBatch prepareBatch(String sql) {
public PreparedBatch prepareBatch(CharSequence sql) {
return new PreparedBatch(this, sql);
}

Expand All @@ -283,7 +283,7 @@ public PreparedBatch prepareBatch(String sql) {
*
* @return the Call
*/
public Call createCall(String sql) {
public Call createCall(CharSequence sql) {
return new Call(this, sql);
}

Expand All @@ -293,7 +293,7 @@ public Call createCall(String sql) {
* @param sql SQL that may return results
* @return a Query builder
*/
public Query createQuery(String sql) {
public Query createQuery(CharSequence sql) {
return new Query(this, sql);
}

Expand All @@ -304,7 +304,7 @@ public Query createQuery(String sql) {
*
* @return the created Script
*/
public Script createScript(String sql) {
public Script createScript(CharSequence sql) {
return new Script(this, sql);
}

Expand All @@ -315,7 +315,7 @@ public Script createScript(String sql) {
*
* @return the Update builder
*/
public Update createUpdate(String sql) {
public Update createUpdate(CharSequence sql) {
return new Update(this, sql);
}

Expand Down
125 changes: 125 additions & 0 deletions core/src/main/java/org/jdbi/v3/core/Sql.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jdbi.v3.core;

import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* <p>
* An immutable sql statement string created from multiple tokens
* in order to write inline sql statements in an easy-to-read fashion
* spread out over multiple lines of code.
* </p>
*
* <p>
* The class implements {@link CharSequence} and thus can be used as a drop-in
* alternative wherever API supports {@code CharSequence} rather than {@code String}.
* </p>
*
* Please note that the validity of the statement is never checked,
* and that {@code null} or empty inputs are permitted (no run-time exceptions).<br>
*
* The input of multiple tokens is formatted into a single String by
* removing leading and trailing whitespace and concatenating
* non-empty tokens by a single space character.
* Further, any trailing semicolons are removed from the resulting sql string.
*
* <p>Example:</p>
*
* <pre>
* String tblName = "table";
* Sql.of("SELECT COUNT(*)",
* "FROM", tblName,
* " WHERE cond1 = :cond1",
* " AND cond2 = :cond2");
* </pre>
*
* @author Markus Spann
*/
public final class Sql implements CharSequence {

/** The internal sql string. Cannot be null. */
private final String str;

private Sql(String sql) {
str = sql;
}

public static Sql of(CharSequence... tokens) {
return tokens == null ? new Sql("") : of(Arrays.asList(tokens));
}

public static Sql of(Collection<CharSequence> tokens) {
return tokens == null ? new Sql("") : new Sql(format(tokens));
}

/**
* Formats an sql statement from multiple tokens.<br>
* Leading and trailing whitespace is removed from each token and empty tokens ignored.<br>
* The tokens are joined using a single blank character to create the sql string.<br>
* Finally, any trailing semicolons are removed from the resulting sql.
*
* @param tokens collection of tokens
* @return formatted sql string
*/
static String format(Collection<CharSequence> tokens) {
String sql = Objects.requireNonNull(tokens).stream()
.filter(Objects::nonNull)
.map(CharSequence::toString)
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.joining(" "));
while (sql.endsWith(";")) {
sql = sql.substring(0, sql.length() - 1);
}
return sql;
}

@Override
public int length() {
return str.length();
}

@Override
public char charAt(int index) {
return str.charAt(index);
}

@Override
public CharSequence subSequence(int start, int end) {
return str.subSequence(start, end);
}

@Override
public int hashCode() {
return str.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return str.equals(((Sql) obj).str);
}

@Override
public String toString() {
return str;
}

}
4 changes: 2 additions & 2 deletions core/src/main/java/org/jdbi/v3/core/statement/Batch.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public Batch(Handle handle) {
* @param sql SQL to be added to the batch, possibly a named statement
* @return the same Batch statement
*/
public Batch add(String sql) {
parts.add(sql);
public Batch add(CharSequence sql) {
parts.add(sql.toString());
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ abstract class CachingSqlParser implements SqlParser {
}

@Override
public ParsedSql parse(String sql, StatementContext ctx) {
public ParsedSql parse(CharSequence sql, StatementContext ctx) {
try {
return parsedSqlCache.get(sql);
return parsedSqlCache.get(sql.toString());
} catch (IllegalArgumentException e) {
throw new UnableToCreateStatementException("Exception parsing for named parameter replacement", e, ctx);
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jdbi/v3/core/statement/Call.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
public class Call extends SqlStatement<Call> {
private final List<OutParamArgument> params = new ArrayList<>();

public Call(Handle handle, String sql) {
public Call(Handle handle, CharSequence sql) {
super(handle, sql);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ public Statement create(Connection conn, StatementContext ctx) throws SQLExcepti
* @return a new PreparedStatement
*/
@Override
public PreparedStatement create(Connection conn, String sql, StatementContext ctx) throws SQLException {
public PreparedStatement create(Connection conn, CharSequence sql, StatementContext ctx) throws SQLException {
if (ctx.isReturningGeneratedKeys()) {
String[] columnNames = ctx.getGeneratedKeysColumnNames();
if (columnNames != null && columnNames.length > 0) {
return conn.prepareStatement(sql, columnNames);
return conn.prepareStatement(sql.toString(), columnNames);
}
return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
return conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
} else if (ctx.isConcurrentUpdatable()) {
return conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
return conn.prepareStatement(sql.toString(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
} else {
return conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
return conn.prepareStatement(sql.toString(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
}

Expand All @@ -68,7 +68,7 @@ public PreparedStatement create(Connection conn, String sql, StatementContext ct
* @throws java.sql.SQLException if anything goes wrong closing the statement
*/
@Override
public void close(Connection conn, String sql, Statement stmt) throws SQLException {
public void close(Connection conn, CharSequence sql, Statement stmt) throws SQLException {
if (stmt != null) {
stmt.close();
}
Expand All @@ -88,7 +88,7 @@ public void close(Connection conn) {}
* @param ctx Statement context associated with the SqlStatement this is building for
*/
@Override
public CallableStatement createCall(Connection conn, String sql, StatementContext ctx) throws SQLException {
return conn.prepareCall(sql);
public CallableStatement createCall(Connection conn, CharSequence sql, StatementContext ctx) throws SQLException {
return conn.prepareCall(sql.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
*/
public class DefinedAttributeTemplateEngine implements TemplateEngine.Parsing {
@Override
public Optional<Function<StatementContext, String>> parse(String template, ConfigRegistry config) {
public Optional<Function<StatementContext, String>> parse(CharSequence template, ConfigRegistry config) {
StringBuilder buf = new StringBuilder();
List<BiConsumer<StatementContext, StringBuilder>> preparation = new ArrayList<>();
Runnable pushBuf = () -> { // NOPMD
Expand All @@ -51,7 +51,7 @@ public Optional<Function<StatementContext, String>> parse(String template, Confi
preparation.add((ctx, b) -> b.append(bit));
}
};
DefineStatementLexer lexer = new DefineStatementLexer(CharStreams.fromString(template));
DefineStatementLexer lexer = new DefineStatementLexer(CharStreams.fromString(template.toString()));
lexer.addErrorListener(new ErrorListener());
Token t = lexer.nextToken();
while (t.getType() != EOF) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public class MessageFormatTemplateEngine implements TemplateEngine {
public MessageFormatTemplateEngine() {}

@Override
public String render(String template, StatementContext ctx) {
MessageFormat msgFormat = new MessageFormat(template);
public String render(CharSequence template, StatementContext ctx) {
MessageFormat msgFormat = new MessageFormat(template.toString());

validateKeys(ctx.getAttributes().keySet(), msgFormat.getFormats().length);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
public class NoTemplateEngine implements TemplateEngine {
@Override
public String render(final String template, final StatementContext ctx) {
return template;
public String render(final CharSequence template, final StatementContext ctx) {
return template == null ? null : template.toString();
}
}
8 changes: 4 additions & 4 deletions core/src/main/java/org/jdbi/v3/core/statement/ParsedSql.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public final class ParsedSql {
private final String sql;
private final ParsedParameters parameters;

private ParsedSql(String sql, ParsedParameters parameters) {
this.sql = sql;
private ParsedSql(CharSequence sql, ParsedParameters parameters) {
this.sql = sql.toString();
this.parameters = parameters;
}

Expand Down Expand Up @@ -91,7 +91,7 @@ public String toString() {
* @see ParsedParameters#positional(int)
* @see ParsedParameters#named(List)
*/
public static ParsedSql of(String sql, ParsedParameters parameters) {
public static ParsedSql of(CharSequence sql, ParsedParameters parameters) {
return new ParsedSql(sql, parameters);
}

Expand Down Expand Up @@ -121,7 +121,7 @@ private Builder() {}
* @param sqlFragment the SQL fragment
* @return this
*/
public Builder append(String sqlFragment) {
public Builder append(CharSequence sqlFragment) {
sql.append(sqlFragment);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class PreparedBatch extends SqlStatement<PreparedBatch> implements Result
private final List<PreparedBinding> bindings = new ArrayList<>();
final Map<PrepareKey, Function<String, Optional<Function<Object, Argument>>>> preparedFinders = new HashMap<>();

public PreparedBatch(Handle handle, String sql) {
public PreparedBatch(Handle handle, CharSequence sql) {
super(handle, sql);
getContext().setBinding(new PreparedBinding(getContext()));
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jdbi/v3/core/statement/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* Statement providing convenience result handling for SQL queries.
*/
public class Query extends SqlStatement<Query> implements ResultBearing {
public Query(Handle handle, String sql) {
public Query(Handle handle, CharSequence sql) {
super(handle, sql);
}

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jdbi/v3/core/statement/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
public class Script extends SqlStatement<Script> {
private final Handle handle;

public Script(Handle h, String sql) {
public Script(Handle h, CharSequence sql) {
super(h, sql);
this.handle = h;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public interface SqlParser {
* @return the parsed SQL representing the SQL statement itself along with
* information about the parameters which should be bound (number and names)
*/
ParsedSql parse(String sql, StatementContext ctx);
ParsedSql parse(CharSequence sql, StatementContext ctx);

/**
* Convert rawName to a name as recognized by this parser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ public abstract class SqlStatement<This extends SqlStatement<This>> extends Base
PreparedStatement stmt;

SqlStatement(Handle handle,
String sql) {
CharSequence sql) {
super(handle);

this.handle = handle;
this.sql = sql;
this.sql = sql.toString();

getContext()
.setConnection(handle.getConnection())
.setRawSql(sql);
.setRawSql(this.sql);
}

protected Binding getBinding() {
Expand Down
Loading

0 comments on commit c2d3cc4

Please sign in to comment.