boat
- 视频学习来源b站:程序猿拉大锯
- 使用sqlite帮助类。【ctrl+N】查找SQLiteOpenHelper发现他是个抽象类,【ctrl+H】查看继承关系,其没有实现类,那待会咱们就需要继承。
- Sql语句传送门,菜鸟教程
- 思维导图
1. 【创建数据库】实现升级版本
1.建立一个类DatabaseHelper继承SQLiteOpenHelper
- 实现两个成员方法onCreate()和onUpgrade() 分别是数据库第一次创建时的回调和升级数据库时的回调
- 复写一个四参构造方法
a. 上下文
b.数据库名
c.游标工厂(用来创建游标对象,类指针,指向某一行,该行有对应的许多字段,null为默认)
d.版本号(只能升级不能降级) - 建立一个类Constances,写静态成员变量用于记录成员信息,供2中的构造方法调用,
- 运行虚拟机找到data/data/com.ywjh.databasedemo
1)目前只有两个文件夹
2)new一个help对象,传入当前上下文,就有数据库了,暂时告别sql语句
DatabaseHelper helper=new DatabaseHelper(this);//创建对象,调用构造方法创建数据库
helper.getWritableDatabase();//获取数据库
2. 完善增删改查,首先确定字段
- 理所当然的在DatabaseHelper的oncreate方法中初始化创建字段(把原数据库先删了),Constances中写个静态表名
public void onCreate(SQLiteDatabase db) {
Log.d(TAG,"创建数据库");
//创建字段
//sql create table table_name(id integer,name varchar(50),age integer,salary integer)//sqlite 都是varchar
String sql="create table "+Constants.TABLE_NAME+"(_id integer,name varchar,age integer,salary integer)";
db.execSQL(sql);//执行语句
}
- 使用Sqlite Expert观察4个字段就有了
- 使用一下onupdate方法添加字段(数据库名,老版本号,新版本号)
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG,"升级数据库");
//sql:alter table table_name add phone integer;
String sql="alter table "+Constants.TABLE_NAME+" add phone integer";
db.execSQL(sql);
}
运行之后发现没变化,查看说明要更新版本号才会调用变化,我们此时更改写好的静态调用版本号(后台会对比,不同则正常old和new版本号进行升级)。运行发现升级的log已经打印了,刷新查看就有了,还多测试了一个sno。
- 为了方便观察版本号更新迭代,可以加入switch(ps:用逗号隔开字段时可能会显示检查错误,不过是误报)
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG,"升级数据库");
//sql:alter table table_name add phone integer;
String sql;
switch (oldVersion){
case 1:
//添加address和phone字段
sql="alter table "+Constants.TABLE_NAME+" add phone integer,address varchar";
db.execSQL(sql);
break;
case 2:
sql="alter table "+Constants.TABLE_NAME+" add sno integer";
db.execSQL(sql);
break;
case 3:
sql="alter table "+Constants.TABLE_NAME+" add address varchar";
db.execSQL(sql);
break;
}
}
2.【增删改查】
public class Dao {
private static final String TAG ="Dao Query" ;
private final DatabaseHelper mhelper;
public Dao(Context context){
//创建并拿到数据库
mhelper= new DatabaseHelper(context);
//helper.getWritableDatabase();
}
public void insert(){
//写入
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
String sql="insert into "+Constants.TABLE_NAME+"(_id,name,age,salary,phone,sno,address) values(?,?,?,?,?,?,?)";
db.execSQL(sql,new Object[]{
1,"Kirika",18,1,12345,"201701014145","china"});
db.close();//关闭相关引用
}
public void delete(){
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
String sql="delete from "+Constants.TABLE_NAME+ " where age=18";
db.execSQL(sql);
db.close();//关闭相关引用
}
public void update(){
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
String sql="update "+Constants.TABLE_NAME+ " set salary=2 where age=18";
db.execSQL(sql);
db.close();//关闭相关引用
}
public void query(){
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
String sql="select * from "+Constants.TABLE_NAME;
//db.execSQL(sql); 这个方法无返回值的
Cursor cursor= db.rawQuery(sql,null);
while (cursor.moveToNext()){
int index=cursor.getColumnIndex("name");
String name=cursor.getString(index);
Log.d(TAG,name);
//cursor.getString(1);//获取第一列,从0开始且不含首为计数列
}
cursor.close();//资源型关闭
db.close();//关闭相关引用
}
}
3.【编写测试类】测试的好处在于每个方法都可以直接运行,先不用加入到核心代码中
- 新版不用继承AndroidTextCase而是
a. 类前加上@RunWith(AndroidJUnit4.class)
b.方法前加上 @Test
c.Context appContext = InstrumentationRegistry.getTargetContext();获取上下文
d.运行报错说找不了test例子,就纳闷了,后来查阅将gradle工具run test using改为IDEA重启即可。
- 其他测试了也没问题
package com.ywjh.databasedemo;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class TestDatabase {
Context appContext = InstrumentationRegistry.getTargetContext();
@Test
public void testCreate() {
//测试创建数据库
}
@Test
public void testInsert() {
//测试插入数据
//System.out.println("111");
Dao dao=new Dao(appContext);
dao.insert();
}
@Test
public void testDelete() {
//测试删除数据
Dao dao=new Dao(appContext);
dao.delete();
}
@Test
public void testUpdate() {
//测试删除数据
Dao dao=new Dao(appContext);
dao.update();
}
@Test
public void testQuery() {
//测试查找数据
Dao dao=new Dao(appContext);
dao.query();
}
}
4.【安卓API_CRUD】sql语句较为严格,空格之类的比较麻烦,google提供封装了语法API
改写为面向对象式的,完全OK
public void insert(){
//写入
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
/* String sql="insert into "+Constants.TABLE_NAME+"(_id,name,age,salary,phone,sno,address) values(?,?,?,?,?,?,?)";
db.execSQL(sql,new Object[]{1,"Kirika",18,1,12345,"201701014145","china"});*/
ContentValues values=new ContentValues();//接着添加数据
values.put("_id",2);
values.put("name","larrpage");
values.put("age",18);
values.put("salary",23243);
values.put("phone",12345);
values.put("sno","2017023432");
values.put("address","china");
db.insert(Constants.TABLE_NAME,null,values);//二参数是可否为空字段 三参数是填充内容 hashMap结构
db.close();//关闭相关引用
}
public void delete(){
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
/*String sql="delete from "+Constants.TABLE_NAME+ " where age=18";
db.execSQL(sql);*/
int result=db.delete(Constants.TABLE_NAME,null,null);//判断条件与返回值 返回为删除行数
System.out.println("删除结果为:"+result);
db.close();//关闭相关引用
}
public void update(){
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
/* String sql="update "+Constants.TABLE_NAME+ " set salary=2 where age=18";
db.execSQL(sql);*/
ContentValues values=new ContentValues();//接着添加数据
values.put("phone",2222222);
db.update(Constants.TABLE_NAME,values,null,null);//null为所有记录
db.close();//关闭相关引用
}
public void query(){
SQLiteDatabase db= mhelper.getWritableDatabase();//获取到数据库后对表进行查找
/* String sql="select * from "+Constants.TABLE_NAME;
//db.execSQL(sql); 这个方法无返回值的
Cursor cursor= db.rawQuery(sql,null);
while (cursor.moveToNext()){
int index=cursor.getColumnIndex("name");
String name=cursor.getString(index);
Log.d(TAG,name);
//cursor.getString(1);//获取第一列,从0开始且不含首为计数列
}
cursor.close();//资源型关闭*/
Cursor cursor=db.query(Constants.TABLE_NAME,null,null,null,null,null,null);
while (cursor.moveToNext()){
int id=cursor.getInt(0);
String name=cursor.getString(1);
Log.d(TAG,"id=="+id+" name"+name);
}
cursor.close();
db.close();//关闭相关引用
}
5.【数据库事务Transcation】安全性与高效性
1.情景
- 每月15号,公司发工资,财务有100000,要-1200,你的账号+1200,此时停电了,公司减了,但账号未增加
2. 测试安全性编码
- 创建安卓项目后再次新建类继承SQLiteOpenHelpter,写个构造方法用于初始化
public class DatabaseHelpter extends SQLiteOpenHelper {
public DatabaseHelpter(Context context) {
super(context,"account.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table account(_id integer,name varchar,money integer)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
- 编写测试类
1)完成数据库构造方法创建数据库并且伴生oncreate()创建Account表,接着测试插入和更新(流程都是通过创建的Helpter对象返回的对应数据库,然后获取对应权限,返回权限对象,通过对象进行sql操作
)
@RunWith(AndroidJUnit4.class)
public class TestDataBase {
Context appContext = InstrumentationRegistry.getTargetContext();
@Test//用于执行创建的方法
public void testDataBase(){
//测试数据库创建
System.out.println("1111111");
DatabaseHelpter helpter=new DatabaseHelpter(appContext);
helpter.getReadableDatabase();
}
@Test
public void testInsert() {
//测试插入数据的方法
//System.out.println("111");
DatabaseHelpter helpter=new DatabaseHelpter(appContext);
SQLiteDatabase db=helpter.getReadableDatabase();
db.execSQL("insert into account values(1,'tony',100000)");
db.execSQL("insert into account values(2,'myc',0)");
db.close();
}
@Test
public void testUpdate() {
//测试插入更新数据的方法
//System.out.println("111");
DatabaseHelpter helpter=new DatabaseHelpter(appContext);
SQLiteDatabase db=helpter.getReadableDatabase();
db.execSQL("update account set money = 100000-1200 where name= 'tony'");
//人为模拟一次异常 发现只执行了第一句
int i=10/0;//分母为0出异常
db.execSQL("update account set money =1200 where name= 'myc'");
}
}
执行后发现人为中断前的操作都执行了
- 改写update,加入try-catch与事务套餐
@Test
public void testUpdate() {
//测试插入数据
//System.out.println("111");
DatabaseHelpter helpter=new DatabaseHelpter(appContext);
SQLiteDatabase db=helpter.getReadableDatabase();
db.beginTransaction();
//人为模拟一次异常 发现只执行了第一句
try{
db.execSQL("update account set money = 100000-600 where name= 'tony'");
int i=10/0;//分母为0发生异常
db.execSQL("update account set money =1200 where name= 'myc'");
db.setTransactionSuccessful();
}catch(Exception e){
//处理异常
throw new RuntimeException("停电了");
}finally {
db.endTransaction();
db.close();
}
}
这里我们修改了中断前的操作,运行发现中断前后都不执行,数据库不变
3.测试高效性编码
- 修改insert,使其插入6000条数
@Test
public void testInsert() {
//测试插入数据
//System.out.println("111");
DatabaseHelpter helpter=new DatabaseHelpter(appContext);
SQLiteDatabase db=helpter.getReadableDatabase();
long start=System.currentTimeMillis();
for(int i=0;i<3000;i++){
db.execSQL("insert into account values(1,'company',200000)");
db.execSQL("insert into account values(2,'my_count',0)");
}
Log.d(TAG,"usetime =="+(System.currentTimeMillis()-start));
System.out.println("使用时间为:"+(System.currentTimeMillis()-start));
/* db.execSQL("insert into account values(1,'tony',100000)");
db.execSQL("insert into account values(2,'myc',0)");*/
db.close();
}
- 开启事务,十分高效
开启和关闭,十分简洁
。 原理是:
a:原操作是打开数据库,插入数据,关闭数据库(耗时较多)
b:运用事务:将数据存入存,通过内存写入,同目录下的journay就是防止文件过大而生成的temp文件。
@Test
public void testInsert() {
//测试插入数据
//System.out.println("111");
DatabaseHelpter helpter=new DatabaseHelpter(appContext);
SQLiteDatabase db=helpter.getReadableDatabase();
long start=System.currentTimeMillis();
db.beginTransaction();
for(int i=0;i<3000;i++){
db.execSQL("insert into account values(1,'company',200000)");
db.execSQL("insert into account values(2,'my_count',0)");
}
db.endTransaction();
Log.d(TAG,"usetime =="+(System.currentTimeMillis()-start));
System.out.println("事务使用时间为:"+(System.currentTimeMillis()-start));
/* db.execSQL("insert into account values(1,'tony',100000)");
db.execSQL("insert into account values(2,'myc',0)");*/
db.close();
}
下面是对比图