ScalikeJDBC + SQlite:建立连接后无法更改只读标志

时间:2014-04-30 14:02:48

标签: sqlite scala scalikejdbc

尝试使用ScalikeJDBC和SQLite。根据提供的示例提供简单的代码:

import scalikejdbc._, SQLInterpolation._

object Test extends App {
  Class.forName("org.sqlite.JDBC")
  ConnectionPool.singleton("jdbc:sqlite:test.db", null, null)

  implicit val session = AutoSession

  println(sql"""SELECT * FROM kv WHERE key == 'seq' LIMIT 1""".map(identity).single().apply()))
}

失败但有例外:

Exception in thread "main" java.sql.SQLException: Cannot change read-only flag after establishing a connection. Use SQLiteConfig#setReadOnly and QLiteConfig.createConnection().
at org.sqlite.SQLiteConnection.setReadOnly(SQLiteConnection.java:447)
at org.apache.commons.dbcp.DelegatingConnection.setReadOnly(DelegatingConnection.java:377)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.setReadOnly(PoolingDataSource.java:338)
at scalikejdbc.DBConnection$class.readOnlySession(DB.scala:138)
at scalikejdbc.DB.readOnlySession(DB.scala:498)
...

我已经尝试了scalikejdbc 1.7和2.0,错误仍然存​​在。作为sqlite驱动程序,我使用"org.xerial" % "sqlite-jdbc" % "3.7.+"

我该怎么做才能解决错误?

4 个答案:

答案 0 :(得分:2)

以下将创建两个单独的连接,一个用于只读操作,另一个用于写入。

ConnectionPool.add("mydb", s"jdbc:sqlite:${db.getAbsolutePath}", "", "")
ConnectionPool.add(
  "mydb_ro", {
    val conf = new SQLiteConfig()
    conf.setReadOnly(true)
    val source = new SQLiteDataSource(conf)
    source.setUrl(s"jdbc:sqlite:${db.getAbsolutePath}")
    new DataSourceConnectionPool(source)
  }
)

答案 1 :(得分:1)

我发现原因是您正在使用"org.xerial" % "sqlite-jdbc" % "3.7.15-M1"。这个版本看起来仍然不稳定。

使用"3.7.2"和@kawty一样。

答案 2 :(得分:0)

在@Synesso的答案的基础上,我进行了一点扩展,以便能够从配置文件中获取配置值并设置连接设置:

import scalikejdbc._
import scalikejdbc.config.TypesafeConfigReader

case class SqlLiteDataSourceConnectionPool(source: DataSource, 
                                           override val settings: ConnectionPoolSettings)
  extends DataSourceConnectionPool(source)


// read settings for 'default' database
val cpSettings = TypesafeConfigReader.readConnectionPoolSettings()
val JDBCSettings(url, user, password, driver) = TypesafeConfigReader.readJDBCSettings()

// use those to create two connection pools
ConnectionPool.add("db", url, user, password, cpSettings)
ConnectionPool.add(
      "db_ro", {
        val conf = new SQLiteConfig()
        conf.setReadOnly(true)
        val source = new SQLiteDataSource(conf)
        source.setUrl(url)
        SqlLiteDataSourceConnectionPool(source, cpSettings)
      }
    )

// example using 'NamedDB'
val name: Option[String] = NamedDB("db_ro") readOnly { implicit session =>
  sql"select name from users where id = $id".map(rs => rs.string("name")).single.apply()
}

答案 3 :(得分:0)

这对我来说org.xerial/sqlite-jdbc 3.28.0有用:

String path = ...
SQLiteConfig config = new SQLiteConfig();
config.setReadOnly(true);
return DriverManager.getConnection("jdbc:sqlite:" + path, config.toProperties());

有趣的是,我在the issue on the xerial repo上写了一个不同的解决方案:

PoolProperties props = new PoolProperties();
props.setDriverClassName("org.sqlite.JDBC");
props.setUrl("jdbc:sqlite:...");

Properties extraProps = new Properties();
extraProps.setProperty("open_mode", SQLiteOpenMode.READONLY.flag + "");
props.setDbProperties(extraProps);
// This line can be left in or removed; it no longer causes a problem
// as long as the open_mode code is present.
props.setDefaultReadOnly(true);

return new DataSource(props);

我不记得为什么我需要第二个,然后能够将其简化为第一个。但是,如果第一个不起作用,则可以尝试第二个。它使用特定于SQLite的open_mode标志,从而可以安全(但不必要)使用setDefaultReadOnly调用。