2014-10-15 17:46:34 chenzhiqin20 阅读数 4512
产生数据库锁的原因,就是因为多个SQLiteOpenHelper,多数据库操作,导致读写竞争。数据库被锁住

1 对一个数据库操作只创建1个SQLiteOpenHelper。最好保存一个全局的SQLiteOpenHelper,用单例实现。退出进程的时候,再清空单例
2 多进程操作数据库,使用ContentProvider,且声明成android:multiprocess="false"。多进程单实例。否则在2.3等低端机器上,还是可能会出现数据库锁的问题。
3 在单进程的情况下,使用greeddao,不会参数数据库锁的问题。因为只使用了1个SQLiteOpenHelper。
   当在多进程的情况下,还是会被锁住,还是得使用ContentProvider。见2.
2019-01-10 14:29:43 u010479969 阅读数 531

最近在项目中遇到一个错误:

12-26 17:29:10.501 32592 32644 W SQLiteConnectionPool: The connection pool for database '***.db' has been unable to grant a connection to thread 1378 (thread-pool-0) with flags 0x2 for 60.038002 seconds.
12-26 17:29:10.501 32592 32644 W SQLiteConnectionPool: Connections: 0 active, 1 idle, 0 available.

 分析了一下,这个就是因为死锁造成的

报错的地方是:

SQLiteConnectionPool.java

    private SQLiteConnection waitForConnection(String sql, int connectionFlags,
            CancellationSignal cancellationSignal) {

        ......
                        logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
        ......

}

错误发生位置

    private void logConnectionPoolBusyLocked(long waitMillis, int connectionFlags) {
        final Thread thread = Thread.currentThread();
        StringBuilder msg = new StringBuilder();
        msg.append("The connection pool for database '").append(mConfiguration.label);
        msg.append("' has been unable to grant a connection to thread ");
        msg.append(thread.getId()).append(" (").append(thread.getName()).append(") ");
        msg.append("with flags 0x").append(Integer.toHexString(connectionFlags));
        msg.append(" for ").append(waitMillis * 0.001f).append(" seconds.\n");
         ......
}

发生上面错误的原因就是:

我们操作数据库时(先不考虑读取)一定要先获取连接,但是同一个connect在不特殊设置的时候,只能维持1个,当大于一个的时候,另一个就需要等待。

(可以通过设置ENABLE_WRITE_AHEAD_LOGGING的值,来提高这个数量)

所以:

当我们有一个连接正在插入数据到数据库,又来了一个插入数据库操作的时候,第二个就会等待,第一个完成,会通知第二个开始插入,这样也保证了数据库的线程安全。

这里虽然不是通过锁的机制实现的,但是和锁的目的是一样的。获取锁与释放锁的位置就是:

acquireConnection
releaseConnection

这两个方法调用的地方有两个:

1.修改数据库的地方,需要在修改之前,获取锁,修改之后,释放锁。

2.当我们获取事务的时候,获取锁,结束事务的时候,释放锁。

我们在操作数据库的时候,有时候,不熟悉相关机制,就有可能增加锁,这时就会导致又可能有两个锁互相锁住的情况,就会造成死锁发生,一旦发生死锁,每个30s就会打印一次上面的log

举一个发生死锁的问题代码:(这个是我当时犯的一个真实线上问题)

    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        if (mDb == null) {
            return null;
        }
        SQLiteDatabase db = mDb;
        Uri result;
        synchronized (sLock) {
            long ret = db.replace(FantasyDbConstant.TABLE_DATA, null, contentValues);
            result = Uri.parse("content://" + xxx + "/" + ret);
        }
        return result;
    }

其实我这个插入操作无论怎么调用都没有问题,因为我只是简单的在insert外面加锁,单个插入我也没有采用事务。

但万万没想到啊,在ContentProvider中有一部分代码是这样的,这个super.applyBatch实际上调用的是insert方法,这就导致了事务的锁中间又插入了一个对象锁,造成了死锁。

    public ContentProviderResult[] applyBatch(@NonNull ArrayList<ContentProviderOperation> operations)
            throws OperationApplicationException {
        SQLiteDatabase db = fantasyDb.getSqlite();
        if (db == null) {
            return new ContentProviderResult[]{};
        }
        db.beginTransaction();
        try {
            ContentProviderResult[] results = super.applyBatch(operations);
            db.setTransactionSuccessful();
            return results;
        } finally {
            db.endTransaction();
        }
    }

所以理论上我们在使用数据库的时候,根本不需要自己加锁,因为数据库本身就是线程安全的,这样既不会增加效果,还会引发各种问题。

到此为止的所有逻辑都是java层的逻辑判断,还没有到数据库真正的核心逻辑。

 

到这里你以为就结束了?远远没有数据这里面还有很多的坑,可能有些复杂的业务场景还是需要加锁的。下面再介绍一个错误:

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

在介绍这个之前,我觉得有必要介绍一下真正的数据库的相关知识了:

数据库的锁机制:一共有五种状态,如下图:

 

了解了锁的机制之后,我们在回到上面的这个错误:

同一个进程多个线程开启多个connection,同时去写数据库:

2018-12-28 17:31:12.039 2562-2580/com.redare.dbmultprocess E/AndroidRuntime: FATAL EXCEPTION: Thread-3
    Process: com.redare.dbmultprocess, PID: 2562
    android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:734)
        at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
        at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
        at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1679)
        at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1608)
        at com.redare.dbmultprocess.db.SQLite_db.exeDO(SQLite_db.java:34)
        at com.redare.dbmultprocess.db.SQLite_db.insertNewTask(SQLite_db.java:48)
        at com.redare.dbmultprocess.MyService$1.run(MyService.java:46)
        at java.lang.Thread.run(Thread.java:764)

我们通过查看sqlite.c文件可以知道:database is locked 对应的是SQLITE_BUSY的错误信息,就是说有一个database connection已经获取写入的状态,并且还没有commit,这是如果有另一个database connection尝试写入,就会报这个错误。

PS:SQLITE_LOCKED是有冲突,例如一个线程使用database connection尝试drop数据库内容,另一个线程使用当前的database connection去读,就会发生这个错误(我没有复现这个场景)

SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){
  static const char* const aMsg[] = {
    *********************************************
    /* SQLITE_BUSY        */ "database is locked",
    /* SQLITE_LOCKED      */ "database table is locked",
    *********************************************
  };

到这里你是不是有些懵逼,sqlite不是说是线程安全的吗?为什么会直接崩溃呢?

这里需要强调几个概念:

1.connection :每个SQLiteOpenHelper,都会对应一个database,而在android 的java层的数据库的锁机制,都是在对象锁的维度上,所以说两个SQLiteOpenHelper之间是不会造成线程之间的排斥的,所以两个,线程可以同时写入

2.对于sqlite c部分的逻辑,我们看到上面图中的锁,他是不允许两个线程同时写入的,如果有一个线程已经获取了RESOLVED,PENDING,EXCLUSIVE的锁的时候,如果有另一个线程尝试去获取该种类型的锁的时候,就会返回SQLITE_BUSY的错误信息,反应到上层就是上面这个错误

现在说一下上面的结论:

1.数据库是线程安全的,这个无论是在android java端,还是在sqlite的c端,都是做了相关的保护的,但java层的保护是不发生异常,而sqlite c这层保护是保证不发生脏数据。

2.android 端如果采用多个SQLiteOpenHelper的对象,那么这个在java这一层的保护就跳过了,不再提供线程安全的保护,这样就有可能发生崩溃,

3.如果你在创建和Drop数据库的时候,查询数据库,那也会发生崩溃(但这个没有复现,验证的时候,虽然是同时,但如果先查询,验证没问题,如果先drop,则查询的时候,找不到table)

 

到这里就要总结一下需要注意的点了:

1.数据库的connection尽量维持一个,这样更安全一些。

2.没有特殊需求,不要自作多情,在增删改查的时候,增加锁。

 

介绍完以上这个问题之后,我们再来讨论一个问题就是

数据库是否是进程安全的?

先来看一下官网的解答:(我们只关注android,Win95 / 98 / ME下,缺少对读取器/写入器锁的支持,情况不一样)

1.sqlite 采用的是读取器/写入器

2.sqlite 允许多个进程一次打开数据库文件

3.sqlite允许多个进程一次读取数据库文件

4.sqlite只允许一个进程写入文件,并且在写入时,锁住整个数据库文件(几毫秒),如果其他进程如果访问会返回SQLITE_BUSY的错误

看到这里我们可以得出与线程安全同样的结论:

1.sqlite可以保证不产生脏数据

2.sqlite多进程写入时会发生崩溃(而且多进程没办法公用一个connection,所以这个问题android端并没有给解决方案)

多进程同时写文件时发生的崩溃:

2018-12-28 16:00:03.381 27025-27041/com.redare.dbmultprocess E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.redare.dbmultprocess, PID: 27025
    android.database.sqlite.SQLiteException: Failed to change locale for db '/data/user/0/com.redare.dbmultprocess/databases/my_db' to 'en_US'.
        at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:393)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:808)
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:793)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:696)
        at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:723)
        at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:299)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:254)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:194)
        at com.redare.dbmultprocess.db.SQLite_db.exeDO(SQLite_db.java:34)
        at com.redare.dbmultprocess.db.SQLite_db.insertNewTask(SQLite_db.java:49)
        at com.redare.dbmultprocess.MainActivity$1.run(MainActivity.java:35)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
        at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
        at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:555)
        at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:371)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218) 
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193) 
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463) 
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185) 
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177) 
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:808) 
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:793) 
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:696) 
        at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:723) 
        at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:299) 
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:254) 
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:194) 
        at com.redare.dbmultprocess.db.SQLite_db.exeDO(SQLite_db.java:34) 
        at com.redare.dbmultprocess.db.SQLite_db.insertNewTask(SQLite_db.java:49) 
        at com.redare.dbmultprocess.MainActivity$1.run(MainActivity.java:35) 
        at java.lang.Thread.run(Thread.java:764) 
2018-12-28 16:02:55.331 27149-27167/com.redare.dbmultprocess:myservice E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.redare.dbmultprocess:myservice, PID: 27149
    android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:734)
        at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
        at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
        at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1679)
        at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1608)
        at com.redare.dbmultprocess.db.SQLite_db.exeDO(SQLite_db.java:35)
        at com.redare.dbmultprocess.db.SQLite_db.insertNewTask(SQLite_db.java:49)
        at com.redare.dbmultprocess.MyService$1.run(MyService.java:46)
        at java.lang.Thread.run(Thread.java:764)

最后总结一下数据库的知识:

1.sqlite尽量保持一个connection

2.多进程操作时,尽量保证只有一个进程写入,可以有多个进程读取

3.不要私自加锁

 

但是你以为这样就完美了?实际上还有坑:

当我们使用数据库连接的时候,我们涉及到关闭连接的问题,如果多个连接使用同一个connection,一旦有一个先close了,那么后面正在使用的就会崩溃(这里android在关闭的时候,会有一个计数器,但没搞明白这里实现的乱七八糟的,明显存在问题,一直没有修复)

同一个SqliteHelper对象进行读写操作:

2018-12-28 18:08:13.739 7603-7932/com.redare.dbmultprocess E/AndroidRuntime: FATAL EXCEPTION: Thread-313
    Process: com.redare.dbmultprocess, PID: 7603
    java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
        at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)
        at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:677)
        at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)
        at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
        at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)
        at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
        at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)
        at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:132)
        at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:219)
        at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:258)
        at com.redare.dbmultprocess.db.SQLite_db.query(SQLite_db.java:64)
        at com.redare.dbmultprocess.MainActivity$2.run(MainActivity.java:48)
        at java.lang.Thread.run(Thread.java:764)

我在github上复现了所有的崩溃,感兴趣的可以自己查看。

https://github.com/xiepengchong/DbException

参考文章:

https://www.sqlite.org/lockingv3.html

https://www.sqlite.org/faq.html#q5

https://www.sqlite.org/rescode.html

2018-02-23 17:43:58 lllx9464 阅读数 298

在sqlite中多次打开数据库,对数据库进行高并发操作,容易造成锁表, 对此可以在只打开一次数据库的情况下,对数据库进行多次操作。

新建一个MyDatabaseHelper继承自SQLiteOpenHelper

使用volatile声明一个MyDatabaseHelper对象,通过getDBHelper()方法来获取唯一实例

private volatile static MyDatabaseHelper myDBHelper;

    private MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    //双重验证的单例方法,通过getDBHelper得到helper对象来得到数据库,保证helper类是单例的
    public static MyDatabaseHelper getDBHelper(Context context) {
        if (myDBHelper == null) {
            synchronized (MyDatabaseHelper.class) {
                if (myDBHelper == null) {
                    myDBHelper = new MyDatabaseHelper(context, "MyWeather.db", null, DBVersion);
                }
            }
        }
        return myDBHelper;
    }

在需要对数据库操作的类中调用MyDatabaseHelper.getDBHelper(context)方法获取一个唯一的helper,使用这个helper来对数据库进行操作

private SQLiteDatabase db;
...
db = MyDatabaseHelper.getDBHelper(getContext()).getWritableDatabase();
...
db.delete("SavedCity", "cityName = ?", new String[]{cityName});

当然也可以直接在Helper类中直接声明获取数据库的方法

//类中声明private SQLiteDatabase mDB;
public SQLiteDatabase openWriteLink(){
    if(mDB == null||!mDB.isOpen()){
        mDB = myDBHelper.getWritableDatabase();
    }
    return mDB;
}
//获取只读的数据库也是同样的写法

同时还要声明关闭数据库的方法

public void closeLink(){
    if(mDB != null && mDB.isOpen()){
        mDB.close();
        mDB = null;
    }
}

2016-02-20 15:26:03 u011324501 阅读数 343

代码下载:http://download.csdn.net/detail/u011324501/9437567

1SQLite数据库是一个轻量级的数据库,数据库可以通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据。

2SQLite支持大部分标准SQL语句,增删改查语句都是通用的,分页查询语句与MySQL相同。

3、创建数据库:需要定义类继承SQLiteOpenHelper,声明构造函数四个参数,重写onCreate()方法,onUpgrade()方法。

实现代码:main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_marginLeft="5dip"
    tools:context=".MainActivity" >

    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:text="@string/hello_world" />
        <EditText 
            android:id="@+id/name"
            android:inputType="text"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"/>
    </LinearLayout>
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:text="@string/age" />
        <EditText 
            android:id="@+id/age"
            android:inputType="number"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"/>
    </LinearLayout>
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:text="@string/height" />
        <EditText 
            android:id="@+id/heigt"
            android:inputType="number"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"/>
    </LinearLayout>
    
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:text="@string/id" />
        <EditText 
            android:id="@+id/id"
            android:inputType="number"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"/>
    </LinearLayout>

    <LinearLayout 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/adddate"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:text="@string/add" />
        <Button 
            android:id="@+id/delete"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:text="@string/de" />
        <Button 
            android:id="@+id/query"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:text="@string/qu" />
        <Button 
            android:id="@+id/deleteid"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:text="@string/deid" />
        
        <Button 
            android:id="@+id/update"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:text="@string/up" />
    </LinearLayout>
    
    <TextView
        android:id="@+id/text"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"/>
</LinearLayout>
主代码:MainActivity.java

package com.example.sqllite;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private EditText starname;
	private EditText starage;
	private EditText starheight;
	private EditText starid;
	
	private Button add;
	private Button delete;
	private Button query;
	private Button deleteid;
	private Button update;
	
	private TextView text;
	private DBAdapter dbAdapter;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		//获得实例
        dbAdapter = new DBAdapter(this);
        //打开数据库
        dbAdapter.open();
		initUI();
	}
	//初始化UI
	private void initUI() {
		// TODO Auto-generated method stub
		starname = (EditText)findViewById(R.id.name);
		starage = (EditText)findViewById(R.id.age);
		starheight = (EditText)findViewById(R.id.heigt);
		starid = (EditText)findViewById(R.id.id);
		
		add = (Button)findViewById(R.id.adddate);
		add.setOnClickListener(new addListener());
		
		delete = (Button)findViewById(R.id.delete);
		delete.setOnClickListener(new deleteListener());
		
		query = (Button)findViewById(R.id.query);
		query.setOnClickListener(new queryListener());
		
		deleteid = (Button)findViewById(R.id.deleteid);
		deleteid.setOnClickListener(new deleteidListener());
		
		update = (Button)findViewById(R.id.update);
		update.setOnClickListener(new updateListener());
		
		text = (TextView)findViewById(R.id.text);
	}

	//添加数据
	public class addListener implements OnClickListener{

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			if(starname.length()!=0 && starage.length()!=0 && starheight.length()!=0){
				 People people = new People();
				 people.Name = starname.getText().toString();
	             people.Age  = Integer.parseInt(starage.getText().toString());
	             people.Height = Float.parseFloat(starheight.getText().toString());
	             long colunm = dbAdapter.insert(people);
	             if(colunm == -1){
	            	 Toast.makeText(getApplication(), "添加错误!", Toast.LENGTH_SHORT).show();
	              }else{
	            	  Toast.makeText(getApplication(), "数据库数据为空!", Toast.LENGTH_SHORT).show();
	              }
			}else{
				Toast.makeText(getApplication(), "输入数据不能为空!", Toast.LENGTH_SHORT).show();
			}
			 
		}
		
	}
	
	//删除数据
	public class deleteListener implements OnClickListener{

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			dbAdapter.deleteAllData();
            Toast.makeText(getApplication(), "数据已删除!", Toast.LENGTH_SHORT).show();
            text.setText("");
		}
		
	}
	
	//查询数据
	public class queryListener implements OnClickListener{

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			People[] peoples = dbAdapter.queryAllData();
            if(peoples == null)
            {
            	 Toast.makeText(getApplication(), "数据库数据为空!", Toast.LENGTH_SHORT).show();
                return;
            }        
            String result = "";
            for (int i = 0; i < peoples.length; i++)
            {
                result += peoples[i].toString()+"\n";
            }
            text.setText(result);
		}
		
	}
	
	//根据 id 删除
	public class deleteidListener implements OnClickListener{

		@SuppressWarnings("static-access")
		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			if(starid.length()!=0){
				int id = Integer.parseInt(starid.getText().toString());
	            long result = dbAdapter.deleteOneData(id);
	            Log.i(dbAdapter.DB_ACTION, "delete long :"+result);
	            String msg = "删除ID为"+starid.getText().toString()+"的数据" + (result>0?"成功":"失败");
	            text.setText(msg);
			}else {
				Toast.makeText(getApplication(), "id不能为空!", Toast.LENGTH_SHORT).show();
			}
		}
		
	}
	
	//更新数据
	public class updateListener implements OnClickListener{

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			People people = new People();
            people.Name = starname.getText().toString();
            people.Age  = Integer.parseInt(starage.getText().toString());
            people.Height = Float.parseFloat(starheight.getText().toString());
            
            int id = Integer.parseInt(starid.getText().toString());
            long count = dbAdapter.updateOneData(id, people);
            if(count == -1 )
            {
            	Toast.makeText(getApplication(), "更新错误!!", Toast.LENGTH_SHORT).show();
                text.setText("");
            }
            else
            {
            	Toast.makeText(getApplication(),"更新成功"+"更新数据第"+String.valueOf(id)+"条", Toast.LENGTH_SHORT).show();
            }
		}
		
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		dbAdapter.close();//关闭数据库
	}
	
}
数据库:DBAdapter.java

package com.example.sqllite;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;

/**
 * @author 
 *
 */
public class DBAdapter
{
    public static final String DB_ACTION="db_action";//LogCat
    
    private static final String DB_NAME="people.db";//数据库名
    private static final String DB_TABLE="peopleinfo";//数据库表名
    private static final int    DB_VERSION=1;//数据库版本号
    
    public static final String KEY_ID = "_id";  //表属性ID
    public static final String KEY_NAME = "name";//表属性name
    public static final String KEY_AGE  = "age";//表属性age
    public static final String KEY_HEIGHT = "height";//表属性height
    
    private SQLiteDatabase db ;
    private Context xContext ;
    private DBOpenHelper dbOpenHelper ;
    public DBAdapter(Context context)
    {
        xContext = context ;
    }
    
    /** 空间不够存储的时候设为只读
     * @throws SQLiteException
     */
    public void open() throws SQLiteException
    {
        dbOpenHelper = new DBOpenHelper(xContext, DB_NAME, null,DB_VERSION);
        try
        {
            db = dbOpenHelper.getWritableDatabase();
        }
        catch (SQLiteException e)
        {
            db = dbOpenHelper.getReadableDatabase();
        }
    }
    
    public void close()
    {
        if(db != null)
        {
            db.close();
            db = null;
        }
    }
    /**
     * 向表中添加一条数据
     * @param people
     * @return
     */
    public long insert(People people)
    {
        ContentValues newValues = new ContentValues();
        
        newValues.put(KEY_NAME, people.Name);
        newValues.put(KEY_AGE, people.Age);
        newValues.put(KEY_HEIGHT, people.Height);
        
        return db.insert(DB_TABLE, null, newValues);
    }
    
    /**
     * 删除一条数据
     * @param id
     * @return
     */
    public long deleteOneData(long id)
    {
        return db.delete(DB_TABLE, KEY_ID+"="+id, null );
    }
    /**
     * 删除所有数据
     * @return
     */
    public long deleteAllData()
    {
        return db.delete(DB_TABLE, null, null);
    }
    /**
     * 根据id查询数据的代码
     * @param id
     * @return
     */
    public People[] queryOneData(long id)
    {
        Cursor result = db.query(DB_TABLE, new String[] {KEY_ID,KEY_NAME,KEY_AGE,KEY_HEIGHT}, 
                KEY_ID+"="+id, null, null, null, null);
        return ConvertToPeople(result) ;
    }
    /**
     * 查询全部数据的代码
     * @return
     */
    public People[] queryAllData()
    {
        Cursor result = db.query(DB_TABLE, new String[] {KEY_ID,KEY_NAME,KEY_AGE,KEY_HEIGHT}, 
                null, null, null, null, null);
        return ConvertToPeople(result);
    }
    
    public long updateOneData(long id ,People people)
    {
        ContentValues newValues = new ContentValues();
        
        newValues.put(KEY_NAME, people.Name);
        newValues.put(KEY_AGE, people.Age);
        newValues.put(KEY_HEIGHT, people.Height);
        
        return db.update(DB_TABLE, newValues, KEY_ID+"="+id, null);
    }
    
    private People[] ConvertToPeople(Cursor cursor)
    {
        int resultCounts = cursor.getCount();
        if(resultCounts == 0 || !cursor.moveToFirst())
        {
            return null ;
        }
        People[] peoples = new People[resultCounts];
        Log.i(DB_ACTION, "PeoPle len:"+peoples.length);
        for (int i = 0; i < resultCounts; i++)
        {
            peoples[i] = new People();
            peoples[i].ID = cursor.getInt(0);
            peoples[i].Name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
            peoples[i].Age  = cursor.getInt(cursor.getColumnIndex(KEY_AGE));
            peoples[i].Height = cursor.getFloat(cursor.getColumnIndex(KEY_HEIGHT));
            Log.i(DB_ACTION, "people "+i+"info :"+peoples[i].toString());
            cursor.moveToNext();
        }
        return peoples;
    }
    
    
    /**
     * 静态Helper类,用于建立、更新和打开数据库
     */
    private static class DBOpenHelper extends SQLiteOpenHelper
    {
        /*
         * 手动建库代码
        CREATE TABLE peopleinfo
        (_id integer primary key autoincrement,
        name text not null,
        age integer,
        height float);*/
        private static final String DB_CREATE=
        "CREATE TABLE "+DB_TABLE
        +" ("+KEY_ID+" integer primary key autoincrement, "
        +KEY_NAME+" text not null, "
        +KEY_AGE+" integer,"+
        KEY_HEIGHT+" float);";
        public DBOpenHelper(Context context, String name,
                CursorFactory factory, int version)
        {
            super(context, name, factory, version);
        }

        @Override
        public void onCreate(SQLiteDatabase db)
        {
            db.execSQL(DB_CREATE);
            Log.i(DB_ACTION, "onCreate");
        }

        @Override
        public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion)
        {
            //函数在数据库需要升级时被调用,
            //一般用来删除旧的数据库表,
            //并将数据转移到新版本的数据库表中
            _db.execSQL("DROP TABLE IF EXISTS "+DB_TABLE);
            onCreate(_db);
            Log.i(DB_ACTION, "Upgrade");
        }

    }
}

代码下载地址:http://download.csdn.net/detail/u011324501/9437567



2009-12-25 17:10:00 wentao158 阅读数 521

SQLite

   

SQLite是一种关系型数据库管理系统(RDBMS)。它在以下方面得到公认:

 

开源

 

标准兼容

 

轻量级

 

单层

 

它已经被实现为紧凑的C库,并作为Android软件栈的一部分。通过库来提供功能而不是作为分离的处理,使得每个数据库都能成为应用程序的集成部分。它能降低关联、减少反应时间、并简化加锁和同步的处理。

 

SQLite拥有值得信赖的名声,因此,在许多消费者电子设备,包括MP3播放器、iPhoneiPod Touch中,都是数据库系统的一种选择。

 

轻量级、功能强大的SQLite不同于许多传统的数据库引擎,它通过使用松散的列定义输入方式。不是要求列的值都符合某一单一类型,每列中每行的值是独立输入的。因此,当从每列的一行中指定或提取值时,美誉哦严格的类型检查。

 

想要了解更多关于SQLite,包括它的能力和限制,请登录官方网站:www.sqlite.org/.

 

2 Cursor和ContentValue   

 

 

 

ContentValue对象用于在数据库表(和Content Provider)中插入新的行。每个ContentValue对象代表单一行,包含列名称映射的值。

ContentValue对象和Hashtable比较类似,负责存储一些名值对。名是String,值是基本类型。

 

 

Android中的查询会得到一个Cursor对象。不是提取和返回结果值的一个拷贝,Cursor实际上指向底层数据的子集。Cursor是一种托管的方式控制在数据库查询的结果集中的位置(行)。

 

Cursor类包含一些用于导航查询结果的函数,包括但不限于以下的:

 

moveToFirst

移动Cursor到查询结果的第一行。

 

moveToNext

移动Cursor到下一行。

 

moveToPrevious

移动Cursor到前一行。

 

getCount

返回结果集中的行数。

 

getColumnIndexOrThrow

通过指定的名称来返回列的索引(如果不存在该名称的列的话,会抛出异常)。

 

getColumnName

返回指定列索引的列名称。

 

getColumnNames

返回当前Cursor所在的所有列名称的字符串数组。

 

moveToPosition

移动Cursor到指定的行。

 

getPosition

返回当前Cursor的位置。

 

Android提供了一种在你的Activity中管理Cursor资源的机制。startManagingCursor方法将Cursor的生命周期集成到父Activity的生命周期管理中。当你使用Cursor完成工作时,调用stopManagingCursor来结束这一现状。

 

 

 

SQLite数据库锁问题

阅读数 909

没有更多推荐了,返回首页