You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Java/JSP/JDBC操作SQLite为何出现[SQLITE_BUSY]数据库锁定错误

解决SQLite数据库锁定(SQLITE_BUSY)问题

你遇到的org.sqlite.SQLiteException: [SQLITE_BUSY] The database file is locked错误,核心原因是数据库连接与资源未正确释放,再加上SQLite作为单写文件型数据库的特性,一旦连接长期占用就会锁死数据库文件。你的代码存在以下几个关键问题:

  • 静态Connection导致连接泄漏:UserDao中的conn是静态变量,多请求场景下会被共用,且连接管理逻辑混乱,极易导致连接未释放、数据库被持续占用。
  • 资源未自动关闭:findUser和addUser方法中仅关闭了ResultSet,PreparedStatement和Connection未被正确关闭,资源泄漏引发数据库锁定。
  • JSP未处理连接关闭:调用连接后未在finally块中释放连接,连接长期持有导致数据库文件锁死。

第一步:重构UserDao,正确管理连接与资源

移除静态Connection,每个操作单独获取连接,并用try-with-resources自动关闭所有资源(Connection、PreparedStatement、ResultSet),确保每次操作后释放数据库连接。

修改后的UserDao代码:

public class UserDao {
    private String table;
    private final String DB_URL = "jdbc:sqlite:c:/Users/ryo/database.db";

    public UserDao(String table) {
        this.table = table;
    }

    // 私有方法:获取数据库连接
    private Connection getConnection() throws Exception {
        Class.forName("org.sqlite.JDBC");
        return DriverManager.getConnection(DB_URL);
    }

    public User findUser(String username) {
        // try-with-resources自动关闭所有资源
        try (Connection conn = getConnection();
             PreparedStatement pStmt = conn.prepareStatement("SELECT * FROM " + table + " WHERE Username=?")) {
            pStmt.setString(1, username);
            try (ResultSet rs = pStmt.executeQuery()) {
                if (rs.next()) {
                    return new User(
                            rs.getString("Name"),
                            rs.getString("Username"),
                            rs.getString("Password"),
                            rs.getBoolean("Admin")
                    );
                }
            }
        } catch (Exception e) {
            System.out.println("ERROR FINDING USER");
            e.printStackTrace();
        }
        return null;
    }

    public long addUser(User user, String password) {
        try (Connection conn = getConnection();
             // 指定RETURN_GENERATED_KEYS获取自增ID
             PreparedStatement pStmt = conn.prepareStatement(
                     "INSERT INTO " + table + " (Username, Name, Password, Admin) VALUES(?,?,?,?)",
                     Statement.RETURN_GENERATED_KEYS)) {
            pStmt.setString(1, user.getUsername());
            pStmt.setString(2, user.getName());
            pStmt.setString(3, password);
            pStmt.setBoolean(4, user.isAdmin());
            int rowsAdded = pStmt.executeUpdate();
            if (rowsAdded == 1) {
                try (ResultSet rs = pStmt.getGeneratedKeys()) {
                    if (rs.next()) {
                        return rs.getLong(1);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("ERROR ADDING USER");
            e.printStackTrace();
        }
        return 0L;
    }
}

第二步:修改JSP代码,简化逻辑并确保资源释放

移除手动调用的UserDao.connect(),因为重构后的UserDao会自行管理连接,同时简化异常处理逻辑:

修改后的JSP代码片段:

<body>
    <%
        String name = request.getParameter("name");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User toAdd = new User(name, username, password, false);
        User existingUser = null;
        boolean userCreated = false;
        try {
            UserDao dao = new UserDao("Users");
            existingUser = dao.findUser(username);
            if (existingUser == null) {
                dao.addUser(toAdd, password);
                userCreated = true;
            }
        } catch (Exception e) {
            out.println("Error: " + e.getMessage() + "<br>");
            e.printStackTrace();
        }
    %>
    <% if(userCreated) { %>
        <p>User created successfully!<br>Now try logging in!</p>
        <a href="./login.html">
            <input type="button" value="Login!" />
        </a>
    <% } else { %>
        <p>User failed to be created! Already exists.<br>Please try again.</p>
        <a href="./signup.html">
            <input type="button" value="Signup!" />
        </a>
    <% } %>
</body>

额外注意事项

  • 驱动版本:确保使用最新版的sqlite-jdbc驱动,旧版本可能存在连接管理的bug。
  • 并发场景:如果网站存在大量并发注册请求,SQLite的单写特性可能仍会偶尔触发锁定,可添加重试逻辑或改用支持多写的数据库(如MySQL)。

内容的提问来源于stack exchange,提问作者Reina297

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:26:15