Storm与JDBC整合

Storm JDBC包含了核心的bolts以及trident states可以使storm topology即能对一个数据库表插入storm tuples又能对数据库进行查询操作,并且丰富了tuple的作用。

注意:在下面的例子中,我们使用了 com.google.common.collect.Lists和com.google.common.collect.Maps.

对数据库进行插入操作

在该包中用来进行插入操作的bolt以及trident state已经与数据库中的单个表建立了联系。

ConnectionProvider

org.apache.storm.jdbc.common.ConnectionProvider该接口由不同的连接池机制来实现。

public interface ConnectionProvider extends Serializable {
    /**
     * method must be idempotent.
     */
    void prepare();

    /**
     *
     * @return a DB connection over which the queries can be executed.
     */
    Connection getConnection();

    /**
     * called once when the system is shutting down, should be idempotent.
     */
    void cleanup();
}
另外,我们支持 org.apache.storm.jdbc.common.HikariCPConnectionProvider(好像这个HikariCP是目前最好的JDBC)。

JdbcMapper

使用JDBC来对数据库进行插入操作的主要API是org.apache.storm.jdbc.mapper.JdbcMapper 接口:

public interface JdbcMapper  extends Serializable {
    List<Column> getColumns(ITuple tuple);
}
getColumns()方法定义了一个tuple如何映射数据库中的列字段列表来表示数据库中的一行(就是tuple中的字段如何与数据库表中的列对应)。返回的列表的顺序性是很重要的。总之就是自己在插入数据的时候,各个字段的顺序要对应一致。例如:我们提交了一个插入查询语句 insert into user(user_id, user_name, create_date) values (?,?, now())那么values()中第一个?就对应了user_id,第二个?对应了usr_name以此类推。getColumns的返回列表也是这样。咱们的jdbc不提供任何不标准的查询语法。

JdbcInsertBolt

要使用JdbcInsertBolt,我们需要用一个ConnectionProvider的实现以及JdbcMapper的实现(该实现将tuple转换成DB的行)来构建一个JdbcInsertBolt实例。另外,我们还需要用withTableName方法提供表名或者用withInsertQuery方法提供一个插入查询。如果我们使用了一个查询查询我们要确保JdbcMapper返回的数据库列字段列表的顺序与我们在插入查询中使用的顺序一致。我们可以设置一个查询超时时间来规定一个查询最多能花多少时间。默认与topology.message.timeout.secs一样大,如果该值为-1那就意味着不设置查询超时。我们可以设置查询超时时间<=topology.message.timeout.secs。

Map hikariConfigMap = Maps.newHashMap();
hikariConfigMap.put("dataSourceClassName","com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
hikariConfigMap.put("dataSource.url", "jdbc:mysql://localhost/test");
hikariConfigMap.put("dataSource.user","root");
hikariConfigMap.put("dataSource.password","password");
ConnectionProvider connectionProvider = new HikariCPConnectionProvider(hikariConfigMap);

String tableName = "user_details";
JdbcMapper simpleJdbcMapper = new SimpleJdbcMapper(tableName, connectionProvider);

JdbcInsertBolt userPersistanceBolt = new JdbcInsertBolt(connectionProvider, simpleJdbcMapper)
                                    .withTableName("user")
                                    .withQueryTimeoutSecs(30);
                                    Or
JdbcInsertBolt userPersistanceBolt = new JdbcInsertBolt(connectionProvider, simpleJdbcMapper)
                                    .withInsertQuery("insert into user values (?,?)")
                                    .withQueryTimeoutSecs(30);  
SimpleJdbcMapper

storm-jdbc有个更一般化的JdbcMapper实现叫做SimpleJdbcMappe,它可以将tuple与数据库的行进行映射。SimpleJdbcMapper假设tuple字段名与你将要写入的数据库表中的列名是一样的。

要使用SimpleJdbcMapper,我们只要告诉其将要写入的表的表名以及提供一个connectionProvider实例就行了。

下面的代码创建了一个SimpleJdbcMapper实例:

1.使tuple转换成数据库列字段列表对应test.user_detials中的一行。

2.提供HikariCP配置利用特定的数据库配置来建立连接池以及自动的算出该表的列名以及对应的数据类型。

通过https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby 来了解更多hikari配置属性

Map hikariConfigMap = Maps.newHashMap();
hikariConfigMap.put("dataSourceClassName","com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
hikariConfigMap.put("dataSource.url", "jdbc:mysql://localhost/test");
hikariConfigMap.put("dataSource.user","root");
hikariConfigMap.put("dataSource.password","password");
ConnectionProvider connectionProvider = new HikariCPConnectionProvider(hikariConfigMap);
String tableName = "user_details";
JdbcMapper simpleJdbcMapper = new SimpleJdbcMapper(tableName, connectionProvider);
在本例中初识化的simpleJdbcMapper假设tuple拥有将要插入的表的所有列的值,并且它的getColumn方法将会以Jdbc connection实例connection.getMetaData().getColumns()方法返回给它的顺序返回所有的列。

如果我们对JdbcInsertBolt进行特定的插入查询,我们必须以显式表示出列的模式(该模式与我们查询中列的顺序一样)来初始SimpleJdbcMapper。例如,如果我们的查询是Insert into user (user_id, user_name) values (?,?)那么我们的SimpleJdbcMapper应该用下列数据进行初始化:

java List<Column> columnSchema = Lists.newArrayList( new Column("user_id", java.sql.Types.INTEGER), new Column("user_name", java.sql.Types.VARCHAR)); <span style="font-family: Arial, Helvetica, sans-serif;">JdbcMapper simpleJdbcMapper = new SimpleJdbcMapper(columnSchema);</span>

如果我们的tuple的字段只是表列的一部分。例如:某些列在我们的表中有默认值我们想只插入那些没有默认值的列,我们就可以用显式的表示列模式来初识化SimpleJdbcMapper.

例如,如果我们有一个user_details表create table if not exists user_details (user_id integer, user_name varchar(100), dept_name varchar(100), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);在该表中,create_time列有默认值。为了保证只插入没有默认值的列我们需要按如下方式初识化jdbcMapper:

List<Column> columnSchema = Lists.newArrayList(
    new Column("user_id", java.sql.Types.INTEGER),
    new Column("user_name", java.sql.Types.VARCHAR),
    new Column("dept_name", java.sql.Types.VARCHAR));
JdbcMapper simpleJdbcMapper = new SimpleJdbcMapper(columnSchema);
JdbcTridentState

为了创建一个jdbc持久化的trident state我们需要提供一个表名或者一个插入查询,JdbcMapper的实例以及connection provider的实例来初始化trident state。如下:

JdbcState.Options options = new JdbcState.Options()
        .withConnectionProvider(connectionProvider)
        .withMapper(jdbcMapper)
        .withTableName("user_details")
        .withQueryTimeoutSecs(30);
JdbcStateFactory jdbcStateFactory = new JdbcStateFactory(options);
与JdbcInsertBolt相似,我们可以利用withInsertquery方法自定义一个插入查询来替代上面的withTableName。


对数据库进行查找

支持selelct查询。执行select查询的主要API是org.apache.storm.jdbc.mapper.JdbcLookupMapper接口:

    void declareOutputFields(OutputFieldsDeclarer declarer);
    List<Column> getColumns(ITuple tuple);
    List<Values> toTuple(ITuple input, List<Column> columns);
declareOutputFields方法用来指定输出的tuple中的字段。

getColumns方法确定查询中的占位符(?)以及它们的SQL类型和值。例如:在上面的user_details表中如果我们执行一个查询
select user_name from user_details where user_id = ? and create_time > ?那么getColumns方法将会接收一个输入tuple并且返回一个包含两个item的列表。他们都是Column类型,第一个item调用getVaule()就可以得到User_id的值,第二个就可以得到create_time的值。

注意:顺序是一致的。。

toTuple方法接收一个输入tuple并且一个表示数据库一行的列字段值列表作为select搜索的结果。返回的是一列用来发送的values列表。注意返回的一列values而不是单个values。这就运行单个数据库行映射多个输出tuple。

SimpleJdbcLookupMapper

storm-jdbc更加普遍的JdbcLookupMapper实现叫做SimpleJdbcLookupMapper。

要使用SimpleJdbcMapper,我们要用bolt将要输出的字段以及在查询中作为占位符的对应的列字段值列表来初识化。SimpleJdbcMapper认为tuple中的字段与你作为占位符的字段名是一致的。例如:这里的占位符对应字段是"user_id",那么他就会从输入的tuple中的"user_id"字段来找对应的值。为了构造输出字段,它首先会从输入tuple中查询相同的字段。如果输入tuple字段没有,那么就从select查询的输出字段中来找相同的字段名。所以在下面的例子中,如果输入tuple的字段为user_id,create_date,select查询为select user_name from user_details where user_id = ?,对于每个输入tuple,SimpleJdbcLookupMapper.getColumns(tuple)将会放回tuple.getValueByField("user_id"),这就是查询中?的值。对于数据库输出的每一行,SimpleJadbcLookUpMapper.toTuple()将会使用输入tuple中的user_id,create_date值,并且仅仅添加从结果行得来的user_name。然后将3个字段作为一个输出tuple。

Fields outputFields = new Fields("user_id", "user_name", "create_date");
List<Column> queryParamColumns = Lists.newArrayList(new Column("user_id", Types.INTEGER));
this.jdbcLookupMapper = new SimpleJdbcLookupMapper(outputFields, queryParamColumns);

JdbcLookupBolt

注意超时设置要<=topology.message.timeout.secs

String selectSql = "select user_name from user_details where user_id = ?";
SimpleJdbcLookupMapper lookupMapper = new SimpleJdbcLookupMapper(outputFields, queryParamColumns)
JdbcLookupBolt userNameLookupBolt = new JdbcLookupBolt(connectionProvider, selectSql, lookupMapper)
        .withQueryTimeoutSecs(30);


用JdbcTridentState进行查询
JdbcState.Options options = new JdbcState.Options()
        .withConnectionProvider(connectionProvider)
        .withJdbcLookupMapper(new SimpleJdbcLookupMapper(new Fields("user_name"), Lists.newArrayList(new Column("user_id", Types.INTEGER))))
        .withSelectQuery("select user_name from user_details where user_id = ?");
        .withQueryTimeoutSecs(30);


为了使用Mysql,在pom.xml中加入下列信息:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.31</version>
</dependency>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值