如何在Apache Beam中将动态SQL语句传递给JDBCIO连接器?

时间:2019-06-06 19:41:31

标签: jdbc google-cloud-platform google-cloud-sql apache-beam

Apache Beam提供了JDBCIO连接器以连接到CloudSql postgreSQL。我的工作从pub / sub中读取一个事件。事件正文如下:

tableName,
list<value>

我需要根据从消息中获得的表名写入表。

JDBCIO已经准备好了一条语句,该语句使我可以在插入查询中参数化值。但是我需要根据事件中存在的信息动态生成插入查询。

 pipeline
   .apply(PubsubIO.readStrings().fromSubscription())
   .apply(convertToKV())
   .apply(JdbcIO.<List<String>>>write()
      .withDataSourceConfiguration(JdbcIO.DataSourceConfiguration.create(
            "com.mysql.jdbc.Driver", "jdbc:mysql://hostname:3306/mydb")
          .withUsername("username")
          .withPassword("password"))
      .withStatement("insert into Person values(?, ?)")
      .withPreparedStatementSetter(new JdbcIO.PreparedStatementSetter<KV<Integer, String>>() {
        public void setParameters(KV<Integer, String> element, PreparedStatement query)
          throws SQLException {
i=0
for each element in list
          query.setInt(i, element.get(i);
i++;

        }
      })
    );

我应该能够基于来自pcollection的输入事件动态创建SQL语句。 我的select语句应基于列表值和表名动态生成。请让我知道我们是否可以这样做。

更新:-

im试图在parDo函数中手动调用jdbc驱动程序,但出现以下错误。 找不到适用于jdbcURL的驱动程序。

请让我知道我是否缺少任何内容:

@Setup
public void doAnyRequiredSetup() throws SQLException
{
    LoggingContextUtil.installContext(loggingContext);


    connection=DriverManager.getConnection(JdbcUrl,user,password);
    statement=connection.createStatement();

    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("In doAnyRequiredSetup logging Context is now set and JDBC connection is .");
    }



}

@SuppressWarnings("unchecked")
@ProcessElement
public void processElement(ProcessContext context)
{
    JsonNode element=context.element();

    try {
        String query=formatQuery(baseQuery);

        boolean result=statement.execute(query);

        if(LOGGER.isDebugEnabled()) {
            LOGGER.debug("Executed query : "+query+" and the result is "+ result);
        }

    } catch (IllegalArgumentException | SQLException e) {

        ErrorMessage em = new ErrorMessage(element.toString(), "Insert Query Failed", e.getMessage());

        context.output(ValidateTagHelper.FAILURE_TAG,em);
    }



}

2 个答案:

答案 0 :(得分:0)

基于输入元素,不能在JdbcIO上进行动态查询。 必须根据需要重置ParDo,您可以重写ParDo,以手动调用JDBC驱动程序。

如果找到其他解决方法,则可以将输入PColleciton分为多个输出。如果您的用例仅限于您可以根据输入从中选择的一组预定义查询,那么这将起作用。这样,您可以将输入分成多个PCollection,然后将不同配置的IO附加到每个PCollection。

https://cloud.google.com/blog/products/gcp/guide-to-common-cloud-dataflow-use-case-patterns-part-1

答案 1 :(得分:0)

您可以尝试读取具有属性的pubsub消息,并且可以在属性中以键值对的形式传递表名和值。

DECLARE @ID int  = 1;

WITH VTE AS(
    SELECT *
    FROM (VALUES(1,3),
                (2,1),
                (3,11),
                (4,2),
                (5,4),
                (6,1),
                (7,8),
                (8,9),
                (9,NULL),
                (10,NULL),
                (11,NULL))V(RowID,FK_RowID)),
Children AS(
    SELECT V.RowID,
           V.FK_RowID
    FROM VTE V
    WHERE V.RowID = @ID
    UNION ALL
    SELECT V.RowID,
           V.FK_RowID
    FROM Children C
         JOIN VTE V ON C.RowID = V.FK_RowID),
Parents AS(
    SELECT V.RowID,
           V.FK_RowID
    FROM VTE V
    WHERE V.RowID = @ID
    UNION ALL
    SELECT V.RowID,
           V.FK_RowID
    FROM Parents P
         JOIN VTE V ON P.FK_RowID = V.RowID)
SELECT RowID
FROM Children
WHERE RowID != @ID
UNION ALL
SELECT RowID
FROM Parents
WHERE RowID != @ID;