Class Db

java.lang.Object
com.codename1.backend.Db

public final class Db extends Object

SQLite persistence for server-side binaries, on the engine the translator already bundles. Not com.codename1.db.Database, which needs a CodenameOneImplementation for every call.

Parameters are always bound, never interpolated: string concatenation into SQL is how injection happens, and a server parses input it did not write.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
    A unit of work run inside transaction(Db.Work).
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    SYNCHRONIZED, like execute, query and transaction, and it keeps the handle when the close does not take.
    void
    Switches the database to write-ahead logging, which is what lets readers run while a writer is active.
    int
    execute(String sql, Object[] params)
    Runs a statement that returns no rows.
    long
    The rowid the most recent insert produced.
    static Db
    open(String path)
    Opens (or creates) the database at the given path.
    query(String sql, Object[] params)
    Runs a query and returns every row as a column-name to value map.
    void
    setBusyTimeout(int millis)
    How long a blocked writer waits for a competing one before giving up.
    Synchronized because a TRANSACTION is not one call.

    Methods inherited from class Object

    clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • open

      public static Db open(String path) throws IOException
      Opens (or creates) the database at the given path. ":memory:" gives a process-lifetime database, which is what a stateless function usually wants for a cache.
      Throws:
      IOException
    • execute

      public int execute(String sql, Object[] params) throws IOException
      Runs a statement that returns no rows. Returns the number of rows changed.
      Throws:
      IOException
    • query

      public List query(String sql, Object[] params) throws IOException
      Runs a query and returns every row as a column-name to value map. Values are String, Long, Double or null, which is exactly what the JSON writer accepts.
      Throws:
      IOException
    • transaction

      public Object transaction(Db.Work body) throws Exception

      Synchronized because a TRANSACTION is not one call.

      SQLite serializes each API call on a connection, which is what made "one shared connection is correct, and SQLite serializes it" look true. It serializes the calls, not the BEGIN/body/COMMIT sequence around them: a second handler sharing this Db can execute between another's BEGIN and COMMIT and have its write committed -- or rolled back -- by a request that knows nothing about it, or meet "cannot start a transaction within a transaction" and fail for a reason its own code cannot explain.

      The monitor is reentrant, which is what makes this work: transaction() holds it for the whole callback and the execute() calls inside it re-enter freely. A pooled connection is used by one thread at a time anyway, so the cost there is an uncontended lock; a shared one is serialized, which is exactly what correctness requires of it.

      Throws:
      Exception
    • enableWriteAheadLog

      public void enableWriteAheadLog() throws IOException
      Switches the database to write-ahead logging, which is what lets readers run while a writer is active. Worth doing once after open for anything that serves concurrent requests; pointless for :memory:.
      Throws:
      IOException
    • setBusyTimeout

      public void setBusyTimeout(int millis) throws IOException
      How long a blocked writer waits for a competing one before giving up. Without this, two connections writing at once produce SQLITE_BUSY immediately rather than queueing.
      Throws:
      IOException
    • lastInsertId

      public long lastInsertId()

      The rowid the most recent insert produced.

      SYNCHRONIZED, like execute, query, transaction and close -- it was the one operation here that was not, and the only one that hands the native handle over without holding the lock that guards it. close() frees the sqlite3 connection and then zeroes the field, so a reader that had already loaded the handle called sqlite3_last_insert_rowid on freed memory. The native's own null check cannot help with that: the pointer it is given is not null, it is dangling.

    • close

      public void close()

      SYNCHRONIZED, like execute, query and transaction, and it keeps the handle when the close does not take.

      sqlite3_close answers SQLITE_BUSY when a prepared statement is still alive on the connection, and does NOT destroy it. Zeroing the handle regardless threw away the only reference to a connection that was still open, so nothing could ever close it again -- the native connection and everything it owns leaked, once per shutdown that raced a borrower.

      The lock is what makes that rare rather than merely recoverable: a DbPool.close() arriving while a borrowed connection is mid-query now waits for the query instead of closing underneath it.