close() was never explicitly called on database 和 database is locked 错误原因

本文分析了在数据库操作中常见的错误原因,并提供了相应的解决办法。重点介绍了如何正确关闭数据库对象,避免出现DatabaseObjectNotClosedException错误。同时,讨论了在多线程环境下防止数据库被锁定的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.cursor 使用完没有关闭。

2.数据库用完没有关闭。

3.数据库重复新建(new了多个对象)。

我的错误是new了多个对象。


LogCat 报错信息:

[java]  view plain copy
  1. 02-13 11:58:13.759: ERROR/Database(432): close() was never explicitly called on database '/data/data/com.yyt.ui/databases/yyt.db'   
  2. 02-13 11:58:13.759: ERROR/Database(432): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here  
  3. 02-13 11:58:13.759: ERROR/Database(432): at android.database.sqlite.SQLiteDatabase.(SQLiteDatabase.java:1847)  
  4. 02-13 11:58:13.759: ERROR/Database(432): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)  
  5. 02-13 11:58:13.759: ERROR/Database(432): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:854)  
  6. 02-13 11:58:13.759: ERROR/Database(432): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:847)  
  7. 02-13 11:58:13.759: ERROR/Database(432): at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:544)  
  8. 02-13 11:58:13.759: ERROR/Database(432): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:203)  
  9. 02-13 11:58:13.759: ERROR/Database(432): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:118)  
  10. 02-13 11:58:13.759: ERROR/Database(432): at com.taobao.htc.db.RecordDBHelper.query(RecordDBHelper.java:49)  
  11. 02-13 11:58:13.759: ERROR/Database(432): at com.taobao.htc.manage.RecordDBopt.get(RecordDBopt.java:78)  
  12. 02-13 11:58:13.759: ERROR/Database(432): at com.taobao.htc.manage.RecordMessage.sendMseeage(RecordMessage.java:111)  
  13. 02-13 11:58:13.759: ERROR/Database(432): at com.taobao.htc.manage.RecordMessage.sendMseeage(RecordMessage.java:87)  
  14. 02-13 11:58:13.759: ERROR/Database(432): at com.taobao.htc.Report.run(Report.java:49)  
  15. 02-13 11:58:13.759: ERROR/Database(432): at java.lang.Thread.run(Thread.java:1019)  
  16. 02-13 11:58:13.830: WARN/ActivityManager(62): Launch timeout has expired, giving up wake lock!  
  17. 02-13 11:58:14.381: DEBUG/dalvikvm(432): GC_CONCURRENT freed 771K, 57% free 2908K/6727K, external 2013K/2137K, paused 7ms+5ms  
  18. 02-13 11:58:14.630: DEBUG/dalvikvm(432): GC_FOR_MALLOC freed 867K, 58% free 2836K/6727K, external 2013K/2137K, paused 178ms  


发生错误原因分析:

主要错误:

[java]  view plain copy
  1. 02-13 11:58:13.759: ERROR/Database(432): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here  


出错处代码

[java]  view plain copy
  1. SQLiteDatabase db = getWritableDatabase();  

 

意思是指在此处打开数据库操作之前,应用程序没有关闭游标或数据库对象。

在应用中,对数据库操作完成后,应该对游标及数据库进行关闭操作

[html]  view plain copy
  1. if (!cur.isClosed()) {  
  2.     cur.close();  
  3. }  
  4. db.close();  


解决办法:

检查操作数据库的相关源代码,增加关闭“cursor or database object”的代码 。


总结一下Activity.startManagingCursor方法:


c=cr.query(Uri.parse(Values.contentA + Values.contentB), null, Values.table_02_name+"="+"\""+str+"\";",
                                null, null);
                startManagingCursor(c);
                c.moveToFirst();


如果操作cursor不是在Activity中,不能用startManagingCursor函数的 

 
1.这个方法使用的前提是:游标结果集里有很多的数据记录。
所以,在使用之前,先对Cursor是否为null进行判断,如果Cursor != null,再使用此方法
 
2.如果使用这个方法,最后也要用stopManagingCursor()来把它停止掉,以免出现错误。
 
3.使用这个方法的目的是把获取的Cursor对象交给Activity管理,这样Cursor的生命周期便能和Activity自动同步,
省去自己手动管理。


——————————————————————————————————我是分割线————————————————————————————————————

而当我们遇到Caused by: android.database.sqlite.SQLiteException: database is locked这个问题时,主要原因大部分都是多线程多数据库进行操作了。这个时候你需要在需要操作数据数据的时候,提供一个统一的入口,并加上同步锁,

我们需要将提供数据库访问的方法设置成同步的,防止异步调用时出现问题,如:

public static synchronized DBConnection getConnection(String connectionName) throws Exception { String pathFile = getPath() + connectionName;// 转换目录data下 return new DBConnection(SQLiteDatabase.openDatabase(pathFile, null, SQLiteDatabase.OPEN_READWRITE)); }

使用synchronized 关键字来修饰获取数据库连接的方法,或者使用 isDbLockedByOtherThreads方法判断数据库是否被锁住了,然后等待一定的时间再进行访问。

或者使用isDbLockedByOtherThreads方法判断数据库是否被锁住了,然后等待一定的时间,并且对于数据库的操作要及时释放,以免多个线程形成死锁


<think>我们正在处理一个Hibernate警告:“Column was never explicitly tracked and isAllColumnsTracked() is false”。这个警告通常与Hibernate的脏检查机制有关,特别是在使用动态更新(dynamic-update)时。Hibernate需要跟踪实体的哪些属性被修改,以便生成只更新这些属性的SQL语句。如果某个列没有被跟踪,Hibernate在更新时就无法知道它是否被修改,因此会发出警告。 原因分析: 1. 实体类中可能没有为所有字段显式定义setter方法,或者Hibernate无法通过反射确定字段的访问状态。 2. 在实体类中使用了字段访问(field access)而不是属性访问(property access),并且某些字段没有被Hibernate跟踪。 3. 可能启用了动态更新(@DynamicUpdate),但是Hibernate无法跟踪所有列的变化。 解决方法: 1. **显式定义setter方法**:确保实体类中每个持久化字段都有对应的setter方法。这样Hibernate可以通过setter方法来跟踪字段的变化。 2. **使用属性访问**:在实体类上使用`@Access(AccessType.PROPERTY)`注解,强制Hibernate通过gettersetter方法访问属性,这样跟踪会更可靠。 3. **检查字段访问策略**:如果使用字段访问(即直接在字段上使用注解),确保所有需要跟踪的字段都被Hibernate管理。注意,final字段可能无法被跟踪。 4. **关闭动态更新**:如果不需要动态更新,可以将`@DynamicUpdate`设置为false,这样Hibernate在更新时会更新所有列,而不会进行脏检查。但这样可能会降低性能,因为即使只有一个字段改变,也会更新所有列。 5. **检查实体继承策略**:如果实体有继承关系,确保父类的字段也被正确跟踪。 另外,Hibernate提供了一个方法`isAllColumnsTracked()`来检查是否所有列都被跟踪。如果返回false,说明有列没有被跟踪,因此我们需要确保所有列都被显式跟踪。 示例代码: 假设有一个实体类`User`,有一个字段`name`没有被跟踪。 错误情况(字段访问,没有setter): ```java @Entity @DynamicUpdate public class User { @Id private Long id; private String name; // 没有setter方法,Hibernate无法跟踪name字段的变化 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } } ``` 修正方法1:添加setter方法 ```java @Entity @DynamicUpdate public class User { @Id private Long id; private String name; // getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 修正方法2:使用属性访问 ```java @Entity @DynamicUpdate @Access(AccessType.PROPERTY) // 使用属性访问 public class User { private Long id; private String name; @Id public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 修正方法3:关闭动态更新(如果不必要) ```java @Entity // 移除@DynamicUpdate,或者设置为false public class User { // ... 字段getter/setter } ``` 如果以上方法都不能解决问题,可能需要检查Hibernate的版本,或者查看是否有其他配置影响了字段的跟踪。 参考:Hibernate官方文档关于脏检查(dirty checking)动态更新的部分[^1]。 §§相关问题§§ 1. Hibernate的脏检查机制是如何工作的? 2. 如何优化Hibernate的更新性能? 3. 在Hibernate中,字段访问属性访问有什么区别?</think>### Hibernate警告“Column was never explicitly tracked and isAllColumnsTracked() is false”的解决方法 此警告通常发生在使用Hibernate的**动态更新(dynamic update)**功能时,表明Hibernate无法正确跟踪实体属性的变更状态。以下是根本原因解决方案: --- #### **根本原因** 1. **字段跟踪缺失** 当实体类中的字段缺少标准的JavaBean setter方法时,Hibernate无法通过反射机制跟踪字段变更状态。例如: ```java // 错误示例:缺少setter方法 @Entity public class Product { @Id private Long id; private String name; // 无setName()方法 } ``` 2. **final字段问题** 被`final`修饰的字段无法被代理类修改,导致跟踪失败: ```java private final String immutableField; // final字段无法跟踪 ``` 3. **字段访问策略冲突** 混合使用字段访问(`@Access(AccessType.FIELD)`)属性访问(`@Access(AccessType.PROPERTY)`)会导致跟踪不一致。 --- #### **解决方案** 1. **添加标准的setter方法** 为所有需要变更跟踪的字段添加setter方法: ```java @Entity public class Product { private String name; // 添加setter方法 public void setName(String name) { this.name = name; } } ``` 2. **统一访问策略** 在实体类上显式声明统一的访问策略(推荐属性访问): ```java @Entity @Access(AccessType.PROPERTY) // 强制使用属性访问 public class Product { ... } ``` 3. **禁用动态更新(备用方案)** 如果不需要动态更新功能,关闭`@DynamicUpdate`: ```java @Entity @DynamicUpdate(false) // 禁用动态更新 public class Product { ... } ``` 4. **避免final字段** 移除实体类中非ID字段的`final`修饰符。 --- #### **验证是否解决** 1. 检查实体类是否包含所有字段的完整setter/getter 2. 确保无`final`修饰的持久化字段 3. 在日志中确认警告消失: ``` Hibernate: update product set name=? where id=? // 动态更新生效 ``` > **性能提示**:动态更新可减少SQL语句的数据量,但需要确保字段被正确跟踪[^1]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值