boat
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();
}
下面是對比圖