JavaFX TableView数据批量存入SQLite遇文件锁错误求助
开发商店管理应用时,新产品先在TableView中展示,再存入数据库。用List<Product>给TableView添加数据显示正常,但调用addBatch()和executeBatch()批量插入SQLite时触发数据库文件锁错误,已经设置autocommit=false。尝试循环插入时,要么数据库无变化,要么同样触发文件锁问题。
批量插入代码
public void validerFacture() throws SQLException { String ajoutProduits = BaseDeDonne.AJOUTER_PRODUITS; Connection connection = null; PreparedStatement ps = null; try { connection = BaseDeDonne.connect(); connection.setAutoCommit(false); ps = connection.prepareStatement(ajoutProduits); for (Produits p : produits) { ps.setInt(1, p.getQuantite()); ps.setString(2, p.getCategorie()); ps.setString(3, p.getTaille()); ps.setString(4, p.getDesignation()); ps.setDouble(5, p.getPrixEnGros()); ps.setDouble(6, p.getPrixUnitaire()); ps.setDouble(7, p.getPrixVente()); ps.setDouble(8, p.getMarge()); ps.addBatch(); } ps.executeBatch(); connection.commit(); } catch (SQLException b) { b.printStackTrace(); } }
插入SQL语句
public static final String AJOUTER_PRODUITS = "INSERT INTO " + TABLE_PRODUIT + "(" + COLUMN_PRODUIT_QUANTITE + "," + COLUMN_PRODUIT_CATEGORIE+ "," + COLUMN_PRODUIT_TAILLE + "," + COLUMN_PRODUIT_DESIGANTION + "," + COLUMN_PRODUIT_PRIX_ACHAT + "," + COLUMN_PRODUIT_PRIX_REVIENT + "," + COLUMN_PRODUIT_PRIX_VENTE + "," + COLUMN_PRODUIT_PRIX_MARGE +")" + " VALUES " + "(" + "?" + "," + "?" + "," + "?" + "," + "?" + "," + "?" + "," + "?" + "," + "?" + "," + "?" + ")";
1. 强制关闭资源,避免锁泄漏
SQLite对连接和语句的资源泄漏极度敏感,必须在finally块中按顺序关闭PreparedStatement和Connection,否则会导致文件锁无法释放。同时出错时要回滚事务:
public void validerFacture() throws SQLException { String ajoutProduits = BaseDeDonne.AJOUTER_PRODUITS; Connection connection = null; PreparedStatement ps = null; try { connection = BaseDeDonne.connect(); connection.setAutoCommit(false); ps = connection.prepareStatement(ajoutProduits); for (Produits p : produits) { ps.setInt(1, p.getQuantite()); ps.setString(2, p.getCategorie()); ps.setString(3, p.getTaille()); ps.setString(4, p.getDesignation()); ps.setDouble(5, p.getPrixEnGros()); ps.setDouble(6, p.getPrixUnitaire()); ps.setDouble(7, p.getPrixVente()); ps.setDouble(8, p.getMarge()); ps.addBatch(); } ps.executeBatch(); connection.commit(); } catch (SQLException b) { if (connection != null) { try { connection.rollback(); // 出错时回滚,避免脏数据 } catch (SQLException e) { e.printStackTrace(); } } b.printStackTrace(); } finally { // 先关Statement,再关Connection,顺序不能乱 if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
2. 拆分大批次为小批量
SQLite单批次操作数量过大时,会延长锁持有时间引发冲突。可以每50条执行一次批次:
int batchSize = 50; int count = 0; for (Produits p : produits) { // 设置参数... ps.addBatch(); count++; if (count % batchSize == 0) { ps.executeBatch(); ps.clearBatch(); // 清空已执行的批次 } } // 执行剩余的未达批次大小的数据 if (count % batchSize != 0) { ps.executeBatch(); }
3. 检查连接获取逻辑
如果BaseDeDonne.connect()返回的是单例连接,或者连接池配置错误,会导致多个操作共享连接引发锁冲突。确保每次获取独立的新连接,或者配置连接池的超时、释放机制。
4. 核对字段映射
检查实体类属性和数据库字段的对应关系:比如代码中ps.setDouble(5, p.getPrixEnGros())对应SQL里的COLUMN_PRODUIT_PRIX_ACHAT,ps.setDouble(6, p.getPrixUnitaire())对应COLUMN_PRODUIT_PRIX_REVIENT,必须完全匹配,否则可能出现插入失败但无异常的情况,导致事务未提交。
5. 排除外部占用
确保没有其他工具(如SQLite浏览器)打开数据库文件,也没有其他线程同时对数据库执行写操作——SQLite的写锁是排他锁,同一时间只能有一个写操作。
内容的提问来源于stack exchange,提问作者aboubacry

