Scala Slick 内存数据库 SQLite 没有这样的表
Posted
技术标签:
【中文标题】Scala Slick 内存数据库 SQLite 没有这样的表【英文标题】:Scala Slick In-Memory Database SQLite No Such Table 【发布时间】:2014-09-24 15:53:42 【问题描述】:使用 Scala 2.10.2 和 Slick 2.1.0,我正在为 SQLite 数据库编写一个包装器。对于测试,我希望能够创建一个内存数据库,用我的表填充它,插入一些数据,然后运行测试查询。以下工作代码 sn-p 概述了我的数据库的玩具等价物:
import org.sqlite.JDBC
import scala.slick.driver.SQLiteDriver.simple._
object MyDB
def testMemory() : Unit = MyDB.withMemoryTestDatabase( test )
def testFile() : Unit = MyDB.withFileTestDatabase( test )
val test : ( MyDB ) => Unit =
( mydb ) => mydb.database.withSession implicit session =>
mydb.metadata.map( m => ( m.key, m.value ) ).insert( ( "foo", "bar" ) )
assert( mydb.metadata( "foo" ) == "bar" )
def withMemoryTestDatabase( testFunction : ( MyDB ) => Unit ) : Unit =
val backend = new MyDB( Database.forURL( "jdbc:sqlite::memory:", driver = "org.sqlite.JDBC" ) )
runTest( backend, testFunction )
def withFileTestDatabase( testFunction : ( MyDB ) => Unit ) : Unit =
val backend = new MyDB( Database.forURL( "jdbc:sqlite:test.sqlite", driver = "org.sqlite.JDBC" ) )
runTest( backend, testFunction )
val dbPath = new java.io.File( "test.sqlite" )
dbPath.delete
def runTest( backend : MyDB, testFunction : ( MyDB ) => Unit ) : Unit =
backend.database.withSession implicit session => backend.metadata.ddl.create
testFunction( backend )
class MyDB( val database : slick.driver.SQLiteDriver.backend.DatabaseDef )
class Metadata( tag : Tag ) extends Table[(String,String)]( tag, "Metadata" )
def key = column[String]( "key" )
def value = column[String]( "value" )
def * = ( key, value )
object metadata extends TableQuery( new Metadata( _ ) )
def apply( key : String ) : String = database.withSession implicit session => this.map( m => ( m.key, m.value ) ).run.toMap.apply( key )
我遇到的问题是,使用该代码,MyDB.testFile()
可以完美运行。但是,MyDB.testMemory()
失败,表示元数据表不存在。完整的错误和回溯在这里:
scala> MyDB.testFile
scala> MyDB.testMemory
java.sql.SQLException: [SQLITE_ERROR] SQL error or missing database (no such table: Metadata)
at org.sqlite.DB.newSQLException(DB.java:383)
at org.sqlite.DB.newSQLException(DB.java:387)
at org.sqlite.DB.throwex(DB.java:374)
at org.sqlite.NativeDB.prepare(Native Method)
at org.sqlite.DB.prepare(DB.java:123)
at org.sqlite.PrepStmt.<init>(PrepStmt.java:42)
at org.sqlite.Conn.prepareStatement(Conn.java:404)
at org.sqlite.Conn.prepareStatement(Conn.java:399)
at scala.slick.jdbc.JdbcBackend$SessionDef$class.prepareStatement(JdbcBackend.scala:152)
at scala.slick.jdbc.JdbcBackend$BaseSession.prepareStatement(JdbcBackend.scala:389)
at scala.slick.jdbc.JdbcBackend$SessionDef$class.withPreparedStatement(JdbcBackend.scala:190)
at scala.slick.jdbc.JdbcBackend$BaseSession.withPreparedStatement(JdbcBackend.scala:389)
at scala.slick.driver.JdbcInsertInvokerComponent$BaseInsertInvoker.preparedInsert(JdbcInsertInvokerComponent.scala:170)
at scala.slick.driver.JdbcInsertInvokerComponent$BaseInsertInvoker.internalInsert(JdbcInsertInvokerComponent.scala:180)
at scala.slick.driver.JdbcInsertInvokerComponent$BaseInsertInvoker.insert(JdbcInsertInvokerComponent.scala:175)
at MyDB$$anonfun$testMemory$1$$anonfun$apply$1.apply(MyDb.scala:9)
at MyDB$$anonfun$testMemory$1$$anonfun$apply$1.apply(MyDb.scala:8)
at scala.slick.backend.DatabaseComponent$DatabaseDef$class.withSession(DatabaseComponent.scala:34)
at scala.slick.jdbc.JdbcBackend$DatabaseFactoryDef$$anon$4.withSession(JdbcBackend.scala:61)
at MyDB$$anonfun$testMemory$1.apply(MyDb.scala:8)
at MyDB$$anonfun$testMemory$1.apply(MyDb.scala:8)
at MyDB$.withMemoryTestDatabase(MyDb.scala:28)
at MyDB$.testMemory(MyDb.scala:8)
at .<init>(<console>:8)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)
at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:745)
at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:790)
at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:702)
at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:566)
at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:573)
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:576)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:867)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:822)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:822)
at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:822)
at scala.tools.nsc.interpreter.ILoop.main(ILoop.scala:889)
at xsbt.ConsoleInterface.run(ConsoleInterface.scala:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102)
at sbt.compiler.AnalyzingCompiler.console(AnalyzingCompiler.scala:77)
at sbt.Console.sbt$Console$$console0$1(Console.scala:23)
at sbt.Console$$anonfun$apply$2$$anonfun$apply$1.apply$mcV$sp(Console.scala:24)
at sbt.TrapExit$.sbt$TrapExit$$executeMain$1(TrapExit.scala:33)
at sbt.TrapExit$$anon$1.run(TrapExit.scala:42)
谁能告诉我哪里出错了?
注意JDBC 构造函数的:memory:
参数似乎有效;它当然不会创建:memory:
文件。
【问题讨论】:
我不确定,但如果您与数据库建立多个连接,则数据不会持续存在。见这里:sqlite.org/inmemorydb.html。您可能需要将咒语“?cache=shared”添加到 jdbc uri 的末尾。 听起来很有道理,但是jdbc:sqlite::memory:?cache=shared
的 URL 会创建一个名为 :memory:?cache=shared
的文件,所以这不太正确。
如果您使用 xerial 库来支持 sqlite jdbc,这可能会有所帮助:groups.google.com/forum/#!topic/xerial/gGeqCw3KzvU
@paulmdavies,连接 URI 必须是 jdbc:sqlite:file::memory:?cache=shared
【参考方案1】:
尝试保持连接打开(这有助于 H2,仅推测 SQLite):
def runTest( backend : MyDB, testFunction : ( MyDB ) => Unit ) : Unit =
backend.database.withSession implicit session =>
backend.metadata.ddl.create
testFunction( backend )
【讨论】:
我已经修改了我的代码以使其像这样工作(我本来打算这样做)。然后我将session
显式传递给testFunction
,并在测试函数中的每个数据库调用中显式使用它。但是,这仍然行不通。调试打印确认创建和测试使用相同的会话。我很茫然!
也许设置一个示例 github 项目来显示效果【参考方案2】:
在此处尝试此选项:How should one configure Slick to persist tables between sessions?
TLDR 版本:将“;DB_CLOSE_DELAY=-1”添加到您的数据库网址的末尾。
【讨论】:
以上是关于Scala Slick 内存数据库 SQLite 没有这样的表的主要内容,如果未能解决你的问题,请参考以下文章
为不存在的表 slick scala (Slick 3.0.0, scala) 创建一个类 Table
在 slick、scala 中处理 Postgres json 数据类型
如何使用 Play with Scala 和 Slick 从数据库中获取记录