Как изменить операторы SQL, прежде чем они будут выполнены в JDBI 3 (объекты SQL)

Я хотел бы перехватить и изменить операторы SQL, прежде чем они будут выполнены в JDBI 3 (объекты SQL). Причиной этого является замена пользовательских заполнителей токенов именами схем. Я нашел этот поток, но он предназначен для JDBI 2 ( Использование Dropwizard и JDBI для запроса базы данных с несколькими схемами?). Я попробовал подход ниже, но я застрял на том, как изменить окончательный запрос SQL перед его выполнением.

public class Product {
    private int id = -1;
    private String name = "";

    public Product() {
    }

    public Product(int id, String name) {
       this.id = id;
       this.name = name;
    }

    public int getId() {
       return this.id;
    }

    public void setId(int id) {
       this.id = id;
    }

    public String getName() {
       return this.name;
    }

    public void setName(String name) {
       this.name = name;
    }        
}

public class ProductMapper implements RowMapper<Product> {

    @Override
    public Product map(ResultSet r, StatementContext ctx) throws SQLException {
        Product product = new Product();

        product.setId(r.getInt("id"));
        product.setName(r.getString("name"));

        return product;
    }
}

@SchemaRewriterFactory
@UseClasspathSqlLocator
public interface ProductDao {

    /**
    * The sql query is located in resources ([package]/listProducts.sql)
    * listProducts.sql: SELECT PRODUCT_ID ID, NAME FROM :schema.PRODUCTS
    * @return
    */
   @SqlQuery
   @RegisterRowMapper(ProductMapper.class)
   List<Product> listProducts();

}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@SqlStatementCustomizingAnnotation(SchemaRewriterFactory.SchemaRewriter.class)
public @interface SchemaRewriterFactory {

    public class SchemaRewriter implements SqlStatementCustomizerFactory {

        @Override
        public SqlStatementCustomizer createForMethod(Annotation annotation, Class<?> sqlObjectType, Method method) {
            return null;
        }

        @Override
        public SqlStatementParameterCustomizer createForParameter(Annotation annotation, Class<?> sqlObjectType, Method method, Parameter param, int index, Type paramType) {
            return null;
        }

        @Override
        public SqlStatementCustomizer createForType(Annotation annotation, Class<?> sqlObjectType) {
            return q -> q.addCustomizer(new StatementCustomizer() {

                @Override
                public void beforeBinding(PreparedStatement stmt, StatementContext ctx) throws SQLException {}

                @Override
                public void beforeExecution(PreparedStatement stmt, StatementContext ctx) throws SQLException {                          
                    System.out.println(stmt.toString());
                    //TODO: HOW DO I MODIFY SQL (REPLACE TOKENS - :schema) BEFORE IT GETS EXECUTED?
                }

                @Override
                public void afterExecution(PreparedStatement stmt, StatementContext ctx) throws SQLException { }


             });

        }

    }
}

Я также нашел эту ветку, которая занимается заменой пользовательских тегов с помощью @Define ( динамический порядок в объектных запросах JDBI SQL). Если возможно, я бы хотел избежать передачи дополнительных параметров при каждом вызове. Любая помощь будет оценена!

2 ответа

Вы можете использовать StringTemplate для этого.

/**
  * The sql query is located in resources ([package]/listProducts.sql)
  * listProducts.sql: SELECT PRODUCT_ID ID, NAME FROM <schemaName>.PRODUCTS
  * @return
*/
@SqlQuery
@RegisterRowMapper(ProductMapper.class)
List<Product> listProducts(@Define("schemaName") String schemaName);

Просто будьте осторожны, чтобы продезинфицировать schemaName параметр (возможное внедрение SQL)

Я нашел способ перехватить запрос и изменить его перед выполнением.

Вы должны создать экземпляр с индивидуальным StatementBuilder. Вот как:

      private fun getCustomStatementBuilderFactory(): StatementBuilderFactory =
            StatementBuilderFactory {
                object : DefaultStatementBuilder() {
                    override fun create(conn: Connection?,
                            sql: String?,
                            ctx: StatementContext?): PreparedStatement {
                        //Perform the modification here
                        val modifiedSql = "--Sample\n$sql"
                        return if (ctx!!.isReturningGeneratedKeys) {
                            val columnNames = ctx.generatedKeysColumnNames
                            if (columnNames != null && columnNames.size > 0) conn!!.prepareStatement(
                                    modifiedSql,
                                    columnNames) else conn!!.prepareStatement(sql, 1)
                        } else {
                            if (ctx.isConcurrentUpdatable) conn!!.prepareStatement(modifiedSql,
                                    1003,
                                    1008) else conn!!.prepareStatement(modifiedSql, 1003, 1007)
                        }
                    }
                }
            }

А теперь установите это StatementBuilderFactory к вашему экземпляру jdbi, который вы используете для выполнения запросов.

      Jdbi.create(dataSource).setStatementBuilderFactory(getCustomStatementBuilderFactory())

Теперь, согласно приведенному выше примеру, каждый выполненный запрос будет иметь комментарий. --Sample на старте.

Другие вопросы по тегам