Java 时间戳不兼容(NiFi&x27;s PutSQL)

Java 时间戳不兼容(NiFi&x27;s PutSQL),java,python,json,postgresql,apache-nifi,Java,Python,Json,Postgresql,Apache Nifi,我面临使用Nifi PutSQL处理器将时间戳插入PostgreSQL数据库的问题 更具体地说,当尝试将格式为“2018-01-31T19:01:09+00:00”的日期插入timestamptz列时,我会收到以下错误消息: 2018-04-01 19:29:40,091 ERROR [Timer-Driven Process Thread-5] o.apache.nifi.processors.standard.PutSQL PutSQL[id=7997503a-0162-1000-ee81-

我面临使用Nifi PutSQL处理器将时间戳插入PostgreSQL数据库的问题

更具体地说,当尝试将格式为“2018-01-31T19:01:09+00:00”的日期插入timestamptz列时,我会收到以下错误消息:

2018-04-01 19:29:40,091 ERROR [Timer-Driven Process Thread-5] o.apache.nifi.processors.standard.PutSQL PutSQL[id=7997503a-0162-1000-ee81-a0361cad5e0c] Failed to update database for StandardFlowFileRecord[uuid=d02e8b39-e564-4c37-a08a-dab8931e9890,claim=StandardContentClaim [resourceClaim=StandardResourceClaim[id=1522615075930-15, container=default, section=15], offset=11492, length=163],offset=0,name=32836401373126,size=163] due to java.sql.SQLDataException: The value of the sql.args.5.value is '2018-01-31T20:19:35+00:00', which cannot be converted to a timestamp; routing to failure: java.sql.SQLDataException: The value of the sql.args.5.value is '2018-01-31T20:19:35+00:00', which cannot be converted to a timestamp
java.sql.SQLDataException: The value of the sql.args.5.value is '2018-01-31T20:19:35+00:00', which cannot be converted to a timestamp
    at org.apache.nifi.processors.standard.PutSQL.setParameters(PutSQL.java:711)
    at org.apache.nifi.processors.standard.PutSQL.lambda$null$5(PutSQL.java:313)
    at org.apache.nifi.processor.util.pattern.ExceptionHandler.execute(ExceptionHandler.java:127)
    at org.apache.nifi.processors.standard.PutSQL.lambda$new$6(PutSQL.java:311)
    at org.apache.nifi.processors.standard.PutSQL.lambda$new$9(PutSQL.java:354)
    at org.apache.nifi.processor.util.pattern.PutGroup.putFlowFiles(PutGroup.java:91)
    at org.apache.nifi.processor.util.pattern.Put.onTrigger(Put.java:101)
    at org.apache.nifi.processors.standard.PutSQL.lambda$onTrigger$20(PutSQL.java:574)
    at org.apache.nifi.processor.util.pattern.PartialFunctions.onTrigger(PartialFunctions.java:114)
    at org.apache.nifi.processor.util.pattern.RollbackOnFailure.onTrigger(RollbackOnFailure.java:184)
    at org.apache.nifi.processors.standard.PutSQL.onTrigger(PutSQL.java:574)
    at org.apache.nifi.controller.StandardProcessorNode.onTrigger(StandardProcessorNode.java:1122)
    at org.apache.nifi.controller.tasks.ContinuallyRunProcessorTask.call(ContinuallyRunProcessorTask.java:147)
    at org.apache.nifi.controller.tasks.ContinuallyRunProcessorTask.call(ContinuallyRunProcessorTask.java:47)
    at org.apache.nifi.controller.scheduling.TimerDrivenSchedulingAgent$1.run(TimerDrivenSchedulingAgent.java:128)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.text.ParseException: Unparseable date: "2018-01-31T20:19:35+00:00"
    at java.text.DateFormat.parse(DateFormat.java:366)
    at org.apache.nifi.processors.standard.PutSQL.setParameter(PutSQL.java:911)
    at org.apache.nifi.processors.standard.PutSQL.setParameters(PutSQL.java:707)
    ... 21 common frames omitted
我已经测试了从命令行将'2018-01-31T19:01:09+00:00'插入到timestamptz列中,它工作得非常好。我尝试过各种替代格式,例如:

  • “2018-01-31 19:01:09+00:00”
  • “2018-01-3119:01:09+00”
  • “2018-01-31T19:01:09+00”
  • “2018-01-3119:01:09”
它们在Nifi中都会失败,并出现相同的错误,即使它们在从命令行执行插入时都插入得很好

请在附件中找到我的流程截图。如果你需要更多的细节,请告诉我

老实说,我更愿意避免java转换,因为将datetime保留为字符串并将其直接插入Postgres DB就可以了。我曾尝试使用UpdateAttribute处理器强制执行此操作,但这会导致其他错误

关于这个话题,我遇到了各种各样的问题,但我仍然不明白到底发生了什么。最值得注意的是:


您可以在PutSQL之前使用UpdateAttribute,以及表达式和语言函数,将时间戳值转换为数据库可以接受的值


或者,您可以使用PutDatabaseRecord跳过SplitText、ConvertJSONToSQL和PutSQL步骤,您可以配置一个RecordReader,该RecordReader将接受您的时间戳格式并相应地进行转换。如果可以的话,这是一个更好的解决方案,因为它可以一次处理整个流文件(而不是单独的行)

​我通过使用ExecuteStreamCommand处理器解决了这个问题,该处理器调用一个python脚本,该脚本将JSON行转换为相应的SQL insert语句。本例中的关注表是
reddit\u post

python脚本的代码(我知道没有必要使用INSERT参数,但这是因为我计划稍后添加UPDATE选项):

ExecuteStreamCommand的配置(请注意,参数Delimeter设置为单个空格):

流程片段:


我希望这能帮助遇到类似问题的人。如果您对如何改进脚本、流程或其他方面有任何建议,请随时告诉我

仅添加3位毫秒就解决了ExecuteSQL处理器的问题——“yyyy-mm-dd hh:mm:ss.sss”

读取时,在默认情况下,它对日期、时间和时间戳分别使用硬编码格式模式
yyy-mm-dd
hh:mm:ss.sss
yyyyy-mm-dd hh:mm:ss.sss
。您尝试过的字符串中没有一个与这些模式匹配,请尝试在秒后追加
.0000
。或者,在流程文件中定义一个日期模式。感谢您指出这一点。这些格式似乎都不支持时区。您将如何定义自己的日期模式?对于这种情况,这似乎是更好的方法。您的数据库可能会自动将当前格式的字符串转换为timestamptz。因此,请尝试将此参数定义为字符串。或者在数据库级别上使用字符串到日期的转换函数。@vcovo,我认为转换数据库列不是这样做的-在数据库方面,您会损失太多,包括特定的验证和与SQL相关的查询细节。既然这在语句级是一个问题,您仍然应该尝试在语句级进行修复。@vcovo,从什么开始,您应该能够在
流文件
中设置一个日期格式的属性,该文件与保存内容的文件相同。我只是希望你能比我更好地理解我刚才说的话,这实际上是一个有用的评论。谢谢你的见解。实际上,我已经尝试过使用UpdateAttribute作为解决方法,但我非常确定错误来自ConvertJSONToSQL处理器,因此将UpdateAttribute处理器放在后面并没有多大区别。我将查看PutDatabaseRecord-您的描述听起来很有希望。很好,简单,解决方案。谢谢你,比尔!
import json
import argparse
import sys

# For command line arguments
parser = argparse.ArgumentParser(description='Converts JSON to respective SQL statement')
parser.add_argument('statement_type', type=str, nargs=1)
parser.add_argument('table_name', type=str, nargs=1)

# Reading the command line arguments
statement_type = parser.parse_args().statement_type[0]
table_name = parser.parse_args().table_name[0]

# Initialize SQL statement 
statement = ''

for line in sys.stdin:
  # Load JSON line
  json_line = json.loads(line)

  # Add table name and SQL syntax
  if statement_type == 'INSERT':
    statement += 'INSERT INTO {} '.format(table_name)

  # Add table parameters and SQL syntax
  statement += '({}) '.format(', '.join(json_line.keys()))

  # Add table values and SQL syntax
  # Note that strings are formatted with single quotes, other data types are converted to strings (for the join method)
  statement += "VALUES ({});".format(', '.join("'{0}'".format(value.replace("'", "''")) if type(value) == str else str(value) for value in json_line.values()))

  # Send statement to stdout
  print(statement)
​