Exposed icon indicating copy to clipboard operation
Exposed copied to clipboard

Oracle JDBC throws null expection on DAO create

Open flyingcircle opened this issue 3 years ago • 1 comments

I am hitting the following exception:

java.lang.NullPointerException: Cannot read the array length because "<local1>" is null
		at oracle.jdbc.driver.OraclePreparedStatement.registerReturnParamsForAutoKey(OraclePreparedStatement.java:11907)
		at oracle.jdbc.driver.OraclePreparedStatement.processCompletedBindRow(OraclePreparedStatement.java:2024)
		at oracle.jdbc.driver.OraclePreparedStatement.addBatch(OraclePreparedStatement.java:10058)
		at oracle.jdbc.driver.OraclePreparedStatementWrapper.addBatch(OraclePreparedStatementWrapper.java:1002)
		at org.jetbrains.exposed.sql.statements.jdbc.JdbcPreparedStatementImpl.addBatch(JdbcPreparedStatementImpl.kt:21)
		at org.jetbrains.exposed.sql.statements.Statement.executeIn$exposed_core(Statement.kt:54)
		at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:138)
		at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:124)
		at org.jetbrains.exposed.sql.statements.Statement.execute(Statement.kt:28)
		at org.jetbrains.exposed.sql.QueriesKt.executeBatch(Queries.kt:179)
		at org.jetbrains.exposed.sql.QueriesKt.batchInsert(Queries.kt:108)
		at org.jetbrains.exposed.sql.QueriesKt.batchInsert(Queries.kt:94)
		at org.jetbrains.exposed.sql.QueriesKt.batchInsert$default(Queries.kt:89)
		at org.jetbrains.exposed.dao.EntityCache$flushInserts$ids$1.invoke(EntityCache.kt:184)
		at org.jetbrains.exposed.dao.EntityCache$flushInserts$ids$1.invoke(EntityCache.kt:183)
		at org.jetbrains.exposed.dao.EntityLifecycleInterceptorKt.executeAsPartOfEntityLifecycle(EntityLifecycleInterceptor.kt:18)
		at org.jetbrains.exposed.dao.EntityCache.flushInserts$exposed_dao(EntityCache.kt:183)
		at org.jetbrains.exposed.dao.EntityCache.flush(EntityCache.kt:138)
		at org.jetbrains.exposed.dao.EntityCache.flush(EntityCache.kt:107)
		at org.jetbrains.exposed.dao.EntityCacheKt.flushCache(EntityCache.kt:243)
		at org.jetbrains.exposed.dao.EntityLifecycleInterceptor.beforeCommit(EntityLifecycleInterceptor.kt:73)
		at org.jetbrains.exposed.sql.Transaction.commit(Transaction.kt:67)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:190)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.access$inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:1)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$inTopLevelTransaction$1.invoke(ThreadLocalTransactionManager.kt:215)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:223)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction(ThreadLocalTransactionManager.kt:214)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$transaction$1.invoke(ThreadLocalTransactionManager.kt:165)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:223)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:135)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:132)
		at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction$default(ThreadLocalTransactionManager.kt:131)

This is caused by running the following code:

transaction {
  HostAlias.new {
    this.ipAddress = ipAddress
    this.hostnamesString = hostNames.joinToString(",")
    this.description = parameters[DescriptionParameter]?.trim()
  }
}

The table is defined as:

object HostAliases : IdTable<Long>("host_aliases") {
    override val id : org.jetbrains.exposed.sql.Column<EntityID<Long>> = long("id").autoIncrement(SEQUENCE_NAME).entityId()
    override val primaryKey : PrimaryKey = PrimaryKey(id)
    val ipAddress : org.jetbrains.exposed.sql.Column<String> = varchar("ip_address",512).uniqueIndex()
    val description : org.jetbrains.exposed.sql.Column<String?> = text("description", eagerLoading = true).nullable()
    val hostnames : org.jetbrains.exposed.sql.Column<String> = text("hostnames", eagerLoading = true)
}

class HostAlias(id: EntityID<Long>) : LongEntity(id) {
    var ipAddress by HostAliases.ipAddress
    var hostnamesString by HostAliases.hostnames
    override var description by HostAliases.description

The error doesn't happen when I run the code with h2, only on an oracle db. Which leads me to believe that I am hitting some corner case bug with Oracle

flyingcircle avatar May 23 '22 17:05 flyingcircle

I found a work around to this problem. It looks like oracle is not able to do batch updates with auto generated id values. So I instead inserted through DSL and then did a get on the id:

transaction {
    val id = HostAliases.insertAndGetId {
        it[id] = flexlineSequence.nextLongVal()
        it[HostAliases.ipAddress] = ipAddress
        it[hostnames] = hostNames.joinToString(",")
        it[description] = parameters[DescriptionParameter]?.trim()
    }
    HostAlias.findById(id)
}

So I believe that that is the root cause of this issue.

flyingcircle avatar May 27 '22 19:05 flyingcircle

@flyingcircle I was not able to reproduce that exception using your example with current version 0.43.0.

Quite a bit has been fixed with Oracle over the last few months. Could you please confirm if it is still a problem for you using new() or if this issue can be closed?

bog-walk avatar Sep 01 '23 01:09 bog-walk

If the problem persists with current version 0.44.0, please consider reopening this issue on YouTrack. If you do so, please also provide a minimally-reproducible sample, so we can attempt to investigate further.

bog-walk avatar Sep 28 '23 20:09 bog-walk