private ColumnMapper<?> columnMapperFor(Type type) { return columnMappers.computeIfAbsent(type, t -> ctx.findColumnMapperFor(t) .orElseThrow(() -> new NoSuchMapperException("No column mapper registered for " + t))); } }
@Override public void logBeforeExecution(StatementContext context) { attributes.add(context.getAttributes()); context.getBinding().findForPosition(0).ifPresent(a -> bindings.add(a.toString())); }
/** * Obtain an argument for given value in this context * * @param type the type of the argument. * @param value the argument value. * @return an Argument for the given value. */ public Optional<Argument> findArgumentFor(Type type, Object value) { return getConfig(Arguments.class).findFor(type, value); }
@Override public String getMessage() { String base = super.getMessage(); StatementContext ctx = getStatementContext(); if (ctx == null) { return base; } else { return String.format("%s [statement:\"%s\", rewritten:\"%s\", parsed:\"%s\", arguments:%s]", base, ctx.getRawSql(), ctx.getRenderedSql(), ctx.getParsedSql(), ctx.getBinding()); } } }
@Override public void beforeTemplating(PreparedStatement stmt, StatementContext ctx) throws SQLException { final Set<String> alreadyDefined = ctx.getAttributes().keySet(); final Binding binding = ctx.getBinding(); binding.getNames().stream() .filter(name -> !alreadyDefined.contains(name)) .forEach(name -> binding.findForName(name, ctx).ifPresent( a -> ctx.define(name, a instanceof NullArgument ? false : true))); } }
@Override public synchronized void collect(final long elapsedTime, final StatementContext ctx) { rawStatements.add(ctx.getRawSql()); renderedStatements.add(ctx.getRenderedSql()); parsedStatements.add(ctx.getParsedSql()); }
@Override public Optional<ColumnMapper<?>> build(Type type, ConfigRegistry config) { if (String.class.equals(type)) { return Optional.empty(); } return Optional.of((rs, i, ctx) -> { // look for specialized json support first, revert to simple String mapping if absent ColumnMapper<String> jsonStringMapper = JdbiOptionals.findFirstPresent( () -> ctx.findColumnMapperFor(QualifiedType.of(String.class).with(Json.class)), () -> ctx.findColumnMapperFor(String.class)) .orElseThrow(() -> new UnableToProduceResultException(JSON_NOT_RETRIEVABLE, ctx)); String json = jsonStringMapper.map(rs, i, ctx); return json == null ? null : ctx.getConfig(JsonConfig.class).getJsonMapper().fromJson(type, json, ctx); }); } }
@Override public String render(String sql, StatementContext ctx) { try { Template template = new Template(null, sql, null); StringWriter writer = new StringWriter(); template.process(ctx.getAttributes(), writer); return writer.toString(); } catch (IOException | TemplateException e) { throw new IllegalStateException("Failed to process template: " + sql, e); } } }
@Override public Optional<Argument> find(String name, StatementContext ctx) { if (args.containsKey(name)) { final Object argument = args.get(name); final Class<?> argumentClass = argument == null ? Object.class : argument.getClass(); return ctx.findArgumentFor(argumentClass, argument); } return Optional.empty(); }
protected Binding getBinding() { return getContext().getBinding(); }
@Override public Optional<Argument> build(Type type, Object value, ConfigRegistry config) { if (String.class.equals(type)) { return Optional.empty(); } return Optional.of((pos, stmt, ctx) -> { String json = value == null ? null : ctx.getConfig(JsonConfig.class).getJsonMapper().toJson(type, value, ctx); // look for specialized json support first, revert to simple String binding if absent Argument stringBinder = JdbiOptionals.findFirstPresent( () -> ctx.findArgumentFor(QualifiedType.of(String.class).with(Json.class), json), () -> ctx.findArgumentFor(String.class, json)) .orElseThrow(() -> new UnableToCreateStatementException(JSON_NOT_STORABLE)); stringBinder.apply(pos, stmt, ctx); }); } }
/** * Collect the results into a container of the given type. A collector * must be registered for the container type, which knows the element type * for the container. A mapper must be registered for the element type. * <p> * This method is equivalent to {@code ResultBearing.mapTo(elementType).collect(containerCollector)}. * </p> * * @param containerType the container type into which results will be collected * @return a container into which result rows have been collected */ @SuppressWarnings({ "unchecked", "rawtypes" }) default Object collectInto(Type containerType) { return scanResultSet((rs, ctx) -> { Collector collector = ctx.findCollectorFor(containerType) .orElseThrow(() -> new NoSuchCollectorException("No collector registered for container type " + containerType)); Type elementType = ctx.findElementTypeFor(containerType) .orElseThrow(() -> new ElementTypeNotFoundException("Unknown element type for container type " + containerType)); RowMapper<?> mapper = ctx.findMapperFor(elementType) .orElseThrow(() -> new NoSuchMapperException("No mapper registered for element type " + elementType)); return ResultIterable.of(rs, mapper, ctx).collect(collector); }); } }
@Override public void logAfterExecution(StatementContext context) { rawSql.add(context.getRawSql()); timings.add(context.getElapsedTime(ChronoUnit.NANOS)); }
String getStatementName(StatementContext statementContext) { ExtensionMethod extensionMethod = statementContext.getExtensionMethod(); if (extensionMethod != null) { Class<?> type = extensionMethod.getType(); Method method = extensionMethod.getMethod(); String group = type.getPackage().getName(); String name = type.getSimpleName(); return group + "." + name + "." + method.getName(); } else { return "sql.raw." + statementContext.getRawSql(); } } }
/** * Collect the results into a container of the given type. A collector * must be registered for the container type, which knows the element type * for the container. A mapper must be registered for the element type. * <p> * This method is equivalent to {@code ResultBearing.mapTo(elementType).collect(containerCollector)}. * </p> * * @param containerType the container type into which results will be collected * @return a container into which result rows have been collected */ @SuppressWarnings("unchecked") default Object collectInto(Type containerType) { return mapResultSet((rs, ctx) -> { Collector collector = ctx.findCollectorFor(containerType) .orElseThrow(() -> new NoSuchCollectorException("No collector registered for container type " + containerType)); Type elementType = ctx.findElementTypeFor(containerType) .orElseThrow(() -> new ElementTypeNotFoundException("Unknown element type for container type " + containerType)); RowMapper<?> mapper = ctx.findRowMapperFor(elementType) .orElseThrow(() -> new UnsupportedOperationException("No mapper registered for element type " + elementType)); return ResultIterable.of(rs, mapper, ctx).collect(collector); }); } }
@Override public void logBeforeExecution(StatementContext context) { rawSql.add(context.getRawSql()); }
public static StatementContext createContext() { return new StatementContext(); }
private String render(String sql, Map<String, Object> attributes) { attributes.forEach((key, value) -> when(ctx.getAttribute(key)).thenReturn(value)); return templateEngine.render(sql, ctx); }
@Override protected Object reducedResult(Stream<?> stream, StatementContext ctx) { Collector collector = ctx.findCollectorFor(returnType.getType()).orElse(null); if (collector != null) { return stream.collect(collector); } return checkResult(stream.findFirst().orElse(null), returnType); }
@Override protected QualifiedType<?> elementType(StatementContext ctx) { // if returnType is not supported by a collector factory, assume it to be a single-value return type. return returnType.mapType(type -> ctx.findElementTypeFor(type)) .orElse(returnType); } }