使用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
相关产品推荐
相关产品推荐

