3.3.1、可配置的SQL查询
虽然Verticle将先前硬编码的值转换为配置参数,但我们还将进一步通过从属性文件加载SQL查询。
查询语句将从作为配置参数传递的文件中加载,如果没有提供,则从默认资源加载。这种方法的优点是可以适应不同的JDBC驱动程序和SQL方言。
Verticle类的部分代码主要由配置键定义组成:
public class WikiDatabaseVerticle extends AbstractVerticle {
public static final String CONFIG_WIKIDB_JDBC_URL = "wikidb.jdbc.url";
public static final String CONFIG_WIKIDB_JDBC_DRIVER_CLASS = "wikidb.jdbc.driver_class";
public static final String CONFIG_WIKIDB_JDBC_MAX_POOL_SIZE = "wikidb.jdbc.max_pool_size";
public static final String CONFIG_WIKIDB_SQL_QUERIES_RESOURCE_FILE = "wikidb.sqlqueries.resource.file";
public static final String CONFIG_WIKIDB_QUEUE = "wikidb.queue";
private static final Logger LOGGER = LoggerFactory.getLogger(WikiDatabaseVerticle.class);
// (...)
SQL查询存储在属性文件中,HSQLDB的默认值位于src/main/resources/db-queries:
create-pages-table=create table if not exists Pages (Id integer identity primary key, Name varchar(255) unique, Content clob)
get-page=select Id, Content from Pages where Name = ?
create-page=insert into Pages values (NULL, ?, ?)
save-page=update Pages set Content = ? where Id = ?
all-pages=select Name from Pages
delete-page=delete from Pages where Id = ?
WikiDatabaseVerticle类的以下代码从文件加载SQL查询, 并以一个map的形式提供出来:
private enum SqlQuery {
CREATE_PAGES_TABLE,
ALL_PAGES,
GET_PAGE,
CREATE_PAGE,
SAVE_PAGE,
DELETE_PAGE
}
private final HashMap<SqlQuery, String> sqlQueries = new HashMap<>();
private void loadSqlQueries() throws IOException {
String queriesFile = config().getString(CONFIG_WIKIDB_SQL_QUERIES_RESOURCE_FILE);
InputStream queriesInputStream;
if (queriesFile != null) {
queriesInputStream = new FileInputStream(queriesFile);
} else {
queriesInputStream = getClass().getResourceAsStream("/db-queries.properties");
}
Properties queriesProps = new Properties();
queriesProps.load(queriesInputStream);
queriesInputStream.close();
sqlQueries.put(SqlQuery.CREATE_PAGES_TABLE, queriesProps.getProperty("create-pages-table"));
sqlQueries.put(SqlQuery.ALL_PAGES, queriesProps.getProperty("all-pages"));
sqlQueries.put(SqlQuery.GET_PAGE, queriesProps.getProperty("get-page"));
sqlQueries.put(SqlQuery.CREATE_PAGE, queriesProps.getProperty("create-page"));
sqlQueries.put(SqlQuery.SAVE_PAGE, queriesProps.getProperty("save-page"));
sqlQueries.put(SqlQuery.DELETE_PAGE, queriesProps.getProperty("delete-page"));
}
我们使用SqlQuery枚举类型以避免代码中的字符串常量。 Verticle的start方法的代码如下:
private JDBCClient dbClient;
@Override
public void start(Future<Void> startFuture) throws Exception {
/*
* Note: 此处使用阻塞APIs, 因为数据量小...
*/
loadSqlQueries(); (1)
dbClient = JDBCClient.createShared(vertx, new JsonObject()
.put("url", config().getString(CONFIG_WIKIDB_JDBC_URL, "jdbc:hsqldb:file:db/wiki"))
.put("driver_class", config().getString(CONFIG_WIKIDB_JDBC_DRIVER_CLASS, "org.hsqldb.jdbcDriver"))
.put("max_pool_size", config().getInteger(CONFIG_WIKIDB_JDBC_MAX_POOL_SIZE, 30)));
dbClient.getConnection(ar -> {
if (ar.failed()) {
LOGGER.error("Could not open a database connection", ar.cause());
startFuture.fail(ar.cause());
} else {
SQLConnection connection = ar.result();
connection.execute(sqlQueries.get(SqlQuery.CREATE_PAGES_TABLE), create -> { (2)
connection.close();
if (create.failed()) {
LOGGER.error("Database preparation error", create.cause());
startFuture.fail(create.cause());
} else {
vertx.eventBus().consumer(config().getString(CONFIG_WIKIDB_QUEUE, "wikidb.queue"), this::onMessage); (3)
startFuture.complete();
}
});
}
});
}
- 有趣的是,我们在Vert.x中打破了一个重要的原则,即避免阻塞API,但是因为没有用于访问类路径上的资源的异步API,我们的选项有限。我们可以使用Vert.x executeBlocking方法来把阻塞I/O操作 从事件循环转移到工作线程中,但由于数据量非常小,这样做没有明显的 益处;
- 这是使用SQL查询的示例;
- consumer方法注册事件总线目的地处理程序。