获取[SQLITE_BUSY]在使用select语句时数据库文件被锁定

41
如果我对我的Web应用程序运行多个线程,会出现以下情况:

java.sql.SQLException: [SQLITE_BUSY]  The database file is locked (database is locked)
    at org.sqlite.DB.newSQLException(DB.java:383)
    at org.sqlite.DB.newSQLException(DB.java:387)
    at org.sqlite.DB.execute(DB.java:339)
    at org.sqlite.PrepStmt.executeQuery(PrepStmt.java:75)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)

我知道只有一个线程可以写入SQLite数据库,但我只是从数据库中读取数据。那么为什么我会收到这个错误信息?

顺便说一下:我的连接池看起来像这样:

<bean class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" id="dataSource">
    <property name="driverClassName" value="${database.driverClassName}" />
    <property name="url" value="${database.url}" />
    <property name="username" value="${database.username}" />
    <property name="password" value="${database.password}" />
    <property name="initialSize" value="1" />
    <property name="maxActive" value="2" />
    <property name="maxIdle" value="1" />
    <property name="poolPreparedStatements" value="true" />
</bean>

环境为:Java 1.6,Tomcat 7.0.34,Spring 3.2,Hibernate 3.6.9 和 sqlite3 3.7.2

祝好


11个回答

32

是的,那个方法可以用。但我仍然不明白为什么文件会被锁定,尽管只执行了选择语句。 - rogergl
@rogergl:尝试使用SQLiteOpenHelper#getReadableDatabase()获取连接。链接:http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html - sorencito

23

对于在WSL2中遇到问题的任何人:

当我在使用WSL2和Datagrip时遇到了这个问题,即使数据库没有繁忙。

原来 Datagrip 尝试通过 Windows 的 sqlite3 连接存在于 WSL2 中的数据库文件。

将文件从 WSL2 移动到 Windows 文件目录似乎可以解决此问题。


1
这个解决方案对我也有效:从WSL2文件系统复制文件到Windows文件系统可解决连接问题。在我的情况下,我试图使用DBeaver进行连接,因此这个问题和解决方案不仅限于Datagrip。(也许两个程序都使用了相同的数据库驱动库。) - Esteis
1
我遇到了同样的问题。在DataGrip问题跟踪器上已经有记录(https://youtrack.jetbrains.com/issue/DBE-11014)。补充说明:这个bug似乎是WSL的问题,不支持文件锁定:https://github.com/microsoft/WSL/issues/4689 - Sammy S.

13

你的应用程序应该只有一个连接。您可以使用此功能来确保。

public class SqliteHelper {
private static Connection c = null;
public static Connection getConn() throws Exception {
    if(c == null){
    Class.forName("org.sqlite.JDBC");
    c = DriverManager.getConnection("jdbc:sqlite:D:/test.db");
    }
    return c;
    }
}

9
请注意,如果您意外忘记关闭连接,也可能会发生这种情况:
Connection connection;
try {
  Statement statement = connection.createStatement();
  ResultSet resultSet = statement.executeQuery(QUERY);
  if (resultSet.next()) { /* do something */ }
catch (SQLException e) { /* handle exception */ }
finally {
  if (connection != null) {
    try {
      connection.close(); // <-- This is important
    } catch (SQLException e) {
      /* handle exception */
    }
  }
}

一旦服务器启动,首个数据库连接可能很好用,但根据连接池的配置,后续查询可能会出现问题。


3

每次建立连接后,务必在完成工作后关闭连接。这对我很有效,就像你使用的那样。

Connection con = null;
PreparedStatement pst = con.prepareStatement("...query... "); 
/*
 do some stuff 
*/
pst.executeQuery();
pst.close();
con.close();

当我依次执行两个预处理语句时,出现了这个问题。解决方法是关闭预处理语句,就像你建议的那样。 - Endrju

1
我遇到了同样的问题,即使所有的连接、结果集和语句都已关闭,我仍然遇到了错误。对我来说,问题在于使用Intellij中的DB浏览器插件来可视化和管理表格。断开此工具对数据库的连接解决了问题。因此,请确保没有外部工具连接到数据库并锁定表格。

这至少对我有所帮助。感谢您的回答。 - sumitya

1
在我的情况下,有线程在后台使用sqlite连接,导致了这个错误。
  1. 关闭sqlitebrowser
  2. 关闭electron应用程序(可能需要重新启动)
  3. 重新运行你的程序。

0
对我来说问题在于我打开了太多的会话,所以我将DAO类中的会话字段设置为静态。

0

您打开了另一个包含数据库的应用程序, 尝试关闭该应用程序,然后再次运行您的程序。这对我有用。


此外,在查询后关闭结果集和prepareStatement,或者您可以使用非常有用的“try with resource”。 - Nàñà Yáw Sçrìptûrés

0

感谢 bowman han,我在他的解决方案中添加了一段代码,现在它对我有用了。

private static Connection c = null;
public static Connection connect() throws Exception {

    if (c == null) {
        c = (Connection) DriverManager.getConnection(url);
    } else {
        c.close();
        c = (Connection) DriverManager.getConnection(url);
    }
    return c;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接