Как изменить операторы 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
на старте.