1. 程式人生 > >SQLiteException: database is locked異常的解決辦法

SQLiteException: database is locked異常的解決辦法

原理:檔案資料庫sqlite,同一時刻允許多個程序/執行緒讀,但同一時刻只允許一個執行緒寫。在操行寫操作時,資料庫檔案被瑣定,此時任何其他讀/寫操作都被阻塞,如果阻塞超過5秒鐘(預設是5秒,能過重新編譯sqlite可以修改超時時間),就報"database is locked"錯誤。

所以,在操作sqlite時,應該即時關閉連線;開啟連線後,儘量減少非常費時的操作。

1. 方法

在頁面中用到了ViewPager控制元件,ViewPager中的內容分別是兩個ListView,兩個ListView的資料都來自本地資料庫(先從網路下載資料,然後更新本地資料庫),在實際的使用過程中發現會出現SQLiteDatabaseLockedException: database is locked的問題。

經網上搜索資料,發現是讀寫資料庫時存在的同步問題,所以採用單例+同步鎖的方法,並且在每次資料庫操作後都關閉資料庫,經測試後發現沒有在出現上述問題。

以下是兩個主類

DBHelper.java(這個類用來管理資料庫)

public class DBHelper extends SQLiteOpenHelper {

	private final String TAG = this.getClass().getSimpleName();
	
	public final static String DATABASE_NAME = "test.db";
	public final static String TABLE = "table";
	public final static int DATABASE_VERSION = 2;

	public DBHelper(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	private static DBHelper mInstance;

	public synchronized static DBHelper getInstance(Context context) {
		if (mInstance == null) {
			mInstance = new DBHelper(context);
		}
		return mInstance;
	};

	@Override
	public void onCreate(SQLiteDatabase db) {
		try {
			db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE
					+ "(id INTEGER PRIMARY KEY ,data BLOB)");
		} catch (SQLiteException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {	
		
		// 刪除原來的資料表
		db.execSQL("DROP TABLE IF EXISTS " + TABLE);
		// 重新建立
		onCreate(db);
	}

	public static byte[] objectToBytes(Object obj) throws Exception {

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ObjectOutputStream sOut = new ObjectOutputStream(out);
		sOut.writeObject(obj);
		sOut.flush();
		byte[] bytes = out.toByteArray();
		return bytes;
	}

	public static Object bytesToObject(byte[] bytes) throws Exception {

		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		ObjectInputStream sIn = new ObjectInputStream(in);
		return sIn.readObject();
	}
}

DBStudentManager類(這裡可以定義自己的管理類)

public class DBStudentManager {

	private DBHelper helper;
	private SQLiteDatabase db;

	public DBStudentManager(Context context) {
		helper = DBHelper.getInstance(context);
		db = helper.getWritableDatabase();
	}

	// 插入
	private void insert(Student student) {

		synchronized (helper) {
			// 看資料庫是否關閉
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			// 開始事務
			db.beginTransaction();
			try {
				db.execSQL(
						"INSERT INTO " + DBHelper.TABLE + " VALUES(?,?)",
						new Object[] { student.mID,
								DBHelper.objectToBytes(student) });
				db.setTransactionSuccessful(); // 設定事務成功完成
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 更新
	private void update(Student student) {

		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			db.beginTransaction();
			try {
				db.execSQL("UPDATE " + DBHelper.TABLE
						+ "SET data = ? WHERE id = ?",
						new Object[] { DBHelper.objectToBytes(student),
								student.mID });
				db.setTransactionSuccessful(); // 設定事務成功完成
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 同步
	public void synchronous(List<Student> students) {
		if (students == null) {
			return;
		}
		for (Student student : students) {
			if (query(student.mID) == null) {
				insert(student);
			} else {
				update(student);
			}
		}
	}

	// 刪除指定資料
	public void delete(String id) {

		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			db.beginTransaction();
			try {
				db.execSQL("DELETE FROM " + DBHelper.TABLE + " WHERE id = ? ",
						new String[] { id });
				db.setTransactionSuccessful();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 刪除所有資料
	public void delete() {

		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			db.beginTransaction();
			try {
				db.execSQL("DELETE * FROM " + DBHelper.TABLE);
				db.setTransactionSuccessful();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
				db.close();
			}
		}
	}

	// 查詢所有的Students
	public List<Student> query() {

		List<Student> students = new ArrayList<Student>();
		synchronized (helper) {
			if (!db.isOpen()) {
				db = helper.getWritableDatabase();
			}
			Cursor c = queryTheCursor();
			Student student = null;
			try {
				while (c.moveToNext()) {

					byte[] bytes = c.getBlob((c.getColumnIndex("data")));
					student = (Student) DBHelper.bytesToObject(bytes);
					students.add(student);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				c.close();
			}
		}
		return students;
	}

	// 查詢指定ID的Student
	public Student query(String id) {

		Student student = null;
		synchronized (helper) {
			if (!db.isOpen()) {
				helper.getWritableDatabase();
			}
			Cursor c = queryTheCursor(id);
			try {
				while (c.moveToNext()) {

					byte[] bytes = c.getBlob((c.getColumnIndex("data")));
					student = (Student) DBHelper.bytesToObject(bytes);
					break;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				c.close();
			}
		}
		return student;
	}

	// 獲取遊標
	public Cursor queryTheCursor(String id) {
		Cursor c = db.rawQuery("SELECT FROM " + DBHelper.TABLE
				+ " WHERE id = ?", new String[] { id });
		return c;
	}

	// 獲取遊標
	public Cursor queryTheCursor() {
		Cursor c = db.rawQuery("SELECT * FROM " + DBHelper.TABLE);
		return c;
	}

	class Student {

		String mID;
		String mName;
		int mAge;
	}
}


 2. 方法

遇到這個問題,有幾種可能,我在下面詳細的列出來,方便大家在遇到的時候檢視

· 多執行緒訪問造成的資料庫鎖定。

如A執行緒在訪問當前的資料庫,這時候B執行緒也需要訪問資料庫,這樣在B執行緒中,就會有類似以上的異常產生,我們需要將提供資料庫訪問的方法設定成同步的,防止非同步呼叫時出現問題,如:

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方法判斷資料庫是否被鎖住了,然後等待一定的時間再進行訪問。 ·sqlite自身的問題 有時我們會在除錯程式的時候發現Log控制檯頻繁的出現上述異常,而在執行程式的時候就沒有這個問題,這種現象我在除錯ResultSet時也會出現,查資料找原因是因為sqlite資料不完全是執行緒安全的,導致在一些地方會莫名其妙的出問題,如果遇到這樣的情況,那隻能不要將斷點打到資料庫連線的地方了。

3. 方法

如果多執行緒同時讀寫(這裡的指不同的執行緒用使用的是不同的Helper例項),後面的就會遇到android.database.sqlite.SQLiteException: database is locked這樣的異常。
對於這樣的問題,解決的辦法就是keep single sqlite connection保持單個SqliteOpenHelper例項,同時對所有資料庫操作的方法新增synchronized關鍵字。

完美解決sqlite的 database locked 或者是 error 5: database locked 問題

相關推薦

SQLiteException: database is locked異常解決辦法

原理:檔案資料庫sqlite,同一時刻允許多個程序/執行緒讀,但同一時刻只允許一個執行緒寫。在操行寫操作時,資料庫檔案被瑣定,此時任何其他讀/寫操作都被阻塞,如果阻塞超過5秒鐘(預設是5秒,能過重新編譯sqlite可以修改超時時間),就報"database is lo

Yum database disk image is malformed 錯誤 解決辦法

yumyum install xxx 時出現: database disk image is malformed解決辦法:yum clean dbcache Yum database disk image is malformed 錯誤 解決辦法

dpkg: error: dpkg status database is locked by another process 解決方法

http other cor 系統監視 pen figure 操作 body lis https://i.cnblogs.com/EditPosts.aspx?opt=1使用dpkg -i/apt命令安裝,報錯: ------------------------------

解決嵌入式使用SQL出現 Error(5): database is locked

之前有遇到通過sqlite3_exec()函式操作資料庫出現 database is locked 這樣的一個問題,特別的記錄一下這個問題。 SQLite,是一款輕型的資料庫,它的設計目標是嵌入式的,而

解決django的sqlite3的database is locked

今天在用admin後臺新增一篇部落格,最後居然報錯:DatabaseError at  ... database is locked, 因為我的資料庫使用的是sqlite3,所以不支援大量的訪問是有可能的,但是目前僅僅我一個人訪問的話,居然報錯,我就納悶了,用google搜

@RequestBody物件為空,異常Required request body is missing的解決辦法

 由於與前端互動的過程當中,都是用json資料與前端進行互動,這樣想取出整個傳送過來的json資料的時候,就需要用到@RequestBody這個註解,前端傳送資料的格式如下://測試傳送json資料 $

ios FMDB多執行緒"is currently in use", "database is locked"問題的解決

is currently in use” 出現的場景是這樣的,多執行緒操作資料庫,每個執行緒都使用了FMDatabase例項 FMDatabase是不具備執行緒安全的,如果兩個執行緒中同時操作資料庫,

不完全解決sqlite遇到database is locked問題方法

這兩天在專案中用大強度大頻率的方法測試時遇到sqlite報database is locked的問題, 分析下來原因是sqlite對資料庫做修改操作時會做(檔案)鎖使得其它程序同一時間使用時會報該錯誤(也就是SQLITE_BUSY),但如果僅是多程序或多執行緒查詢sqlite是支援的。解決方法有:1。使

TortoiseSVN提交提示423 Locked解決辦法

提交 locks mit 4.2 pdf tortoise 擁有 release ise 此辦法是閱讀官方文檔(TortoiseSVN-1.6.16-zh_CN.pdf) 4.21 鎖部分提供的辦法: 首先選擇選擇要提交的文件右鍵菜單:“To

eclipse報jvm terminated.exitcode=2異常解決辦法

不用 oracl system data eclips jre lib acl bsp 如上圖,我原來安裝的是jdk1.7,現在要用到jdk1.8,所以我又安裝了個jdk1.8,安裝jdk1.8後,打開eclipse就提示這樣的信息,上網查了一下說是環境變量path需要修

svn和NetBeans一起使用造成svn老是死鎖, database is locked

beans 就是 base clear 所有 編譯 bean post net 其實我一直沒發現,又一次我以為需要寫些前端吧NetBeans關了,使用HBuilder寫vue,因為內存只有8G,亂七八糟的一些東西一開,只剩20%左右,我就把 NetBeans關了,發現更新s

Keil4 一例異常解決辦法

via .com main.c 自己的 異常 提示 一個 image syn 以下幾個問題在Keil5上沒有發現,不過因為下載的例子都是Keil4,強迫癥讓我用起了Keil4 錯誤一:main.c: Warning: C3910W: Old syntax, please

報錯--dpkg status database is locked

報錯 dpkg status databas dpkg: error: dpkg status database is locked by another process一、可能是軟件中心,關掉就好了,或者打開‵系統監視器‵,在進程裏面把帶有apt, dpkg 字樣的進程 kill掉就行了。二、也可

Pycharm中安裝package出現microsoft visual c++ 14.0 is required問題解決辦法

twisted mina 信息 根據 錯誤 nta 他會 窗口 8.4 在利用pycharm安裝scrapy包是遇到了挺多的問題。在折騰了差不多折騰了兩個小時之後總算是安裝好了。期間各種谷歌和百度,發現所有的教程都是利用命令行窗口安裝的。發現安裝scrapy需要的包真是多的

testNg 關閉瀏覽器異常解決辦法

ask sed kill 系統文件 process sys after 環境 lips 執行testNg關閉瀏覽器方法一直報錯,報錯信息如下:Process refused to die after 10 seconds, and couldn‘t taskkill it

Redis安裝異常解決辦法

命令 alloc error: 說明 文件中 密碼登錄 from nload size $ wget http://download.redis.io/releases/redis-4.0.11.tar.gz $ tar xzf redis-4.0.11.tar.gz $

python學習(5)--網易郵箱發郵件異常解決辦法

python發郵件 解決辦法如下 nbsp -- bsp err error 發郵件 png 在用python發郵件時,發現用網易郵箱作為發件人。會出現以下異常: smtplib.SMTPDataError: (554, b‘DT:SPM 163 smtp12,EMCo

spark/java連線 kudu incompatible RPC? Error is: step 異常解決

如果是新搭的CDH 叢集並使用預設的kudu設定, 使用spark /java 連線kudu時候,一般都會碰到此類異常。 程式: val masteraddr = "kudumasterip:7051" //建立kudu的資料庫連結 val client = new KuduClient.Kudu

Linux——重啟防火牆時出現Failed to start firewalld.service: Unit is masked的解決辦法

報錯如下: 這是因為程序被鎖住了。 對它進行解鎖就可以了。 執行命令,即可實現取消服務的鎖定 # systemctl unmask firewalld 我們試一試 好了,現在防火牆已經重新啟動了。  注:下次需要鎖定該服務時可以輸入以下命令列執行

按需載入iviei元件時,引入vue專案報iview is not defined解決辦法(使用vue-vli + iview)

解決方法 npm 下載(我使用了淘寶映象): cnpm install iview --save cnpm install babel-plugin-import --save-dev .babelrc檔案 { "presets": [ ["env", {