2015-10-04 93 views
0

我正在查询Postgres中的表以查看是否存在具有主键值的行匹配给定Map中的行。如果没有结果,我想插入一个新行。当我使用创建预准备语句的标准JDBC路由并明确设置所有列值时,我可以很好地做到这一点,但是我希望Groovy的Sql类可以提供更通用的方法。向Postgres插入行时出现空指针异常

这是我的INSERT语句(主键列的前三个):

insert into MY_TABLE 
    (contract_month, contract_year, publish_date, open, high, low) 
values 
    (10, 2015, 2015-09-15 00:00:00.0, 46.65, 46.9, 45.98) 

的问题是,我是从Postgres的司机尽快得到一个NullPointerException我插入一行:

Exception in thread "main" java.lang.NullPointerException 
    at org.postgresql.core.v3.SimpleParameterList.setNull(SimpleParameterList.java:142) 
    at org.postgresql.jdbc2.AbstractJdbc2Statement.setNull(AbstractJdbc2Statement.java:1259) 
    at org.postgresql.jdbc3.AbstractJdbc3Statement.setNull(AbstractJdbc3Statement.java:1499) 
    at org.postgresql.jdbc4.AbstractJdbc4Statement.setNull(AbstractJdbc4Statement.java:85) 
    at org.postgresql.jdbc2.AbstractJdbc2Statement.setObject(AbstractJdbc2Statement.java:2092) 
    at org.postgresql.jdbc3g.AbstractJdbc3gStatement.setObject(AbstractJdbc3gStatement.java:38) 
    at org.postgresql.jdbc4.AbstractJdbc4Statement.setObject(AbstractJdbc4Statement.java:48) 

挖掘到它,我发现一个名为ProtoConnection的对象在org.postgresql.core.v3.SimpleParameterList类中为null。我究竟做错了什么?

这是包裹代码创建对象Sql

private Main() {  
    final def headers = new HashMap<Integer, String>() 
    final def record = new HashMap<String, Object>() 

    Sql.withInstance(/* my db */) { final Sql sql -> 
     sql.withBatch { 
     new URL(/* my url */).withReader { 
      it.readLines().each { 
      if (headers.isEmpty()) { 
       setHeaders it, headers 
      } 
      else { 
       processRecord it, headers, record 
       storeRecord sql, record 
      } 
      } 
     } 
     } 
    } 
    } 

而这是数据库的逻辑。错误发生在最后一行:

def storeRecord = { final Sql sql, final Map<String, Object> record -> 
    final def connection = sql.connection 
    final def metadata = connection.metaData 

    final def columns = metadata.getColumns(null, null, MY_TABLE, null).with { 
     final def result = [] 
     while (it.next()) { 
     result << it.getString(4) 
     } 
     result 
    } 

    final def primaryKeys = metadata.getPrimaryKeys(null, null, MY_TABLE).with { 
     final def result = [] 
     while (it.next()) { 
     result << it.getString(4) 
     } 
     result 
    } 

    final def whereClause = primaryKeys.collect { final String pk -> 
     "$pk = ?.$pk" 
    } 

    final def select = """ 
     select ${columns.join(', ')} 
      from MY_TABLE 
     where ${whereClause.join(' and ')} 
     """ 

    final def row = sql.firstRow(select, record) 

    if (row) { 
     println "Updating" 
     // Not implemented yet 
    } 
    else { 
     println "Inserting record: $record" 
     final def insert = """ 
       insert into MY_TABLE 
        (${columns.findAll { null != record[it] }.join(", ")}) 
       values 
        (${columns.findAll { null != record[it] }.collect { record[it] }.join(", ")}) 
       """ 
     println insert 
     sql.executeInsert(insert, record) 
    } 
+0

你的堆栈跟踪是不完整的,但我猜想,对于一个'NOT NULL” fieldd在你的记录图的值为空。 – hotzst

+0

由于Groovy关闭的疯狂,我缩写了stacktrace。尽管存在所有非空列的值。 – lealand

+0

感谢您的输入,@hotzst。在经过几次失败后(主要在我身上)管理解决问题! – lealand

回答

0

有几件事我做错了我的代码。首先,我没有将记录映射中的字符串对象转换为适当的JDBC类型。这就解释了为什么我能够通过标准准备好的语句路由插入记录,就像我最初在那里进行转换一样,并在转换到Groovy的API时忽略了它。

修复此问题后,我得到一个错误,不支持hstore扩展名。这只是通过在Postgres中执行create extension hstore来解决的。

最后,我收到一个索引超出界限的错误。这是由于我的代码中的一个愚蠢的错误,我在我的插入语句中设置值,而不是让API填充参数。您可以在现在的INSERT语句的最后一行看到此修复程序:

final def insert = """ 
    insert into MY_TABLE 
     (${columns.join(', ')}) 
    values 
     (${columns.collect { "?.$it" }.join(', ')}) 
    """ 
sql.executeInsert(insert, record)