Android之本地数据存储(SQLite数据库)


一、概念

1、了解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


2、应用场景

现在的主流移动设备像 Android、iPhone 等都使用 SQLite 作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到 SQLite 来存储我们大量的数据,所以我们就需要掌握移动设备上的 SQLite 开发技巧。对于 Android 平台来说, 系统内置了丰富的 API 来供开发人员操作 SQLite,我们可以轻松的完成对数据的存取


二、数据库操作

1、创建数据库

create table info_tab(
	/*primary key 主键 --> 唯一标识符  auto_increment 自动增长*/
	_id int primary key auto_increment,
    name varchar(30) not null,
    /*允许年龄为空(女性年龄不能随意透露*/
    age int,
    gender varchar(2) not null
)

在这里插入图片描述


2、数据操作(增删改查)

DML(数据库操作语句)

2.1 添加语句

方法1:

在这里插入图片描述
insert into info_tab(_id,name,age,gender) values (1,'张三',23,'男')

报错: Error Code: 1366. Incorrect string value: ‘\xE5\xBC\xA0\xE4\xB8\x89’ for column ‘name’ at row 1

解决:
添加以下代码,便可以输入汉字
alter table info_tab convert to character set utf8 ;

注意:
①数据类型要对应
在这里插入图片描述
②数据个数要对应
在这里插入图片描述
③数据顺序要对应
年龄列插入年龄,姓名列插入姓名

④一定要包含所有非空列
NN:not null 非空列
在这里插入图片描述
⑤自动增长列
可以不需要管,默认忽视,会自动配置好
若一定要手动添加:
a.给一个不存在的数据
b.null(会自动增长)


方法2:

在这里插入图片描述
默认所有列都要插入数据(一定要按照表的结构中列的顺序来插入)

insert into info_tab values(3,'张四',34,'女')


2.2 删除语句

在这里插入图片描述
如果不带条件的话,就会删除该表中所以的内容

delete from info_tab where gender='女'

报错: Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect.

原因: MySQL运行在safe-updates模式下,该模式会导致非主键条件下无法执行update或者delete命令

解决: 执行命令SET SQL_SAFE_UPDATES = 0;修改下数据库模式

删除是针对记录而言(不可能删除某个列)


2.3 更新语句

在这里插入图片描述
update info_tab set name='汤姆',gender='女' where age=23

若是表中不存在的数据
update info_tab set age=100 where _id=3

结果: 0 row(s) affected Rows matched: 0 Changed: 0 Warnings: 0


2.4 查询语句

查询产生的结果其实是一个虚拟的结果集,并不是把真正的数据取出来传给客户端

select * from 表名 查询某表中所有的记录

select 列名1,列名2 from 表名 ; 查询部分列

select * from 表名 where<查询条件>; 查询部分列


3、手机数据库文件的导入

①在模拟机上

参考资料:如何使用MysqlWorkbench导出一整个sql文件

将以上数据库文件保存为info_tab.sql,然后直接拖入到模拟机中。(在真机上不能这样操作)
在这里插入图片描述
在虚拟机sdcard/Download下可以看到info_tab.sql文件
在这里插入图片描述

②在真机上

参考资料:如何在Android Studio内打开DDMS

在这里插入图片描述
复制SDK路径在文件夹中打开
在这里插入图片描述
进入文件夹后 tools->monitor,双击打开
在这里插入图片描述
至此,打开了DDMS

报错:

Could not open Selected VM debug port (8700). Make sure you do not have another instance of DDMS or of the eclipse plugin running. If it’s being used by something else, choose a new port number in the preferences.”
在这里插入图片描述

解决:

关闭Android studio后,再打开DDMS,无报错

参考资料:查看哪个应用占用了8700端口

疑问:
在这里插入图片描述

参考资料: Android Studio 内置模拟器开启 DDMS 后 mnt/sdcard 无法加载目录树

解决:
模拟器版本问题,切换至低版本(非 7.0即可)
开的是 7.0 以上的模拟器,6.0 的还暂时没有做测试,换成5.0 之后问题顺利解决,并且不需要再操作 mnt 下的 sdcard,只需要操作 storage 下的 sdcard 。打印 sd 的绝对路径,出来的也是 storage/sdcard。
在这里插入图片描述

疑问:mnt/adcard和storage/sdcard的区别?

切换到模拟机上,可以在storage/sdcard/Download目录下看到我们之前导入的info_tab.sql文件
在这里插入图片描述

在sdcard的根目录下导入数据库文件:

在这里插入图片描述

导入结果:

在这里插入图片描述


三、在Android中对SQLite的使用

1、界面布局

对应于数据库文件info_tab.sql

<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".MainActivity">


    <EditText
        android:id="@+id/name_edt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:hint="姓名:" />

    <EditText
        android:id="@+id/age_edt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:hint="年龄:"
        android:numeric="integer" />
    <!--     android:numeric="integer" 限制输入数据只能是整型-->

    <!--    单选按钮框-->
    <RadioGroup
        android:id="@+id/gender_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:text="性别:" />

        <RadioButton
            android:id="@+id/male"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:checked="true"
            android:text="男" />
        <!--android:checked="true" 设置该按钮为默认选项-->

        <RadioButton
            android:id="@+id/female"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:text="女" />

    </RadioGroup>

    <EditText
        android:id="@+id/id_edt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:hint="编号" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/insert_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="operate"
            android:text="添加" />
        <!--        在xml布局文件中添加点击事件属性-->

        <Button
            android:id="@+id/select_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="operate"
            android:text="查询" />

        <Button
            android:id="@+id/delete_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="operate"
            android:text="删除" />

        <Button
            android:id="@+id/update_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="operate"
            android:text="修改" />

    </LinearLayout>

</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

}

效果图:
在这里插入图片描述


2、SQLiteOpenHelper类

参考资料:Android之SQLiteOpenHelper

MainActivity

package com.example.sqlitestudy;

import androidx.appcompat.app.AppCompatActivity;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //SQLiteOpenHelper
    public void operate(View view) {

        switch (view.getId()){

            case R.id.insert_btn:

                /**
                 * 添加操作
                 */
                //参数1:上下文环境
                // 参数2:数据库名称 (如果只有一个数据库名称,那么这个数据库的位置会是在私有目录中;
                //                  如果带SD卡路径,那么数据库位置则在指定的路径下)
                String path= Environment.getExternalStorageDirectory()+"/test.sql";
                SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,1) {
                    @Override
                    public void onCreate(SQLiteDatabase db) {
                        /**
                         * 创建
                         */
                        Toast.makeText(MainActivity.this,"数据库创建",Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                        /**
                         * 升级
                         */
                        Toast.makeText(MainActivity.this,"数据库升级",Toast.LENGTH_SHORT).show();

                    }
                };
                //用于获取数据库对象
                helper.getReadableDatabase();
                break;

            case R.id.select_btn:
                break;
            case R.id.delete_btn:
                break;
            case R.id.update_btn:
                break;
        }
    }
}

helper.getReadableDatabase(); 用于获取数据库对象

有下列三种可能情况:

1、数据库存在,则直接打开数据库
2、数据库不存在,则调用数据库创建方法,再打开数据库
3、数据库存在,但版本号升高了,则调用数据库升级方法

对应的效果图:

数据库不存在的情况下:
在这里插入图片描述

①如果只有一个数据库名称

SQLiteOpenHelper helper=new SQLiteOpenHelper(this,"try8888.sql",null,1) {
	...
}

那么这个数据库的位置会是在私有目录中:data/data/包名com.example.sqlitestudy
在这里插入图片描述
②如果带SD卡路径

String path= Environment.getExternalStorageDirectory()+"/info_tab.sql";
SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,1) {
	... ...
}

那么数据库位置则在指定的路径下storage/sdcard
在这里插入图片描述

版本号升高的情况下:

SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,2){
	... ...
}

在这里插入图片描述


3、SQLiteDatabase类

参考资料:Android之SQLiteDatabase

使用到rawQuery() 、execSQL()

3.1 创建表

SQL语句:
String sql= "create table test_tab(_id integer primary key autoincrement,name varchar(20),age integer,gender varchar)";

执行SQL语句:
db.execSQL(sql);

具体代码:MainActivity

public class MainActivity extends AppCompatActivity {

    ... ...
    
    //SQLiteOpenHelper
    public void operate(View view) {

        switch (view.getId()){

            case R.id.insert_btn:

                /**
                 * 添加操作
                 */
                //参数1:上下文环境 ;参数2:数据库名称 
                String path= Environment.getExternalStorageDirectory()+"/test.sql";
                //storage/sdcard/test.sql
                Log.d("MainActivity", path);
                
                SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,1) {
                    @Override
                    /**
                     * onCreate是一个回调方法,它的执行在数据库不存在,并且已经helper.getReadableDatabase()的情况下
                     * 才会调用onCreate方法,会将返回的SQLiteDatabase对象传回到onCreate中
                     */
                    public void onCreate(SQLiteDatabase db) {
                    
                        // 创建
                        Toast.makeText(MainActivity.this,"数据库创建",Toast.LENGTH_SHORT).show();
                        
                        /**
                         * 数据库不存在,则会调用onCreate方法,那么我们可以将表的创建工作放在这里完成
                         */
                        //SQL语句
                        String sql= "create table test_tab(_id integer primary key autoincrement,name varchar(20),age integer,gender varchar)";
                        db.execSQL(sql);

                    }

                    @Override
                    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                        
                        // 升级
Toast.makeText(MainActivity.this,"数据库升级",Toast.LENGTH_SHORT).show();

                    }
                };
              
                SQLiteDatabase db=helper.getReadableDatabase();

                /**
                 * db.rawQuery() 查询
                 * db.execSQL()  添加、删除、修改、创建表 (execte 执行SQL语句)
                 */

                break;

            ... ...
        }
    }
}

将生成的test.sql文件导出到桌面上:
在这里插入图片描述

导入到MySQLWorkbench中,查看是否生成了test_tab表:

这一步出现了很大的问题,耗费了很多时间,之后发现,虽然SQL语句都是一样的,但是MySQLWorkbench更适合于MySQL。最后下载了DB Browser for SQLite,成功的完成了导入数据库的工作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


3.2 添加数据

传参方式一:

String sql="insert into test_tab (name,age,gender) values ('"+nameStr+"',"+ageStr+",'"+genderStr+"')";
db.execSQL(sql);

方式二:

String sql = "insert into test_tab (name,age,gender) values (?,?,?)";
db.execSQL(sql,new String[]{nameStr,ageStr,genderStr});

具体代码:MainActivity

public class MainActivity extends AppCompatActivity {

    private EditText nameEdt,ageEdt;
    private RadioGroup genderGp;
    private String genderStr="男";//以防什么都不选,设置默认选项为男
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化控件
        nameEdt=(EditText)findViewById(R.id.name_edt);
        ageEdt=(EditText)findViewById(R.id.age_edt);

        //读单选按钮做一个选项切换的事件监听
        genderGp=(RadioGroup)findViewById(R.id.gender_group);
        genderGp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if(checkedId==R.id.male){
                    //男
                    genderStr="男";
                }
                else{
                    //女
                    genderStr="女";
                }
            }
        });
    }

    //SQLiteOpenHelper
    //SQLiteDatabase
    public void operate(View view) {

        switch (view.getId()){

            case R.id.insert_btn:

                ... ...
               
                SQLiteDatabase db=helper.getReadableDatabase();

                //获取输入框的内容
                String nameStr=nameEdt.getText().toString();
                String ageStr=ageEdt.getText().toString();
                //插入的数据为输入框的内容
                String sql="insert into test_tab (name,age,gender) values ('"+nameStr+"',"+ageStr+",'"+genderStr+"')";
                db.execSQL(sql);
                //提示性语句
				Toast.makeText(MainActivity.this,"添加成功",Toast.LENGTH_SHORT).show();


                break;

            ... ...
        }
    }
}

效果图:
在这里插入图片描述
在这里插入图片描述


3.2 查询数据

activity_main.xml

为了让查询到的数据显示在界面上,添加一个ListView控件

  <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/stu_list"></ListView>

item.xml

查询结果的子项

<?xml version="1.0" encoding="utf-8"?><!--ListView中子项的布局,包括4个文本-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/id_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/name_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/age_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/gender_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

</LinearLayout>

MainActivity

因为查询数据也需要用到SQLiteOpenHelper和SQLiteDatabase,为了避免重复代码(同时也是因为SQLiteDatabase对象可以重复执行rawQuery()和execSQL()方法),将new SQLiteOpenHelper和获取SQLiteDatabase对象(db)的相关代码房子onCreate()方法中,并将db提取为全局变量。

参考资料:Android之SimpleCursorAdapter

1、全部查询:

String sql2="select * from test_tab";

查询结果
Cursor c=db.rawQuery(sql2,null);

2、部分查询:

String sql2="select * from test_tab";

查询条件

String idStr=idEdt.getText().toString();
if(!idStr.equals("")){
	 sql2+=" where _id="+idStr;
}

查询结果
Cursor c=db.rawQuery(sql2,null);

具体代码:

public class MainActivity extends AppCompatActivity {

    private EditText nameEdt,ageEdt,idEdt;
    private RadioGroup genderGp;
    private ListView  listView;
    private String genderStr="男";//以防什么都不选,设置默认选项为男(添加相应的xml设置)
    private SQLiteDatabase db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //参数1:上下文环境
        //参数2:数据库名称 (如果只有一个数据库名称,那么这个数据库的位置会是在私有目录中;
        //如果带SD卡路径,那么数据库位置则在指定的路径下)
        String path= Environment.getExternalStorageDirectory()+"/test.sql";
        //storage/sdcard/test.sql
        Log.d("MainActivity", path);
        SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,1) {
            @Override
            /**
             * onCreate是一个回调方法,它的执行在数据库不存在,并且已经helper.getReadableDatabase()的情况下
             * 才会调用onCreate方法,会将返回的SQLiteDatabase对象传回到onCreate中
             */
            public void onCreate(SQLiteDatabase db) {

                // 创建
                Toast.makeText(MainActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();

                /**
                 * 数据库不存在,则会调用onCreate方法,那么我们可以将表的创建工作放在这里完成
                 */
                String sql = "create table test_tab(_id integer primary key autoincrement,name varchar(20),age integer,gender varchar)";
                db.execSQL(sql);

            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                // 升级
                Toast.makeText(MainActivity.this,"数据库升级",Toast.LENGTH_SHORT).show();

            }
        };

        //用于获取数据库对象
        //1、数据库存在,则直接打开数据库
        //2、数据库不存在,则创建数据库,再打开数据库
        //3、数据库存在,但版本号升高了,则调用数据库升级方法
        db = helper.getReadableDatabase();

        ... ...
        
        idEdt=(EditText)findViewById(R.id.id_edt);

		... ...

        //在listView设置显示在界面上的数据(ListView需要Adapter)
        listView=(ListView)findViewById(R.id.listView);
    }

    //SQLiteOpenHelper
    //SQLiteDatabase
    public void operate(View view) {

        switch (view.getId()){

                ... ...

            case R.id.select_btn:

                //select * from 表名 where _id=?
                String sql2="select * from test_tab";
                //查询结果
                Cursor c=db.rawQuery(sql2,null);
//                db.rawQuery(sql,new String[]{})
                /**
                 * 下面要对查询结果进行处理,将里面的数据显示在界面上
                 */
                /*
                SimpleCursorAdapter:参数1 上下文环境;参数2 布局资源;参数3 数据源;
                 */
                SimpleCursorAdapter adapter=new SimpleCursorAdapter(
                        this,
                        R.layout.item,
                        c,
                        new String[]{"_id","name","age","gender"},
                        new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER
                        );
                listView.setAdapter(adapter);

                break;

            ... ...

        }
    }
}

效果图:
在这里插入图片描述在这里插入图片描述

疑问:为甚么要把学号属性名设置为_id?

解答:因为new SimpleCursorAdapter时候识别到数据源Cursor c没有_id ——> SimpleCursorAdapter在定义的初期对这个类的要求就是数据源必须要有_id列 ——> 如果没有,就不能识别,会产生异常 ——> 这就是为什么一开始要把学号属性名设置为_id


3.3 删除数据

public class MainActivity extends AppCompatActivity {
 
 	public void operate(View view) {

		... ...
		
	 	//获取输入框的内容
        String nameStr=nameEdt.getText().toString();
        String ageStr=ageEdt.getText().toString();
        //条件查询的条件
        String idStr = idEdt.getText().toString();
		
		... ...
		
		switch (view.getId()){
		
			... ...
	       
			case R.id.delete_btn:
	
	    		String sql3="delete from test_tab where _id=?";
	    		db.execSQL(sql3,new String[]{idStr});

    		break;
			
			... ...
		}
	}
}
  

效果图:
在这里插入图片描述在这里插入图片描述


3.4 修改数据

public class MainActivity extends AppCompatActivity {
 
 	public void operate(View view) {

		... ...
		
	 	//获取输入框的内容
        String nameStr=nameEdt.getText().toString();
        String ageStr=ageEdt.getText().toString();
        //条件查询的条件
        String idStr = idEdt.getText().toString();
		
		... ...
		
		switch (view.getId()){
		
			... ...
	       
			case R.id.update_btn:

                String sql4="update test_tab set name=?,age=?,gender=? where _id=?";
                db.execSQL(sql4,new String[]{nameStr,ageStr,genderStr,idStr});

                break;
			
			... ...
		}
	}
}
  

效果图:
在这里插入图片描述在这里插入图片描述


3.5 改进

1、在输入框输入信息,操作完一下把输入框清空一下(避免每一次的手动清除)(单选按钮回归到初始设置”男“)

private String genderStr = "男";
malerb = (RadioButton) findViewById(R.id.male);
malerb.setChecked(true);

nameEdt.setText("");
ageEdt.setText("");
idEdt.setText("");

2、在操作成功时,提示一下

Toast.makeText(this,"添加成功",Toast.LENGTH_SHORT).show();
Toast.makeText(this,"删除成功",Toast.LENGTH_SHORT).show();
Toast.makeText(this,"修改成功",Toast.LENGTH_SHORT).show();

3.6 完整代码

MainActivity

public class MainActivity extends AppCompatActivity {

    private EditText nameEdt,ageEdt,idEdt;
    private RadioGroup genderGp;
    private ListView  listView;
    private String genderStr="男";//以防什么都不选,设置默认选项为男(添加相应的xml设置)
    private SQLiteDatabase db;
    private RadioButton malerb;//让单选按钮每次操作结束后,能回复到初始状态"男"

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //参数1:上下文环境
        // 参数2:数据库名称 (如果只有一个数据库名称,那么这个数据库的位置会是在私有目录中;
        //                  如果带SD卡路径,那么数据库位置则在指定的路径下)
        String path= Environment.getExternalStorageDirectory()+"/test.sql";
        //storage/sdcard/test.sql
        Log.d("MainActivity", path);
        SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,1) {
            @Override
            /**
             * onCreate是一个回调方法,它的执行在数据库不存在,并且已经helper.getReadableDatabase()的情况下
             * 才会调用onCreate方法,会将返回的SQLiteDatabase对象传回到onCreate中
             */
            public void onCreate(SQLiteDatabase db) {

                // 创建
                Toast.makeText(MainActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();

                /**
                 * 数据库不存在,则会调用onCreate方法,那么我们可以将表的创建工作放在这里完成
                 */
                //SQL语句
                String sql = "create table test_tab(_id integer primary key autoincrement,name varchar(20),age integer,gender varchar)";
                db.execSQL(sql);

            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                // 升级
                Toast.makeText(MainActivity.this,"数据库升级",Toast.LENGTH_SHORT).show();

            }
        };


        //用于获取数据库对象
        //1、数据库存在,则直接打开数据库
        //2、数据库不存在,则创建数据库,再打开数据库
        //3、数据库存在,但版本号升高了,则调用数据库升级方法
        db = helper.getReadableDatabase();

        //初始化控件
        nameEdt=(EditText)findViewById(R.id.name_edt);
        ageEdt=(EditText)findViewById(R.id.age_edt);
        idEdt=(EditText)findViewById(R.id.id_edt);
        malerb=(RadioButton)findViewById(R.id.male);

        //读单选按钮做一个选项切换的事件监听
        genderGp=(RadioGroup)findViewById(R.id.gender_group);
        genderGp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if(checkedId==R.id.male){
                    //男
                    genderStr="男";
                }
                else{
                    //女
                    genderStr="女";
                }
            }
        });

        //在listView设置显示在界面上的数据(ListView需要Adapter)
        listView=(ListView)findViewById(R.id.listView);


    }

    //SQLiteOpenHelper
    //SQLiteDatabase
    public void operate(View view) {

        //获取输入框的内容
        String nameStr=nameEdt.getText().toString();
        String ageStr=ageEdt.getText().toString();
        //条件查询的条件
        String idStr = idEdt.getText().toString();

        switch (view.getId()){

            case R.id.insert_btn:

                /**
                 * db.rawQuery() 查询
                 * db.execSQL()  添加、删除、修改、创建表 (execte 执行SQL语句)
                 */


                //插入的数据为输入框的内容
//                String sql="insert into test_tab (name,age,gender) values ('"+nameStr+"',"+ageStr+",'"+genderStr+"')";
                String sql = "insert into test_tab (name,age,gender) values (?,?,?)";
                db.execSQL(sql,new String[]{nameStr,ageStr,genderStr});
                Toast.makeText(MainActivity.this,"添加成功",Toast.LENGTH_SHORT).show();

                break;

            case R.id.select_btn:

                //select * from 表名 where _id=?
                String sql2="select * from test_tab";

                if(!idStr.equals("")){
                    sql2+=" where _id="+ idStr;
                }
                //查询结果
                Cursor c=db.rawQuery(sql2,null);
//                db.rawQuery(sql,new String[]{})
                /**
                 * 下面要对查询结果进行处理,将里面的数据显示在界面上
                 */
                /*
                SimpleCursorAdapter:参数1 上下文环境;参数2 布局资源;参数3 数据源;
                 */
                SimpleCursorAdapter adapter=new SimpleCursorAdapter(
                        this,
                        R.layout.item,
                        c,
                        new String[]{"_id","name","age","gender"},
                        new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item},
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER
                        );
                listView.setAdapter(adapter);

                break;

            case R.id.delete_btn:

                String sql3="delete from test_tab where _id=?";
                db.execSQL(sql3,new String[]{idStr});
                Toast.makeText(this,"删除成功",Toast.LENGTH_SHORT).show();

                break;

            case R.id.update_btn:

                String sql4="update test_tab set name=?,age=?,gender=? where _id=?";
                db.execSQL(sql4,new String[]{nameStr,ageStr,genderStr,idStr});
                Toast.makeText(this,"修改成功",Toast.LENGTH_SHORT).show();

                break;
        }

        nameEdt.setText("");
        ageEdt.setText("");
        idEdt.setText("");
        malerb.setChecked(true);
    }
}

效果图:

如图显示,按下“修改”按钮后,输入框的内容全部清除
在这里插入图片描述


四、通过Android的API对SqLite数据库进行操作

参考资料:SQLiteDatabase的4个方法

1、添加insert

case R.id.insert_btn:
  
   ContentValues values = new ContentValues();
   //insert into 表名(列1,列2) values(值1,值2)
   //values(值1,值2)这一部分交给ContentValues操作
   values.put("name",nameStr);
   values.put("age",ageStr);
   values.put("gender",genderStr);
   
   long id = db.insert("test_tab",null,values);
   Toast.makeText(this,"添加成功,新学员学号是:" + id,Toast.LENGTH_SHORT).show();

   break;
   

效果图:
在这里插入图片描述


2、查询query

 case R.id.select_btn:
   
     Cursor c=db.query("test_tab",null,null,null,null,null,null);

     //参数3:数据源
     SimpleCursorAdapter adapter = new SimpleCursorAdapter(
             this, R.layout.item,c,
             new String[]{"_id","name","age","gender"},
             new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item});
     listView.setAdapter(adapter);
     break;

效果图:
在这里插入图片描述


3、删除delete

case R.id.delete_btn:

    int count = db.delete("test_tab","_id=?",new String[]{idStr});
    if(count > 0) {
        Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
    }
    
    break;

效果图:
在这里插入图片描述在这里插入图片描述

4、修改update

case R.id.update_btn:

   ContentValues values2 = new ContentValues();
    //update info_tb set 列1=xx , 列2=xxx where 列名 = 值
    values2.put("name",nameStr);
    values2.put("age",ageStr);
    values2.put("gender",genderStr);
    int count2 = db.update("test_tb",values2,"_id=?",new String[]{idStr});
    if(count2 > 0) {
        Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show();
    }

    break;

效果图:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

5、完整代码

MAinActivity2

//通过Android的API对SqLite数据库进行操作
public class MainActivity2 extends AppCompatActivity {

    private EditText nameEdt,ageEdt,idEdt;
    private RadioGroup genderGp;
    private ListView listView;
    private String genderStr="男";//以防什么都不选,设置默认选项为男(添加相应的xml设置)
    private SQLiteDatabase db;
    private RadioButton malerb;//让单选按钮每次操作结束后,能回复到初始状态"男"

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //参数1:上下文环境
        // 参数2:数据库名称 (如果只有一个数据库名称,那么这个数据库的位置会是在私有目录中;
        //                  如果带SD卡路径,那么数据库位置则在指定的路径下)
        String path= Environment.getExternalStorageDirectory()+"/test.sql";
        //storage/sdcard/test.sql
        Log.d("MainActivity", path);
        SQLiteOpenHelper helper=new SQLiteOpenHelper(this,path,null,2) {
            @Override
            /**
             * onCreate是一个回调方法,它的执行在数据库不存在,并且已经helper.getReadableDatabase()的情况下
             * 才会调用onCreate方法,会将返回的SQLiteDatabase对象传回到onCreate中
             */
            public void onCreate(SQLiteDatabase db) {

                // 创建
                Toast.makeText(MainActivity2.this, "数据库创建", Toast.LENGTH_SHORT).show();

                /**
                 * 数据库不存在,则会调用onCreate方法,那么我们可以将表的创建工作放在这里完成
                 */
                //SQL语句
//                String sql = "create table test_tab(_id integer primary key autoincrement,name varchar(20),age integer,gender varchar)";
//                db.execSQL(sql);

            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                // 升级
                Toast.makeText(MainActivity2.this,"数据库升级",Toast.LENGTH_SHORT).show();

            }
        };


        //用于获取数据库对象
        //1、数据库存在,则直接打开数据库
        //2、数据库不存在,则创建数据库,再打开数据库
        //3、数据库存在,但版本号升高了,则调用数据库升级方法
        db = helper.getReadableDatabase();

        //初始化控件
        nameEdt=(EditText)findViewById(R.id.name_edt);
        ageEdt=(EditText)findViewById(R.id.age_edt);
        idEdt=(EditText)findViewById(R.id.id_edt);
        malerb=(RadioButton)findViewById(R.id.male);

        //读单选按钮做一个选项切换的事件监听
        genderGp=(RadioGroup)findViewById(R.id.gender_group);
        genderGp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if(checkedId==R.id.male){
                    //男
                    genderStr="男";
                }
                else{
                    //女
                    genderStr="女";
                }
            }
        });

        //在listView设置显示在界面上的数据(ListView需要Adapter)
        listView=(ListView)findViewById(R.id.listView);


    }

    //SQLiteOpenHelper
    //SQLiteDatabase
    public void operate(View view) {

        //获取输入框的内容
        String nameStr=nameEdt.getText().toString();
        String ageStr=ageEdt.getText().toString();
        //条件查询的条件
        String idStr = idEdt.getText().toString();

        switch (view.getId()){

            case R.id.insert_btn:

                /**在SqliteDatabase类下,提供四个方法
                 * insert(添加)、delete(删除)、update(修改)、query(查询)
                 * 都不需要写sql语句
                 * 参数1:你所要操作的数据库表的名称
                 * 参数2:可以为空的列  如果第三个参数是null或者说里面没有数据
                 * 那么我们的sql语句就会变为insert into info_tb () values ()  ,在语法上就是错误的
                 * 此时通过参数3指定一个可以为空的列,语句就变成了insert into info_tb (可空列) values (null)
                 */
                ContentValues values = new ContentValues();
                //insert into 表名(列1,列2) values(值1,值2)
                //values(值1,值2)这一部分交给ContentValues操作
                values.put("name",nameStr);
                values.put("age",ageStr);
                values.put("gender",genderStr);
                long id = db.insert("test_tab",null,values);
                Toast.makeText(this,"添加成功,新学员学号是:" + id,Toast.LENGTH_SHORT).show();

                break;

            case R.id.select_btn:

                /**
                 * 参数1:表名
                 * 参数2:你所要查询的列 {"name","age","gender"} 如果想要查询所有的话,传入null或{*}
                 * 参数3:条件(针对列)
                 * 参数5:分组
                 * 参数6:当 group by对数据进行分组后,可以通过having来去除不符合条件的组
                 * 参数7:排序
                 */
                Cursor c=db.query("test_tab",null,null,null,null,null,null);

                //参数3:数据源
                SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                        this, R.layout.item,c,
                        new String[]{"_id","name","age","gender"},
                        new int[]{R.id.id_item,R.id.name_item,R.id.age_item,R.id.gender_item});
                listView.setAdapter(adapter);
                break;

            case R.id.delete_btn:

                int count = db.delete("test_tab","_id=?",new String[]{idStr});
                if(count > 0) {
                    Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
                }

                break;

            case R.id.update_btn:

                ContentValues values2 = new ContentValues();
                //update info_tb set 列1=xx , 列2=xxx where 列名 = 值
                values2.put("name",nameStr);
                values2.put("age",ageStr);
                values2.put("gender",genderStr);
                int count2 = db.update("test_tab",values2,"_id=?",new String[]{idStr});
                if(count2 > 0) {
                    Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show();
                }

                break;
        }

        nameEdt.setText("");
        ageEdt.setText("");
        idEdt.setText("");
        malerb.setChecked(true);
    }
}


五、SQLite总结

SQLite数据库是一个轻量级的数据库,本质上是一个二进制文件。
在这里插入图片描述


六、数据库操作的封装

Student

public class Student{

    //私有属性
    private int id;
    private String name;
    private int age;
    private String gender;

    //无参构造
    public Student(){

    }

    //有参构造(3个参数)
    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    //有参构造(4个参数)
    public Student(int id, String name, int age, String gender) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    //创建的setter和getter方法
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}

StudentDao

//Dao(data access object)数据访问对象
public class StudentDao {

    //之后的数据库操作都需要用到SQLiteDatabase
    private SQLiteDatabase db;

    //先实例化StudentDao这个类,然后实例化出来的对象再点db的方法,所以,在这个类的实例化过程中,将db对象获取到
    public StudentDao(final Context context){

        String path= Environment.getExternalStorageDirectory()+"/test.sql";

        SQLiteOpenHelper helper=new SQLiteOpenHelper(context,path,null,2) {
            @Override
            public void onCreate(SQLiteDatabase db) {
                // 创建
            }
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                // 升级
            }
        };

        //获取对象
        db=helper.getReadableDatabase();

    }
    
	 /**
     * 添加操作
     */
    public void addStudent(){

    }

    /**
     * 查询操作
     */
    public void getStudent(){

    }

    /**
     * 删除操作
     */
    public void deleteStudent(){

    }

    /**
     * 修改操作
     */
    public void uodatae(){

    }
}

MainActivity3

public class MainActivity3 extends AppCompatActivity {

    private EditText nameEdt, ageEdt, idEdt;
    private RadioGroup genderGp;
    private ListView listView;
    private String genderStr = "男";//以防什么都不选,设置默认选项为男(添加相应的xml设置)
    private RadioButton malerb;//让单选按钮每次操作结束后,能回复到初始状态"男"

    private StudentDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //实例化
        dao = new StudentDao(this);

        //初始化控件
        nameEdt = (EditText) findViewById(R.id.name_edt);
        ageEdt = (EditText) findViewById(R.id.age_edt);
        idEdt = (EditText) findViewById(R.id.id_edt);
        malerb = (RadioButton) findViewById(R.id.male);

        //读单选按钮做一个选项切换的事件监听
        genderGp = (RadioGroup) findViewById(R.id.gender_group);
        genderGp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if (checkedId == R.id.male) {
                    //男
                    genderStr = "男";
                } else {
                    //女
                    genderStr = "女";
                }
            }
        });
		
		listView=(ListView)findViewById(R.id.listView);

    }

    public void updataeStudent(View view) {

        //获取输入框的内容
        String nameStr = nameEdt.getText().toString();
        String ageStr = ageEdt.getText().toString();
        //条件查询的条件
        String idStr = idEdt.getText().toString();

        switch (view.getId()) {

            case R.id.insert_btn:


                Student stu = new Student(nameStr, Integer.parseInt(ageStr), genderStr);
                dao.addStudent(stu);
                Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();


                break;

            case R.id.select_btn:


                break;

            case R.id.delete_btn:


                break;

            case R.id.update_btn:


                break;
        }

        nameEdt.setText("");
        ageEdt.setText("");
        idEdt.setText("");
        malerb.setChecked(true);
    }
}


1、添加操作

StudentDao

public void addStudent(Student stu){

   String sql="insert into test_tab (name,age,gender) values (?,?,?)";
   db.execSQL(sql,new String[]{stu.getName(),stu.getAge()+"",stu.getGender()});

}

MainActivity3

case R.id.insert_btn:
	  Student stu = new Student(nameStr, Integer.parseInt(ageStr), genderStr);
	  dao.addStudent(stu);
	  Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
	  break;

效果图:
在这里插入图片描述


2、查询操作

StudentDao

public Cursor getStudent(String...strings){

     //1、查询所有(没有参数)
     String sql="select * from test_tab";
     //2、含条件查询(姓名、年龄、编号)(参数形式:第一个参数指明条件,第二个参数指明条件值)
     if(strings.length!=0){
         sql+=" where "+strings[0]+"='"+strings[2]+"'";//在查询时,age用''修饰是没关系的
     }
     Cursor c=db.rawQuery(sql,null);
     return c;

}

MainActivity3

case R.id.select_btn:

    String key = "", value = "";
    //需要判断nameEdt ageEdt idEdt哪个不为空,不为空的就是getStudent()需要传入的参数
    if (!nameStr.equals("")) {
        key = "name";
        value = nameStr;
    } else if (!ageStr.equals("")) {
        key = "age";
        value = ageStr;
    } else if (!idStr.equals("")) {
        key = "_id";
        value = idStr;
    }

    Cursor c;
    if (key.equals("")) {
        c = dao.getStudent();
    } else {
        c = dao.getStudent(key, value);
    }

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(
            this, R.layout.item, c,
            new String[]{"_id", "name", "age", "gender"},
            new int[]{R.id.id_item, R.id.name_item, R.id.age_item, R.id.gender_item});
    listView.setAdapter(adapter);
    break;

效果图:
在这里插入图片描述


3、删除操作

StudentDao

public void deleteStudent(String...strings){

       String sql="delete from test_tab where "+strings[0]+"='"+strings[1]+"'";
       db.execSQL(sql);

}

MainActivity3

因为delete中也需要”判断输入框的内容是否为空“的操作,所以将这部分提取为一个getParams()方法

case R.id.delete_btn:

    String[] params = getParams(nameStr, ageStr, idStr);
    dao.deleteStudent(params[0],params[1]);
    Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
    break;
public String[] getParams(String nameStr, String ageStr, String idStr) {
    String[] params = new String[2];
    if (!nameStr.equals("")) {
        params[0] = "name";
        params[1] = nameStr;
    } else if (!ageStr.equals("")) {
        params[0] = "age";
        params[1] = ageStr;
    } else if (!idStr.equals("")) {
        params[0] = "_id";
        params[1] = idStr;
    }
    return params;
}

效果图:
在这里插入图片描述在这里插入图片描述


4、修改操作

StudentDao

public void updateStudent(Student stu){

	   String sql = "update test_tab set name=?,age=?,gender=? where _id=?";
	   db.execSQL(sql,new Object[]{stu.getName(),stu.getAge(),stu.getGender(),stu.getId()});
}

MainActivity3

case R.id.update_btn:

     Student stu2=new Student(Integer.parseInt(idStr),nameStr,Integer.parseInt(ageStr),genderStr);
     dao.updateStudent(stu2);
     Toast.makeText(this,"修改成功",Toast.LENGTH_SHORT).show();
     break;

效果图:
在这里插入图片描述在这里插入图片描述


5、小结

StudentDao

另一种查询方法

moveToNext() 可以将cursor移动到下一条记录上

public ArrayList<Student> getStudentInList(String...strings){
    ArrayList<Student> list=new ArrayList<>();
    Cursor c=getStudent(strings);
    while (c.moveToNext()){
        int id=c.getInt(0);
        String name=c.getString(1);
        int age=c.getInt(2);
        String gender=c.getString(3);
        Student s=new Student(id,name,age,gender);
        list.add(s);
    }
    return list;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值