SQLite的使用和實現一個簡單的數據庫框架

SQLite的基本知識

SQLite經常使用的數據類型

字段 做用
char(n) 固定n長度的字串
varchar(n) 長度不固定的字符串,n表示最大的長度
nchar(n) 同char,不一樣的是能夠用來存儲中文
nvarchar(n) 同varchar,不一樣的是能夠用來存儲中文
text 存儲文本
blob 存儲二進制文件
int 整形
integer 整形
bigint 整形
float 單精度類型
double 雙精度浮點

這裏intintegerbigint的具體區別,還沒弄明白。若是有哪一個大佬瞭解,請在評論區指導一下😄😄php

建立和刪除數據表

建立表的爲語法:html

create table database_name.table_name(
   column1 datatype  primary key(one or more columns),
   column2 datatype,
   column3 datatype,
   .....
   columnN datatype,
);
複製代碼

刪除數據表的語法爲java

drop table database_name.table_name;
複製代碼

插入數據

插入數據表的語法爲:android

insert into table_name [(column1, column2, column3,...columnN)]  
values (value1, value2, value3,...valueN);
或
//注意:這種方式要確保值的順序與列在表中的順序一致
insert into table_name values (value1,value2,value3,...valueN);
複製代碼

刪除數據

刪除數據的語法爲:sql

delete from table_name [條件];
複製代碼

若是沒有刪除數據的條件,默認刪除全部數據;若是指定了條件,則刪除符合條件的 數據數據庫

更新數據

語法爲:bash

update table_name
set column1 = value1, column2 = value2...., columnN = valueN [條件];
複製代碼

查詢數據

語法爲:app

//查詢指定字段(列)的值
SELECT column1, column2, columnN FROM table_name;
或
//查詢全部字段的值
SELECT * FROM table_name;
複製代碼

SQLite的邏輯運算符

運算符 描述
AND AND 運算符容許在一個 SQL 語句的 WHERE 子句中的多個條件的存在
BETWEEN BETWEEN 運算符用於在給定最小值和最大值範圍內的一系列值中搜索值
EXISTS EXISTS 運算符用於在知足必定條件的指定表中搜索行的存在
IN IN 運算符用於把某個值與一系列指定列表的值進行比較
NOT IN IN 運算符的對立面,用於把某個值與不在一系列指定列表的值進行比較
LIKE LIKE 運算符用於把某個值與使用通配符運算符的類似值進行比較
GLOB GLOB 運算符用於把某個值與使用通配符運算符的類似值進行比較。GLOB 與 LIKE 不一樣之處在於,它是大小寫敏感的
NOT NOT 運算符是所用的邏輯運算符的對立面。好比 NOT EXISTS、NOT BETWEEN、NOT IN,等等。它是否認運算符
OR OR 運算符用於結合一個 SQL 語句的 WHERE 子句中的多個條件
IS NULL NULL 運算符用於把某個值與 NULL 值進行比較
IS IS 運算符與 = 類似
IS NOT IS NOT 運算符與 != 類似
|| 鏈接兩個不一樣的字符串,獲得一個新的字符串
UNIQUE UNIQUE 運算符搜索指定表中的每一行,確保惟一性(無重複)

where

where用來過濾數據的,例如select * from employee where salary >= 65000;是指查詢工資高於65000的員工的數據,使用where salary >= 65000;來過濾數據框架

and/or

and至關於邏輯與運算,只有條件全爲真時,結果才爲真;or至關於邏輯或運算,只要其中一個條件爲真,結果就爲真。ide

LIKE

LIKE運算符是用來匹配通配符指定模式的文本值。若是搜索表達式與模式表達式匹配,LIKE 運算符將返回真(true),也就是 1。這裏有兩個通配符與 LIKE 運算符一塊兒使用:

  • 百分號 (%)
  • 下劃線 (_)

百分號(%)表明零個、一個或多個數字或字符。下劃線(_)表明一個單一的數字或字符。這些符號能夠被組合使用。

下面一些實例演示了 帶有 '%' 和 '_' 運算符的 LIKE 子句不一樣的地方:

語句 描述
WHERE SALARY LIKE '200%' 查找以 200 開頭的任意值
WHERE SALARY LIKE '%200%' 查找任意位置包含 200 的任意值
WHERE SALARY LIKE '_00%' 查找第二位和第三位爲 00 的任意值
WHERE SALARY LIKE '2_%_%' 查找以 2 開頭,且長度至少爲 3 個字符的任意值
WHERE SALARY LIKE '%2' 查找以 2 結尾的任意值
WHERE SALARY LIKE '_2%3' 查找第二位爲 2,且以 3 結尾的任意值
WHERE SALARY LIKE '2___3' 查找長度爲 5 位數,且以 2 開頭以 3 結尾的任意值

GLOB

GLOB 運算符是用來匹配通配符指定模式的文本值。若是搜索表達式與模式表達式匹配,GLOB 運算符將返回真(true),也就是 1。與 LIKE 運算符不一樣的是,GLOB 是大小寫敏感的,對於下面的通配符,它遵循 UNIX 的語法。

  • 星號 (*)
  • 問號 (?)

星號(*)表明零個、一個或多個數字或字符。問號(?)表明一個單一的數字或字符。這些符號能夠被組合使用。

語句 描述
WHERE SALARY GLOB '200*' 查找以 200 開頭的任意值
WHERE SALARY GLOB '200' 查找任意位置包含 200 的任意值
WHERE SALARY GLOB '?00*' 查找第二位和第三位爲 00 的任意值
WHERE SALARY GLOB '2??' 查找以 2 開頭,且長度至少爲 3 個字符的任意值
WHERE SALARY GLOB '*2' 查找以 2 結尾的任意值
WHERE SALARY GLOB '?2*3' 查找第二位爲 2,且以 3 結尾的任意值
WHERE SALARY GLOB '2???3' 查找長度爲 5 位數,且以 2 開頭以 3 結尾的任意值

LIMIT

子句用於限制由 SELECT 語句返回的數據數量

ORDER BY

子句是用來基於一個或多個列按升序或降序順序排列數據。

ORDER BY 子句的基本語法以下:

SELECT column-list 
FROM table_name 
[WHERE condition] 
[ORDER BY column1, column2, .. columnN] [ASC | DESC];//ASC升序排序,DESC降序排序
複製代碼

GROUP BY

子句用於與 SELECT 語句一塊兒使用,來對相同的數據進行分組。 在 SELECT 語句中,GROUP BY 子句放在 WHERE 子句以後,放在 ORDER BY 子句以前

HAVING

子句容許指定條件來過濾將出如今最終結果中的分組結果。 WHERE 子句在所選列上設置條件,而 HAVING 子句則在由 GROUP BY 子句建立的分組上設置條件。

DISTINCT

關鍵字與 SELECT 語句一塊兒使用,來消除全部重複的記錄,並只獲取惟一一次記錄。 有可能出現一種狀況,在一個表中有多個重複的記錄。當提取這樣的記錄時,DISTINCT 關鍵字就顯得特別有意義,它只獲取惟一一次記錄,而不是獲取重複記錄。

select distinct name from company;
複製代碼

建立數據庫

操做數據庫,要使用SQLiteOpenHelper,因爲SQLiteOpenHelper是抽象類,使用要實現它,並重寫它的 onCreate(), onUpgrade()方法

public class MyDatabase extends SQLiteOpenHelper {
    //建立表
    public static final String CreateTable_my="create Table user(" +
            "id primary key, name text, sex text , age integer, password text)";

    Context myContext;
    /** * * @param context * @param name 建立數據庫的名字 * @param factory 用於返回自定義的Cursor,通常填null * @param version 表示當前數據庫的版本號,可用於對數據庫進行升級 */
    public Mydatabase(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        myContext=context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
          sqLiteDatabase.execSQL(CreateTable_my);//建立表
          Toast.makeText(myContext, "數據表建立成功", Toast.LENGTH_SHORT).show();
    }
  
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}
複製代碼

建立MyDatabase的對象

//first.db是數據庫名
private Mydatabase base=new Mydatabase(DatabaseActivity.this,"first.db",null,1);
複製代碼

生成數據庫(會在/data/data/<package name>/databases/目錄下創建數據庫)

base.getWritableDatabase();
//或者
base.getReadableDatabase()
複製代碼

兩個方法的不一樣處:
getWritableDatabase()返回一個可對數據庫進行讀寫操做的對象,會拋出異常
getReadableDatabase()返回一個以只讀方法打開的數據庫,不會拋出異常

添加表

public class Mydatabase extends SQLiteOpenHelper {
//用戶表
public static final String CreateTable_user="create Table user(" +
            "id primary key, name text, sex text , age integer, password text)";

//建立另外一個表,班級表
public static final String CreateTable_me="create Table clazz(" +
            "id primary key, className text, teacher text)";

    Context myContext;

    /** * * @param context * @param name 建立數據庫的名字 * @param factory 用於返回自定義的Cursor,通常填null * @param version 表示當前數據庫的版本號,可用於對數據庫進行升級 */
    public Mydatabase(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        myContext=context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
          sqLiteDatabase.execSQL(CreateTable_user);
          sqLiteDatabase.execSQL(CreateTable_me);
          Toast.makeText(myContext, "數據庫建立成功", Toast.LENGTH_SHORT).show();
    }
    //當 version 中的值改變時,會調用這個方法,經過這個方法,刪除原來的表,再調用onCreate()方法生成兩個表
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
           sqLiteDatabase.execSQL("drop table if exists user");
           sqLiteDatabase.execSQL("drop table if exists clazz");
           onCreate(sqLiteDatabase);
           Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show();
    }
}
複製代碼

操做表

  • 添加數據
//經過getWritableDatabase()獲取SQLiteOpenHelper來操做數據庫
SQLiteDatabase database= base.getWritableDatabase();
                ContentValues values=new ContentValues();
                values.put("name","小明");
                values.put("sex","男");
                values.put("password","12hjfgikldgislk");
                values.put("age",18);
                //參數 1.表的名字 2.用於未指定添加數據的狀況下給某些可爲空的列自動賦值NULL 
                //3. ContentValues 對象
                database.insert("user",null,values);
複製代碼
  • 更新數據
SQLiteDatabase database= base.getWritableDatabase();
                ContentValues values=new ContentValues();
                values.put("name","小軍");  
                //後面的兩個參數是操做的限制條件
                database.update("user",values,"age=?",new String[]{"18"});
複製代碼
  • 刪除數據
SQLiteDatabase database= base.getWritableDatabase();
//後面的兩個參數是操做的限制條件,用來約束刪除哪幾行,若是不指定就刪除全部行
database.delete("user",null,null);
複製代碼
  • 查詢數據
SQLiteDatabase database=base.getWritableDatabase();
                Cursor cursor= database.query("user",null,null,null,null,null,null);
                if (cursor.moveToFirst()){
                    do{
                        Log.d("name:",cursor.getString(cursor.getColumnIndex("name")));
                        Log.d("age:",cursor.getString(cursor.getColumnIndex("age")));
                        Log.d("sex:",cursor.getString(cursor.getColumnIndex("sex")));
                        Log.d("password:",cursor.getString(cursor.getColumnIndex("password")));
                    }while (cursor.moveToNext());
                }
                cursor.close();
複製代碼

query的參數以下:

  • 使用sql直接對數據庫進行操做
SQLiteDatabase database=base.getWritableDatabase();
database.execSQL();
database.rawQuery();//只有查詢數據的時候才調用這個方法
複製代碼

仿照LitePal實現一個簡易的數據庫框架SimpleDatabase

SimpleDatabase的使用

  1. 先在asset文件中建立my_database.xml

my_database.xml以下:

<?xml version="1.0" encoding="UTF-8" ?>
<database name="test.db" version="1">
    <!--class屬性是數據表Bean的全路徑 -->
    <table class="com.example.mylibrary.Employee"/>
</database>
複製代碼

Employee的源碼以下

public class Employee {
   private int id;

   private String name;

   private char sex;

    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 char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        //使用id做爲員工的惟一標識
        return Integer.toString(id);
    }
}
複製代碼

注意:SimpleDatabase經過toString來區別兩個對象是否爲同一對象,如Employee就使用id做爲標識符。

  1. AndroidManifest.xml中加入android:name="com.example.databaselibrary.MyApplication"
<application ... android:name="com.example.databaselibrary.MyApplication" >
複製代碼
  1. 使用SimpleDatabase
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        SimpleDatabase.newInstance().create();//初始化
        
         Employee employee =new Employee();
                employee.setId(1);
                employee.setName("a");
                employee.setSex('男');
                SimpleDatabase.saveAndUpdate(employee);

                Employee employee1 =new Employee();
                employee1.setId(2);
                employee1.setName("b");
                employee1.setSex('男');
                SimpleDatabase.saveAndUpdate(employee1);

                Employee employee2 =new Employee();
                employee2.setId(3);
                employee2.setName("c");
                employee2.setSex('女');
                SimpleDatabase.saveAndUpdate(employee2);

                List<Employee> l=SimpleDatabase.select(Employee.class,null,null,null,null,null,null);
                for (int i = 0; i <l.size() ; i++) {
                    Employee e=l.get(i);
                    Log.d("===============",e.getName());
                    Log.d("===============",e.getSex()+"");
                    Log.d("===============",e.getId()+"");
                }
    }
    }
複製代碼

實現原理

首先讀取配置信息,獲取數據庫和表的信息

/** * 解析xml文件 */
public class XMLParser {

   private final static String RESOURCES="my_database.xml";//配置數據庫信息的xml名字

   private final static String TABLE="table";//xml屬性常量

    final static String VERSION="version";//xml屬性常量

    final static String DATABASE="database";//xml屬性常量

    private final static String NAME="name";//xml屬性常量

    private Context context;

    private Map<String,String> map=null;//用來存儲數據庫信息

    private List<String> tables=null;//用來存儲表信息

   public XMLParser(){
        init();
    }

   private void init(){
        context=MyApplication.getContext();
        map=new HashMap<>(2);
        tables=new ArrayList<>();

   }

  //解析數據
  public void parse() throws IOException, XmlPullParserException {

        XmlPullParserFactory factory=XmlPullParserFactory.newInstance();

        XmlPullParser xmlPullParser=factory.newPullParser();
       //從asset文件下讀取my_database.xml的信息
        xmlPullParser.setInput(new InputStreamReader(context.getAssets().open(RESOURCES)));
        int type=xmlPullParser.getEventType();
        while(type!=XmlPullParser.END_DOCUMENT){
                if (xmlPullParser.getEventType()==XmlResourceParser.START_TAG){//若是爲開始標籤
                    String name=xmlPullParser.getName();
                    switch (name){
                        case DATABASE://標籤爲<database>
                            parseDatabase(xmlPullParser);
                            break;
                        case TABLE://標籤爲<table>
                            parseTable(xmlPullParser);
                            break;
                    }
                }
                xmlPullParser.next();//下一個標籤
                type=xmlPullParser.getEventType();
        }

    }

    //解析數據庫信息
    private void parseDatabase(XmlPullParser xmlPullParser) {
        String databaseName=null;
        String version=null;
        if (xmlPullParser.getAttributeCount()==2){
            String value_1=xmlPullParser.getAttributeName(0);
            if (NAME.equals(value_1)){
                databaseName=xmlPullParser.getAttributeValue(0);
                version=xmlPullParser.getAttributeValue(1);
            }else {
                databaseName=xmlPullParser.getAttributeValue(1);
                version=xmlPullParser.getAttributeValue(0);
        }
        }else{
            throw new MyException("database標籤的參數錯誤");
        }
        map.put(DATABASE,databaseName);
        map.put(VERSION,version);
    }

    //解析表格信息
    private void parseTable(XmlPullParser xmlPullParser) {
        String className=null;
        if (xmlPullParser.getAttributeCount()==1){
            className=xmlPullParser.getAttributeValue(0);
        }else
            throw new MyException("table參數錯誤");
        tables.add(className);
    }

    public Map<String, String> getMap() {
        return map;
    }
    public List<String> getTables() {
        return tables;
    }
}
複製代碼

建立數據庫的類

public class MyDatabase extends SQLiteOpenHelper {

    private onDatabaseUpdateListener listener=null;

    private static final String TAG = "MyDatabase";

    public MyDatabase(Context context, String name, SQLiteDatabase.CursorFactory factory, int version,onDatabaseUpdateListener listener) {
        super(context, name, factory, version);
        this.listener=listener;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String[] createTables = listener.onCreate();
            for (String s: createTables){
                db.execSQL(s);
                Log.d("======建表語句",s);
            }
        Log.d("======","onCreate執行");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String[] deleteTable = listener.update(db);
        Log.d("======","onUpgrade執行");
           if (deleteTable !=null){
               for (String s: deleteTable){
                   db.execSQL(s);
                   Log.d("=====刪表語句",s);
               }
           }
           onCreate(db);
           listener.onCreateLater(db);
    }
    
    interface onDatabaseUpdateListener{
        String[] update(SQLiteDatabase db);//數據庫版本更新時調用
        String[] onCreate();//建立新的表時調用
        void onCreateLater(SQLiteDatabase db);//建立完表時調用
    }
}
複製代碼

完成數據庫操做的實現類

/** * 實現數據的操做和數據庫的建立 */
public class SimpleDatabase implements MyDatabase.onDatabaseUpdateListener{

    private final static String NAME="SimpleDatabase.xml";

    private final static String OLD="old";

    private final static String TABLE="table_";

    private final static String NUMBER="number";

    //MyDatabaseHelper是一個輔助類,用來生成建立數據庫和表所須要的數據
    private static MyDatabaseHelper databaseHelper=null;

   private static SQLiteDatabase db=null;

   private Map<String,Cursor> savedData=null;

    String simpleNames[]=null;

   private static final String TAG = "SimpleDatabase";

   private static SimpleDatabase simpleDatabase=new SimpleDatabase();

   public SimpleDatabase(){
       init();
   }

   private void init(){
       databaseHelper=new MyDatabaseHelper();
   }

    /** * 查詢指定的數據 */
    public static<T> List<T> select(Class<T> clazz,String columnNames[],String where, String args[],String groupBy, String having, String orderBy){
        List<T> list = new ArrayList<>();
        Cursor cursor= db.query(clazz.getSimpleName(),columnNames,where,args,groupBy,having,orderBy);
        while(cursor.moveToNext()){
            try {
                T t = clazz.newInstance();
                Field fields[]=clazz.getDeclaredFields();
                for (Field f:fields) {
                        f.setAccessible(true);
                        String fieldName = f.getName();
                        String fieldValue = cursor.getColumnName(cursor.getColumnIndex(fieldName));
                        //因爲getColumnName()只會返回String類型,因此這裏須要getInitialTypeValue()
                        //獲取初始類型的值
                        f.set(t,getInitialTypeValue(f.getType().getSimpleName(),fieldValue));
                }
                list.add(t);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        cursor.close();
        return list;
    }

    private static Object getInitialTypeValue(String type,String value){
        switch (type){
            case "int":
            case "integer":
                return Integer.valueOf(value);
            case "boolean":
                return Boolean.valueOf(value);
            case "float":
                return Float.valueOf(value);
            case "double":
                return Double.valueOf(value);
            case "String":
            case "Character":
            case "char":
               return value;
        }
        return null;
    }

    /** * 查詢指定的數據 */
    private static Cursor select(Object obj,String columnNames[],String where,String args[]){
       String tableName=obj.getClass().getSimpleName();
       return db.query(tableName,columnNames,where,args,null,null,null);
    }

    /** * 若是不存在數據庫就建立,若是已經存在,則直接結束 */
   public void create(){
       String name=databaseHelper.getName();
       String version=databaseHelper.getVersion();
       if (databaseHelper.check(getOldVersion(),Integer.valueOf(version)))//若是須要更新
           saveDataInSharedPreferences(Integer.valueOf(version));
       Log.d("=========","name"+name);
       MyDatabase database = new MyDatabase(MyApplication.getContext(), name, null, Integer.valueOf(version), SimpleDatabase.this);
       db= database.getWritableDatabase();
   }

    public static SimpleDatabase newInstance() {
        return simpleDatabase;
    }

    /** * 存儲批量數據 * @param list * @throws IllegalAccessException */
   public static void save(List<Object> list) {
       for (Object o:list) {
           save(o);
       }
   }

    /** * 存儲單個數據到表中 * @param o * @throws IllegalAccessException */
   public static void save(Object o) {
           Class clazz=o.getClass();
           Field fields[]=clazz.getDeclaredFields();
           ContentValues values=new ContentValues();
           values.put("simple_database_id",o.toString());
           for (Field f:fields) {
               try {
                      f.setAccessible(true);
                      if (f.get(o)!=null&&!"serialVersionUID".equals(f.getName())) {
                          values.put(f.getName(), f.get(o).toString());
                          Log.d("========value", f.get(o).toString());
                      }
               } catch (IllegalAccessException e) {
                   e.printStackTrace();
                   Log.wtf(TAG,"類中因此的數據應該設置值");
               }
           }
           db.insert(clazz.getSimpleName(),null,values);
   }

   public static void saveAndUpdate(Object o){
       Class clazz=o.getClass();
       String id=o.toString();
       Field fields[]=clazz.getDeclaredFields();
       ContentValues values=new ContentValues();
       values.put("simple_database_id",o.toString());
       for (Field f:fields) {
           try {
               f.setAccessible(true);
               if (f.get(o)!=null&&!"serialVersionUID".equals(f.getName())) {
                   values.put(f.getName(), f.get(o).toString());
                   Log.d("========value", f.get(o).toString());
               }
           } catch (IllegalAccessException e) {
               e.printStackTrace();
               Log.wtf(TAG,"類中因此的數據應該設置值");
           }
       }
       Cursor cursor=select(o,null,"simple_database_id=?",new String[]{id});
       if (cursor.getCount()==0){//插入
           db.insert(clazz.getSimpleName(),null,values);
       }else {//更新
           db.update(clazz.getSimpleName(),values,"simple_database_id=?",new String[]{id});
       }
   }


    /** * 刪除表中全部的數據 * @param o */
   public static void delete(Object o){
       Class clazz=o.getClass();
       delete(clazz.getSimpleName(),"simple_database_id=?",o.toString());
   }


    /** * 刪除表中指定的數據 * @param name * @param where * @param arg */
   private static void delete(String name,String where,String... arg){
       db.delete(name,where,arg);
   }

    /** * 若是版本更新,則存儲最新的版本 * @param version 版本號 */
   private static void saveDataInSharedPreferences(int version){
       //獲取SharedPreferences的Editor對象來執行儲存操做
       SharedPreferences.Editor editor=MyApplication.getContext().getSharedPreferences(NAME,0).edit();
       editor.putInt(OLD,version);
       editor.apply();//最後必定要調用這個方法,完成數據的儲存
   }

    /** *存儲過去的表名 * @param names */
    private void saveDataInSharedPreferences(String[] names){
        //獲取SharedPreferences的Editor對象來執行儲存操做
        SharedPreferences.Editor editor=MyApplication.getContext().getSharedPreferences(NAME,0).edit();
        for (int i=0;i<names.length;i++){
            editor.putString(TABLE+i,names[i]);
        }
        editor.putInt(NUMBER,names.length);
        editor.apply();//最後必定要調用這個方法,完成數據的儲存
    }


    /** * 獲取上一次的數據庫的版本 * @return */
   private static int getOldVersion(){
       SharedPreferences get=MyApplication.getContext().getSharedPreferences(NAME,0);
       return get.getInt(OLD,0);
   }


    private String[] getOldTableName(){
        SharedPreferences get=MyApplication.getContext().getSharedPreferences(NAME,0);
        int length=get.getInt(NUMBER,-1);
        if (length==-1)
            Log.wtf(TAG,"原有表格不存在");
        String names[]=new String[length];
        for (int i=0;i<length;i++){
            names[i]=get.getString(TABLE+i,"");
        }
        return names;
    }

    @Override
    public String[] update(SQLiteDatabase db) {//在刪除表以前把表的數據保存起來
        simpleNames=getOldTableName();
        savedData=new HashMap<>(simpleNames.length);
        for (String name:simpleNames) {
            Cursor cursor=db.query(name,null,null,null,null,null,null);
            savedData.put(name,cursor);
        }
        Cursor cursor=savedData.get(simpleNames[0]);
        if (cursor.moveToFirst()){
            String sex=cursor.getString(cursor.getColumnIndex("sex"));
            Log.d("===============update","sex="+sex);
        }
        return databaseHelper.getDeleteTable();
    }

    @Override
    public String[] onCreate() {
        saveDataInSharedPreferences(databaseHelper.getSimpleTableName());
        return databaseHelper.getCreateTable();
    }

    @Override
    public void onCreateLater(SQLiteDatabase db) {
        recoverAllData(db);
    }

    /** * 恢復全部的數據 */
    private void recoverAllData(SQLiteDatabase db){
       List<String> deleteTable=checkWhichTableDisappear(databaseHelper.getTables());
       List<String> nowTable=Arrays.asList(simpleNames);
       nowTable.remove(deleteTable);
       for (int i=0;i<nowTable.size();i++){
          Cursor cursor=savedData.get(nowTable.get(i));
          ContentValues values=new ContentValues();
           if (cursor.moveToFirst()){
               do{
                   String columnNames[]=cursor.getColumnNames();
                   for (int j=0;j<columnNames.length;j++)
                         values.put(columnNames[j],cursor.getString(cursor.getColumnIndex(columnNames[j])));
               }while (cursor.moveToNext());
               db.insert(nowTable.get(i),null,values);
           }
       }
        for (String n:simpleNames) {//釋放全部的資源
            savedData.get(n).close();
        }
    }

    /** * 檢查有哪些表被刪除 * @param newTable * @return */
    private List<String> checkWhichTableDisappear(List<String> newTable){
            String deleteTable[]=new String[simpleNames.length];
            for (int i=0,j=0;i<simpleNames.length;i++){
                if (!newTable.contains(simpleNames[i])){
                    deleteTable[j]=simpleNames[i];
                    j++;
                }
            }
          return Arrays.asList(deleteTable);
    }
}
複製代碼

SimpleDatabase的主要做用是在my_database.xml中的配置更改時,能自動更新數據庫;插入和更新時,經過saveAndUpdate(Object o)使用對象來實現插入和更新操做(當數據庫中不存在同一條數據時,就插入;當數據庫中存在同一條數據時,就更新);查詢時,經過

List<T> select(Class<T> clazz,String columnNames[],String where,String args[],String groupBy, String having, String orderBy)獲取包含查詢結果對象的集合;刪除時,經過delete(Object o)使用對象來實現刪除操做。

實現原理:

SimpleDatabase經過實現MyDatabaseonDatabaseUpdateListener接口,監聽MyDatabaseonCreateonUpgrade方法。在onCreate被調用時,調用onDatabaseUpdateListener.onCreate來存儲以前的表名(若是修改了配置文件的話),並返回建立表的sql語句集合(可能建立多個表),以後在MyDatabase.onCreate中建立表。當onUpgrade被調用時,調用onDatabaseUpdateListener.update來存儲當前數據庫中的數據,並返回刪除表的sql語句集合,刪除成功後建立新的表,以後調用onDatabaseUpdateListener.onCreateLater方法將以前存儲的數據從新存儲到數據庫中。

SimpleDatabase中的selectdeletesaveAndUpdate方法是經過反射實現的,具體能夠看註釋。

其餘類的實現很簡單,具體能夠看源碼:

  • MyApplication類
/** * 獲取系統的context */
public class MyApplication extends Application {
    private static Context context;
    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }
    public static Context getContext(){
        return context;
    }
}
複製代碼
  • MyDatabaseHelper類
/** * 生成建立數據庫和表所須要的數據 */
public class MyDatabaseHelper {

    private String name=null;

    private String version=null;

    private List<String> tables=null;//存儲完整類名

    private Map<String,Table[]> maps=null;

    private String createTable[]=null;//存儲建表語句

    private String deleteTable[]=null;//存儲刪除表的語句

    private boolean ok=false;

    private static final String TAG = "MyDatabaseHelper";

    public MyDatabaseHelper(){
       init();
    }

    /** * 初始化數據 */
    private void init(){
        XMLParser xmlParser=null;
        xmlParser=new XMLParser();
        try {
            xmlParser.parse();
        } catch (IOException | XmlPullParserException  e) {
            e.printStackTrace();
        }
        name=xmlParser.getMap().get(XMLParser.DATABASE);
        version=xmlParser.getMap().get(XMLParser.VERSION);
        tables=xmlParser.getTables();
        maps=new HashMap<>(tables.size());
    }

    /** * 檢查是否須要更新 * @param old 以前的版本 * @param now 如今的版本 */
    public boolean check(int old,int now){
        if (now>old) {
            try {
                parseTable();
                ok=true;
                return true;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /** * 解析類的數據,在version改變時調用 * @throws ClassNotFoundException */
    private void parseTable() throws ClassNotFoundException {
        for (String name:tables){
            Class table=Class.forName(name);
            Field[] field=table.getDeclaredFields();
            Table info[]=new Table[field.length];
            for (int i=0;i<field.length;i++){
                Table t=new Table();
                t.setProperty(field[i].getName());
                t.setType(field[i].getType().getSimpleName());
                info[i]=t;
            }
            maps.put(name,info);
        }
    }
    /** * 生成建表語句 */
    private void generateTable(){
        for (int i=0;i<tables.size();i++){
            Table table[]=maps.get(tables.get(i));
            StringBuilder stringBuilder=new StringBuilder();
            String simpleName=getSimpleName(tables.get(i));
            stringBuilder.append("create table "+simpleName+"( ");
            for (int j=0;j<table.length;j++){
                Table t=table[j];
                if (t!=null)
                if (!Table.OTHER.equals(t.getType()))
                    stringBuilder.append(" , "+t.getProperty()+" "+t.getType());
            }
            String string=stringBuilder.append(")").toString();
            string=string.replaceFirst(",","");
            createTable[i]=string;
        }
    }

    /** * 生成刪除表的語句 */
    private void deleteTable() {
        for (int i = 0; i < tables.size(); i++) {
            deleteTable[i]="drop table if exists "+getSimpleName(tables.get(i));
        }
    }

    /** * 獲取簡單類名,不包括包 * @param name 帶有包名的類名 * @return 不包含包名的類名 */
    private String getSimpleName(String name){
       int position= name.lastIndexOf('.');
       return name.substring(position+1);
    }



    public String getName() {
        return name;
    }

    public String getVersion() {
        return version;
    }

    public String[] getCreateTable() {
        if (!ok)
            Log.e(TAG,"必須先調用check()");
        createTable=new String[tables.size()];
        generateTable();
        return createTable;
    }

    public String[] getDeleteTable() {
        if (!ok)
            Log.e(TAG,"必須先調用check()");
        deleteTable=new String[tables.size()];
        deleteTable();
        return deleteTable;
    }

    public List<String> getTables() {
        return tables;
    }

    public String[] getSimpleTableName() {

        String simpleTableName[]=new String[tables.size()];

        for (int i=0;i<tables.size();i++) {

            String simpleName = getSimpleName(tables.get(i));

            simpleTableName[i] = simpleName;

        }

        return simpleTableName;

    }
}
複製代碼
  • MyException
public class MyException extends RuntimeException {
    public MyException(String message) {
        super(message);
    }
}
複製代碼
  • Table類
/** * 存儲每一個字段對應的屬性和名字 */
public class Table {
    final static String INTEGER="integer";

    final static String TEXT="text";

    final static String REAL="real";

    final static String BLOB="blob";

    final static String INT="int";

    final static String CHAR="char";

    final static String FLOAT="float";

    final static String DOUBLE="double";

    final static String STRING="String";

    final static String BOOLEAN="boolean";

    final static String OTHER="other";

    private String property;//對應的屬性

    private String type;//對應的屬性的類型

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public String getType() {
        return type;
    }
    
    public void setType(String type) {
        checkProperty(type);
    }

    private void checkProperty(String property){
        switch (property){
            case INT:
            case BOOLEAN:
                type=INTEGER;
                break;
            case FLOAT:
            case DOUBLE:
                type=REAL;
                break;
            case STRING:
            case CHAR:
                type=TEXT;
                break;
            default:
                 type=OTHER;
                 break;
        }
    }
}
複製代碼

參考菜鳥教程

相關文章
相關標籤/搜索