Android中SQLite数据库和Room的简单使用

创建数据库

升级数据库

数据库的CRUD操作

添加数据

更新数据

删除数据

查询数据

使用事务

Room的使用

定义Entity

定义Dao

定义DataBase

建立数据库类,并创建一个Book表。首先把创建表的SQL语句写好,然后在onCreate中执行这条语句。

class MyDatabaseHelper(val context: Context, name: String, version: Int) : SQLiteOpenHelper(context, name, null, version) {

    private val createBook = "create table Book (" +
            " id integer primary key autoincrement," +
            "author text," +
            "price real," +
            "pages integer," +
            "name text," +
            "category_id integer)"

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(createBook)
        Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {

    }

}

修改activity_main.xml中的代码,只添加一个按钮用于创建数据库。

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">

    <button android:id="@+id/createDatabase" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Create Database">

</button></linearlayout>

最后修改MainActivity中的代码,创建数据库。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
}
}

其中MyDatabaseHelper()中的参数分别是上下文、数据库名字、数据库版本。数据库只能创建一次,再次创建时发现已经存在该数据库了,那么就不会再次创建了。

假设升级后的数据库增加了一张Category表,那么就可以这么写:首先把创建Category表的SQL语句写好,然后在 onUpgrade中执行这条语句,在MainActivity中修改数据库的版本号为2,升级的逻辑就会被执行。注意,每个版本都要进行判断,不然的话,如果直接从1升到3版本,那么2版本的升级逻辑就会被略过!!


class MyDatabaseHelper(val context: Context, name: String, version: Int) : SQLiteOpenHelper(context, name, null, version) {

    private val createBook = "create table Book (" +
            " id integer primary key autoincrement," +
            "author text," +
            "price real," +
            "pages integer," +
            "name text," +
            "category_id integer)"

    private val createCategory = "create table Category (" +
            "id integer primary key autoincrement," +
            "category_name text," +
            "category_code integer)"

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(createBook)
        db.execSQL(createCategory)
        Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        if (oldVersion <= 1) { db.execsql(createcategory) } if (oldversion <="2)" db.execsql("alter table book add column category_id integer") }< code></=>

添加数据用的是insert方法 参数为 (表名、未添加数据的情况下为可为空的列自动赋值null、要添加的数据),首先在activity_main.xml 中添加一个id为addData的按钮,然后为这个按钮设置点击事件。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // &#x63D2;&#x5165;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
            val values2 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // &#x63D2;&#x5165;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
        }
}
}

因为在表中的id列设置了自增,所以不需要给id列赋值,这里的values编写形式还可以继续简化,如values1可以这么写:

val values1= contentValuesOf("name" to "The Da Vinci Code","author" to "Dan Brown","pages" to  454,"price" to  16.96)

更新数据使用的是update方法 参数为 (表名、要更新的数据、第三和第四个参数具体指定更新哪些行), 首先在activity_main.xml 中添加一个id为updateData的按钮,然后为这个按钮设置点击事件。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // &#x63D2;&#x5165;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
            val values2 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // &#x63D2;&#x5165;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
}
}

这里的values编写形式也是可以简化的。

更新数据使用的是delete方法 参数为(表名、第二和第三个参数具体指定删除那些行), 首先在activity_main.xml 中添加一个id为deleteData的按钮,然后为这个按钮设置点击事件。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // &#x63D2;&#x5165;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
            val values2 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // &#x63D2;&#x5165;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
        deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500"))
        }
}
}

这是最复杂的一种操作,查询使用的是query方法,为了方便演示,使用最短的query重载方法,它有七个参数, 分别是(表名,查询哪几列 /null为查询所有列,第三和第四个参数约束查询哪几行/null为查询所有行,指定需要去group by的列/null为不进行group by操作,对group by的结果进行过滤/null为不进行过滤,指定查询结果的排序方式/null为使用默认排序)该方法返回一个cursor对象。 首先在activity_main.xml 中添加一个id为query Data的按钮,然后为这个按钮设置点击事件。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // &#x63D2;&#x5165;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
            val values2 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // &#x63D2;&#x5165;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
        deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500"))
        }
        queryData.setOnClickListener {
            val db = dbHelper.writableDatabase
            // &#x67E5;&#x8BE2;Book&#x8868;&#x4E2D;&#x6240;&#x6709;&#x7684;&#x6570;&#x636E;
            val cursor = db.query("Book", null, null, null, null, null, null)
            if (cursor.moveToFirst()) {
                do {
                    // &#x904D;&#x5386;Cursor&#x5BF9;&#x8C61;&#xFF0C;&#x53D6;&#x51FA;&#x6570;&#x636E;&#x5E76;&#x6253;&#x5370;
                    val name = cursor.getString(cursor.getColumnIndex("name"))
                    val author = cursor.getString(cursor.getColumnIndex("author"))
                    val pages = cursor.getInt(cursor.getColumnIndex("pages"))
                    val price = cursor.getDouble(cursor.getColumnIndex("price"))
                    Log.d("MainActivity", "book name is $name")
                    Log.d("MainActivity", "book author is $author")
                    Log.d("MainActivity", "book pages is $pages")
                    Log.d("MainActivity", "book price is $price")
                } while (cursor.moveToNext())
            }
            cursor.close()
        }
}
}

在数据库中使用事务可以保证一系列操作要么会全部完成,要么一个都不会完成。首先在activity_main.xml 中添加一个id为replace Data的按钮,然后为这个按钮设置点击事件。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // &#x63D2;&#x5165;&#x7B2C;&#x4E00;&#x6761;&#x6570;&#x636E;
            val values2 = ContentValues().apply {
                // &#x5F00;&#x59CB;&#x7EC4;&#x88C5;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // &#x63D2;&#x5165;&#x7B2C;&#x4E8C;&#x6761;&#x6570;&#x636E;
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
        deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500"))
        }
        queryData.setOnClickListener {
            val db = dbHelper.writableDatabase
            // &#x67E5;&#x8BE2;Book&#x8868;&#x4E2D;&#x6240;&#x6709;&#x7684;&#x6570;&#x636E;
            val cursor = db.query("Book", null, null, null, null, null, null)
            if (cursor.moveToFirst()) {
                do {
                    // &#x904D;&#x5386;Cursor&#x5BF9;&#x8C61;&#xFF0C;&#x53D6;&#x51FA;&#x6570;&#x636E;&#x5E76;&#x6253;&#x5370;
                    val name = cursor.getString(cursor.getColumnIndex("name"))
                    val author = cursor.getString(cursor.getColumnIndex("author"))
                    val pages = cursor.getInt(cursor.getColumnIndex("pages"))
                    val price = cursor.getDouble(cursor.getColumnIndex("price"))
                    Log.d("MainActivity", "book name is $name")
                    Log.d("MainActivity", "book author is $author")
                    Log.d("MainActivity", "book pages is $pages")
                    Log.d("MainActivity", "book price is $price")
                } while (cursor.moveToNext())
            }
            cursor.close()
        }
        replaceData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.beginTransaction() // &#x5F00;&#x542F;&#x4E8B;&#x52A1;
            try {
                db.delete("Book", null, null)
//                if (true) {
//                    // &#x5728;&#x8FD9;&#x91CC;&#x624B;&#x52A8;&#x629B;&#x51FA;&#x4E00;&#x4E2A;&#x5F02;&#x5E38;&#xFF0C;&#x8BA9;&#x4E8B;&#x52A1;&#x5931;&#x8D25;
//                    throw NullPointerException()
//                }
                val values = cvOf("name" to "Game of Thrones", "author" to "George Martin", "pages" to 720, "price" to 20.85)
                db.insert("Book", null, values)
                db.setTransactionSuccessful() // &#x4E8B;&#x52A1;&#x5DF2;&#x7ECF;&#x6267;&#x884C;&#x6210;&#x529F;
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                db.endTransaction() // &#x7ED3;&#x675F;&#x4E8B;&#x52A1;
            }
        }
}
}

Room是对数据库的原生的API进行的封装,它由3部分组成 Entity、Dao、Database。Entity可以理解为数据库中的表,Dao是对数据库操作进行的封装,Database定义了数据库中的关键信息,并提供Dao的访问实体类。

首先在软件级的gradle中添加如下依赖:

apply plugin: 'kotlin-kapt'
    implementation "androidx.room:room-runtime:2.1.0"
    kapt "androidx.room:room-compiler:2.1.0"

@Entity
data class User(var firstName: String, var lastName: String, var age: Int) {

    @PrimaryKey(autoGenerate = true)
    var id: Long = 0

}

在数据类前加上注解@Entity,将它生命成一个实体类。然后在id列前面加上注解@PrimaryKey(autoGenerate = true),将id声明为主键并设置自增。

Dao层是封装业务逻辑的地方,尽量将可能用到的CRUD操作写到这里。


@Dao
interface UserDao {

    @Insert
    fun insertUser(user: User): Long

    @Update
    fun updateUser(newUser: User)

    @Query("select * from User")
    fun loadAllUsers(): List<user>

    @Query("select * from User where age > :age")
    fun loadUsersOlderThan(age: Int): List<user>

    @Delete
    fun deleteUser(user: User)

    @Query("delete from User where lastName = :lastName")
    fun deleteUserByLastName(lastName: String): Int

}</user></user>

这里的插入、更新、删除操作为了简便演示并没有写SQL语句进行操作,但是它们一样能使用SQL语句进行操作。我们可以看到 @Query注解下并不全是查询操作,最后一个就是删除操作,这里使用@Query注解而不使用@Delete的原因是下面那个使用的是非实体类参数,使用非实体类参数来进行CRUD操作时统一使用@Query注解。在查询操作中可使用使用传入的变量对数据进行动态的查询,就像第二个查询操作一样。


@Database(version = 1, entities = [User::class, Book::class])
abstract class AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    abstract fun bookDao(): BookDao

    companion object {

        private val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("create table Book (id integer primary key autoincrement not null, name text not null, pages integer not null)")
            }
        }

        private val MIGRATION_2_3 = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("alter table Book add column author text not null default 'unknown'")
            }
        }

        private var instance: AppDatabase? = null

        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
                .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                .build().apply {
                instance = this
            }
        }
    }

}

这里要使用@Database注解来声明这是一个数据库型实体类。然后写入数据库的版本、包含的实体类。然后在companion object { }中定义了数据库的升级逻辑,这里定义了1到2版本、2到3版本的升级逻辑,而在测试开发的时候一般使用.fallbackToDestructiveMigration()来替换这里的.addMigrations(MIGRATION_1_2, MIGRATION_2_3),这个方法升级的时候会销毁当前的数据库再创建,这样就暂时不用写复杂的升级逻辑了(开发完了记得及时替换回来)。还有,数据库的操作属于耗时操作,所以一般不允许在主线程中操作,只能放到子线程中,但为了开发时方便,我们可以在数据库执行.build()前添加.allowMainThreadQueries()方法使操作能够在主线程中进行(开发完了记得及时删除)。 随后,定义了instance来缓存该数据库的实例,并且使用 getDatabase方法进行判空处理,若不为空则返回它,否则就调用Room.databaseBuilder方法直接再建一个赋值给instance然后返回。注意Room.databaseBuilder的第一个参数一定要用applicationContext ,不然容易出现内存泄露,第二个参数时该数据库的class类型,第三个参数是数据库的名字。

最后,我们看一下实际操作数据库是怎样的。

先定义4个按钮用于进行CRUD操作:



class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val userDao = AppDatabase.getDatabase(this).userDao()
        val user1 = User("Tom", "Brady", 40)
        val user2 = User("Tom", "Hanks", 63)

        addDataBtn.setOnClickListener {
            thread {
                user1.id = userDao.insertUser(user1)
                user2.id = userDao.insertUser(user2)
            }
        }
        updateDataBtn.setOnClickListener {
            thread {
                user1.age = 42
                userDao.updateUser(user1)
            }
        }
        deleteDataBtn.setOnClickListener {
            thread {
                userDao.deleteUserByLastName("Hanks")
            }
        }
        queryDataBtn.setOnClickListener {
            thread {
                for (user in userDao.loadAllUsers()) {
                    Log.d("MainActivity", user.toString())
                }
            }
        }

}

好了,数据库的基本操作就到这了,作为基础学习应该够了。

Original: https://blog.csdn.net/liny70858/article/details/127345412
Author: 贪睡的汤圆
Title: Android中SQLite数据库和Room的简单使用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/815367/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球