sqlite database lock problem in android content providers

本文描述了Android Content Provider实现时,sqlite数据库出现lock问题的大致原因和解决思路;

虽然最终可以归结为线程同步和资源竞争问题,但是对于trouble shooting却并非如此简单.

[Symptom]
From the logcat:
08-22 22:32:30.499 475 1692 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 25. Continuing to wait in thread: 30
08-22 22:32:32.850 475 1836 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 25. Continuing to wait in thread: 31
08-22 22:32:44.000 475 1059 E SqliteCursor.cpp: Bailing on database busy retry
08-22 22:32:44.000 475 1059 E SQLiteQuery: exception: database is locked: retrycount exceeded; query: SELECT storage_id FROM files WHERE (_data=?)
08-22 22:32:44.000 475 1059 E DatabaseUtils: Writing exception to parcel
08-22 22:32:44.000 475 1059 E DatabaseUtils: android.database.sqlite.SQLiteDatabaseLockedException: database is locked: retrycount exceeded
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.nativeFillWindow(Native Method)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:86)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.database.CursorToBulkCursorAdaptor.count(CursorToBulkCursorAdaptor.java:184)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:117)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at android.os.Binder.execTransact(Binder.java:338)
08-22 22:32:44.000 475 1059 E DatabaseUtils: at dalvik.system.NativeStart.run(Native Method)
..........................................
08-22 22:33:36.420 475 1836 E SqliteCursor.cpp: Bailing on database busy retry
08-22 22:33:36.420 475 1836 E SQLiteQuery: exception: database is locked: retrycount exceeded; query: SELECT _id FROM files WHERE (_data=?)
08-22 22:33:36.430 475 1836 E DatabaseUtils: Writing exception to parcel
08-22 22:33:36.430 475 1836 E DatabaseUtils: android.database.sqlite.SQLiteDatabaseLockedException: database is locked: retrycount exceeded
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.nativeFillWindow(Native Method)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:86)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.database.CursorToBulkCursorAdaptor.count(CursorToBulkCursorAdaptor.java:184)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:117)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at android.os.Binder.execTransact(Binder.java:338)
08-22 22:33:36.430 475 1836 E DatabaseUtils: at dalvik.system.NativeStart.run(Native Method)
..........................................
08-22 22:34:06.639 475 1059 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 30. Continuing to wait in thread: 25
08-22 22:34:28.630 475 1692 E SqliteCursor.cpp: Bailing on database busy retry
08-22 22:34:28.630 475 1692 E SQLiteQuery: exception: database is locked: retrycount exceeded; query: SELECT _id FROM images
08-22 22:34:28.640 475 1692 E MediaScanner: SQLException in MediaScanner.scan()
08-22 22:34:28.640 475 1692 E MediaScanner: android.database.sqlite.SQLiteDatabaseLockedException: database is locked: retrycount exceeded
08-22 22:34:28.640 475 1692 E MediaScanner: at android.database.sqlite.SQLiteQuery.nativeFillWindow(Native Method)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:86)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.media.MediaScanner.prescan(MediaScanner.java:1132)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.media.MediaScanner.scanDirectories(MediaScanner.java:1315)
08-22 22:34:28.640 475 1692 E MediaScanner: at com.android.providers.media.MediaScannerService.scan(MediaScannerService.java:111)
08-22 22:34:28.640 475 1692 E MediaScanner: at com.android.providers.media.MediaScannerService.access$300(MediaScannerService.java:49)
08-22 22:34:28.640 475 1692 E MediaScanner: at com.android.providers.media.MediaScannerService$ServiceHandler.handleMessage(MediaScannerService.java:271)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.os.Handler.dispatchMessage(Handler.java:99)
08-22 22:34:28.640 475 1692 E MediaScanner: at android.os.Looper.loop(Looper.java:137)
08-22 22:34:28.640 475 1692 E MediaScanner: at com.android.providers.media.MediaScannerService.run(MediaScannerService.java:193)
08-22 22:34:28.640 475 1692 E MediaScanner: at java.lang.Thread.run(Thread.java:856)
08-22 22:34:28.640 475 1692 D MediaProvider: [MediaScannerService] broadcast ACTION_MEDIA_SCANNER_FINISHED with file:///mnt/sdcard
..........................................
08-22 22:34:58.719 475 1692 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 25. Continuing to wait in thread: 30
08-22 22:34:58.969 475 1836 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 25. Continuing to wait in thread: 31
..................
08-22 22:35:22.369 475 1059 E SqliteCursor.cpp: Bailing on database busy retry
08-22 22:35:22.369 475 1059 E SQLiteQuery: exception: database is locked: retrycount exceeded; query: SELECT _id FROM files WHERE (_data=?)
08-22 22:35:22.369 475 1059 E DatabaseUtils: Writing exception to parcel
08-22 22:35:22.369 475 1059 E DatabaseUtils: android.database.sqlite.SQLiteDatabaseLockedException: database is locked: retrycount exceeded
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.nativeFillWindow(Native Method)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:86)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.database.CursorToBulkCursorAdaptor.count(CursorToBulkCursorAdaptor.java:184)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:117)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at android.os.Binder.execTransact(Binder.java:338)
08-22 22:35:22.369 475 1059 E DatabaseUtils: at dalvik.system.NativeStart.run(Native Method)
..........................................
08-22 22:35:52.380 475 1692 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 31. Continuing to wait in thread: 30
08-22 22:35:52.949 475 486 W SQLiteDatabase: database lock has not been available for 30 sec. Current Owner of the lock is 31. Continuing to wait in thread: 19
..........................................
08-22 22:36:15.209 475 1836 E SqliteCursor.cpp: Bailing on database busy retry
08-22 22:36:15.209 475 1836 E SQLiteQuery: exception: database is locked: retrycount exceeded; query: SELECT _id,date_modified,_data,artist_id FROM audio ORDER BY _id
08-22 22:36:15.209 475 1836 E DatabaseUtils: Writing exception to parcel
08-22 22:36:15.209 475 1836 E DatabaseUtils: android.database.sqlite.SQLiteDatabaseLockedException: database is locked: retrycount exceeded
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.nativeFillWindow(Native Method)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:86)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.database.CursorToBulkCursorAdaptor.count(CursorToBulkCursorAdaptor.java:184)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:117)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at android.os.Binder.execTransact(Binder.java:338)
08-22 22:36:15.209 475 1836 E DatabaseUtils: at dalvik.system.NativeStart.run(Native Method)

..........................................
08-22 22:36:15.229 2036 2049 W dalvikvm: threadid=11: thread exiting with uncaught exception (group=0x40adb9f0)
08-22 22:36:15.229 2036 2049 E test : Exception
08-22 22:36:15.229 2036 2049 E AndroidRuntime: FATAL EXCEPTION: pool-1-thread-1
08-22 22:36:15.229 2036 2049 E AndroidRuntime: android.database.sqlite.SQLiteException: database is locked: retrycount exceeded
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:180)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:136)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at android.content.ContentProviderProxy.query(ContentProviderNative.java:358)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at android.content.ContentResolver.query(ContentResolver.java:311)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at com.xxxx.mediaextra.provider.MediaExtraUpdater.getAudioInfoListFromMediaDb(MediaExtraUpdater.java:629)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at com.xxxx.mediaextra.provider.MediaExtraUpdater.access$500(MediaExtraUpdater.java:61)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at com.xxxx.mediaextra.provider.MediaExtraUpdater$2.run(MediaExtraUpdater.java:200)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
08-22 22:36:15.229 2036 2049 E AndroidRuntime: at java.lang.Thread.run(Thread.java:856)
08-22 22:36:15.249 258 593 W ActivityManager: sendCrashtoMTBF!!

[Reason]
1. The Provider-use side
The App with pid 2049 uses ContentResolver proxy to query Audio Info and gets an exception string in the reply parcel from the Provider-Impl side.
The following line output by pid 2049
08-22 22:36:15.229 2036 2049 E AndroidRuntime: android.database.sqlite.SQLiteException: database is locked: retrycount exceeded,is the EXCEPTION interpretion string.

The call realtion throw the exception.
query(...) => readExceptionFromParcel(String reply) => readExceptionFromParcel(Parcel reply,String msg,int code)

326 public Cursor ContentResolver::query(Uri url,String[] projection,String selection,
327 String[] selectionArgs,String sortOrder) throws RemoteException {
328 BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
..............
353 data.writeString(sortOrder);
354 data.writeStrongBinder(adaptor.getObserver().asBinder());
355
356 mRemote.transact(IContentProvider.QUERY_TRANSACTION,data,reply,0);
357
358 DatabaseUtils.readExceptionFromParcel(reply);
359
..............
}

163 private static final void DatabaseUtils::readExceptionFromParcel(Parcel reply,int code) {
164 switch (code) {
165 case 2:
166 throw new IllegalArgumentException(msg);
167 case 3:
168 throw new UnsupportedOperationException(msg);
169 case 4:
170 throw new SQLiteAbortException(msg);
171 case 5:
172 throw new SQLiteConstraintException(msg);
173 case 6:
174 throw new SQLiteDatabaseCorruptException(msg);
175 case 7:
176 throw new SQLiteFullException(msg);
177 case 8:
178 throw new SQLiteDiskIOException(msg);
179 case 9:
180 throw new SQLiteException(msg);
181 default:
182 reply.readException(code,msg);
183 }
184 }

2. The Provider-Implemention side.
The Binder Thread #x series are 1692,1836,1059 and 486. Worker thread with a small pid value 486 may be one main worker thread.
When one thread wants to modify the database,it acquires and holds a lock on the SQLiteDatabase.

The first two-line like log is printed in SQLiteDatabase::lock@SQLiteDatabase.java,when the binder worker threads do while-try to get lock on the SQLiteDatabase::mLock.

407 private static final long LOCK_WAIT_PERIOD = 30L;
408 private void SQLiteDatabase::lock(String sql,boolean forced) {
409 // make sure this method is NOT being called from a 'synchronized' method
410 if (Thread.holdsLock(this)) {
411 Log.w(TAG,"don't lock() while in a synchronized method");
412 }
413 verifyDbIsOpen();
414 if (!forced && !mLockingEnabled) return;
415 boolean done = false;
416 long timeStart = SystemClock.uptimeMillis();
417 while (!done) {
418 try {
419 // try to acquire the lock within 30sec
420 done = mLock.tryLock(LOCK_WAIT_PERIOD,TimeUnit.SECONDS);
421 if (!done) {
422 // lock not acquired in NSec. print a message and stacktrace saying the lock
423 // has not been available for 30sec.
424 Log.w(TAG,"database lock has not been available for " + LOCK_WAIT_PERIOD +
425 " sec. Current Owner of the lock is " + mLock.getOwnerDescription() +
426 ". Continuing to wait in thread: " + Thread.currentThread().getId());
427 }
428 } catch (InterruptedException e) {
429 // ignore the interruption
430 }
431 }
432 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
433 if (mLock.getHoldCount() == 1) {
434 // Use elapsed real-time since the CPU may sleep when waiting for IO
435 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
436 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
437 }
438 }
439 if (sql != null) {
440 if (ENABLE_DB_SAMPLE) {
441 logTimeStat(sql,timeStart,GET_LOCK_LOG_PREFIX);
442 }
443 }
444 }

The claim of Bailing on database busy is output in nativeFillWindow@android_database_SQLiteQuery.cpp
If one process tries to access a sqlite file that is locked by another process,the default behavior is to return SQLITE_BUSY.
And if the table in the database is locked by another process,SQLITE_LOCKED is returned.
22 public static final int SQLITE_BUSY = 5; /* The database file is locked */
23 public static final int SQLITE_LOCKED = 6; /* A table in the database is locked */

The code of nativeFillWindow@android_database_SQLiteQuery.cpp list as follows.

38 static jint nativeFillWindow(JNIEnv* env,jclass clazz,jint databasePtr,
39 jint statementPtr,jint windowPtr,jint startPos,jint offsetParam) {
40 sqlite3* database = reinterpret_cast<sqlite3*>(databasePtr);
41 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
42 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);

59 // We assume numRows is initially 0.
60 LOG_WINDOW("Window: numRows = %d,size = %d,freeSpace = %d",
61 window->getNumRows(),window->size(),window->freeSpace());
62
63 int numColumns = sqlite3_column_count(statement);
64 status_t status = window->setNumColumns(numColumns);
65 if (status) {
66 LOGE("Failed to change column count from %d to %d",window->getNumColumns(),numColumns);
67 jniThrowException(env,"java/lang/IllegalStateException","numColumns mismatch");
68 return 0;
69 }
70
71 int retryCount = 0;
72 int totalRows = 0;
73 int addedRows = 0;
74 bool windowFull = false;
75 bool gotException = false;
76 const bool countAllRows = (startPos == 0); // when startPos is 0,we count all rows
77 while (!gotException && (!windowFull || countAllRows)) {
78 int err = sqlite3_step(statement);
79 if (err == SQLITE_ROW) {
80 LOG_WINDOW("Stepped statement %p to row %d",statement,totalRows);

} else if (err == SQLITE_DONE) {
182 // All rows processed,bail
183 LOG_WINDOW("Processed all rows");
184 break;
185 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
186 // The table is locked,retry
187 LOG_WINDOW("Database locked,retrying");
188 if (retryCount > 50) {
189 LOGE("Bailing on database busy retry");
190 throw_sqlite3_exception(env,database,"retrycount exceeded");
191 gotException = true;
192 } else {
193 // Sleep to give the thread holding the lock a chance to finish
194 usleep(1000);
195 retryCount++;
196 }
197 } else {
198 throw_sqlite3_exception(env,database);
199 gotException = true;
200 }
201 }
202
203 LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
204 "to the window in %d bytes",
205 statement,totalRows,addedRows,window->size() - window->freeSpace());
206 sqlite3_reset(statement);
207
208 // Report the total number of rows on request.
209 if (startPos > totalRows) {
210 LOGE("startPos %d > actual rows %d",startPos,totalRows);
211 }
212 return countAllRows ? totalRows : 0;
213}

When the binder worker thread succeed in acquiring the SQLiteDatabase::mLock in while-try loop,it uses jni nativeFillWindow() via sqlite api to query sqlite and fill the CursorWindow;
If the table or the file is locked by another process,it makes a 50-times try. If retrycount exceeded,the thread Bails on database busy and exception string is passed to the client.

[Conclusion]

So,the reason is that the SQLiteDatabase high-level lock doesn't well protect the database access,and threads get into sqlite-level lock problem.
It is a sqlite access competition issue.

Why the thread has acquired SQLiteDatabase::mLock BUT encounters to sqlite-level's lcok problem.
There may be three reasons,
a. The threads use different SQLiteDatabase instance(should be singleton),so not the sole SQLiteDatabase::mLock.
b. Some thread forgets closing the sqlite and releasing sqlite lock whenit leaves.
c. Some thread is in a long time wasted sqlite batch transaction.

For one table or file-db,it is recommended to use one SQLiteDatabase instance which uses its lock to protect the db.
And pay attention to the comment "// make sure this method is NOT being called from a 'synchronized' method" in SQLiteDatabase::lock().

Pls check com.xxxx.ppppp.provider.MediaExtraUpdater's code; For further investigating,com.xxxx.metadatacleanup source code is needed.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


SQLite架构简单,又有Json计算能力,有时会承担Json文件/RESTful的计算功能,但SQLite不能直接解析Json文件/RESTful,需要用Java代码硬写,或借助第三方类库,最后再拼成insert语句插入数据表,代码非常繁琐,这里就不展示了。参考前面的代码可知,入库的过程比较麻烦,不能只用SQL,还要借助Java或命令行。SPL是现代的数据计算语言,属于简化的面向对象的语言风格,有对象的概念,可以用点号访问属性并进行多步骤计算,但没有继承重载这些内容,不算彻底的面向对象语言。...
使用Python操作内置数据库SQLite以及MySQL数据库。
破解微信数据库密码,用python导出微信聊天记录
(Unity)SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。本教程将告诉您如何使用 SQLite 编程,并让你迅速上手。.................................
安卓开发,利用SQLite实现登陆注册功能
相比大多数数据库而言,具有等优势,广泛应用于、等领域。
有时候,一个项目只有一个数据库,比如只有SQLite,或者MySQL数据库,那么我们只需要使用一个固定的数据库即可。但是一个项目如果写好了,有多个用户使用,但是多个用户使用不同的数据库,这个时候,我们就需要把软件设计成可以连接多个数据库的模式,用什么数据库,就配置什么数据库即可。4.Users实体类,这个实体类要和数据库一样的,形成一一对应的关系。11.Sqlite数据库,需要在代码里面创建数据库,建立表,再建立数据。8.我们开启MySQL数据库,然后进行调试,看程序的结果。2.安装SqlSugar。
基于Android的背单词软件,功能强大完整。
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统。说白了就是使用起来轻便简单,
Android的简单购物车案例
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库...
Qt设计较为美观好看的登录注册界面(包含SQLite数据库以及TCP通信的应用)
SQLite是用C语言开发的跨平台小型数据库,可嵌入其他开发语言,也可在单机执行。SPL是用Java开发的跨平台的数据计算语言,可嵌入Java,可在单机执行,可以数据计算服务的形式被远程调用。两者的代码都是解释执行的。...
新建库.openDATA_BASE;新建表createtableLIST_NAME(DATA);语法:NAME关键字...<用逗号分割>删除表droptableNAME;查看表.schema查看表信息新建数据insertintoLIST_NAMEvalues();语法:CLASS,PARAMETER...,CLASS是类别,PARAMETER是参数<用逗号分割新建的
importsqlite3classDemo01:def__init__(self):self.conn=sqlite3.connect("sql_demo_001.db")self.cursor1=self.conn.cursor()self.cursor1.execute("select*fromtable_001wherename=?andid=?",('ssss&#0
 在客户端配置文件<configuration>节点下,添加:<connectionStrings>      <add name="localdb" connectionString="Data Source=config/local.db;Version=3;UseUTF16Encoding=True;" providerName="System.Data.SQLite.SQLiteFactory"/&g
提到锁就不得不说到死锁的问题,而SQLite也可能出现死锁。下面举个例子:连接1:BEGIN(UNLOCKED)连接1:SELECT...(SHARED)连接1:INSERT...(RESERVED)连接2:BEGIN(UNLOCKED)连接2:SELECT...(SHARED)连接1:COMMIT(PENDING,尝试获取EXCLUSIVE锁,但还有SHARED锁未释放,返回SQLITE_BUSY)连接2:INSERT...
SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。在使用SQLite前,我们先要搞清楚几个概念:表
设计思想————首先要确定有几个页面、和每个页面的大致布局由于是入门,我也是学习了不是很长的时间,所以项目比较low。。。。第一个页面,也就是打开APP的首页面:今天这个博客,先实现添加功能!:首先对主界面进行布局:其中activity_main.xml的代码为<?xmlversion="1.0"encoding="