wtorek, 7 sierpnia 2018

[1] Kotlin - SQLCipher

W tym poście chciałbym przedstawić sposób dostępu do bazy danych SQLCipher.

[Źródło: www.kotlinlang.org]

Program:


Na samym początku należy dołożyć implementację biblioteki SQLCipher w pliku build.grade.

  1. implementation "net.zetetic:android-database-sqlcipher:3.5.9@aar"

Dalej tworzę przykładową klasę zawierającą dane które będą dokładane do bazy:

  1. class User{
  2.     var id : Int = 0
  3.     var name : String = ""
  4.     var surname: String = ""
  5.     var age : Int = 0
  6.  
  7.     constructor(name:String, surname: String, age:Int){
  8.         this.name = name
  9.         this.surname = surname
  10.         this.age = age
  11.     }
  12.  
  13.     constructor(){
  14.     }
  15. }

Przed zaimplementowaniem klasy należy dołożyć biblioteki dla SQLite, które muszą pochodzić z pakietów SQLCipher:

  1. import net.sqlcipher.database.SQLiteDatabase
  2. import net.sqlcipher.database.SQLiteOpenHelper
  3.  
  4. /*
  5.     Tego nie importujemy, tylko zastępujemy biblioteki na
  6.     te zdefiniowane powyżej
  7.  
  8.     import android.database.sqlite.SQLiteDatabase
  9.     import android.database.sqlite.SQLiteOpenHelper
  10. */

Teraz klasa odpowiedzialna za komunikację z bazą danych. Musi ona dziedziczyć po SQLiteOpenHandler, ponieważ w niej zdefiniowane są operacje na bazie:

  1. class DataBaseHandler : SQLiteOpenHelper{

Kolejnym krokiem jest zdefiniowanie potrzebnych danych w obiekcie companion. Dzięki temu będzie można je wywołać poza klasą:

  1.     companion object {
  2.         val Tag = "DatabaseHandler"
  3.         val DATABASE_NAME = "test7DB.db"
  4.   val TABLE_NAME = "users_table"
  5.         val DBVersion = 1
  6.  
  7.         val COL_ID = "id"
  8.         val COL_NAME = "name"
  9.         val COL_SURNAME = "surname"
  10.         val COL_AGE = "age"
  11.     }

W nim znajdują się nazwy poszczególnych kolumn czy nazwa bazy danych wraz z wersją.

Teraz tworzę kilka zmiennych przechowujących hasło, kontekst, obiekt bazy danych oraz ścieżkę do pliku.

  1.     val DATA_BASE_PASSWORD: String = "testPas2"
  2.     var context: Context? = null
  3.     var sqlObj: SQLiteDatabase
  4.     var databaseFile: File

Nadszedł czas na konstruktor:

  1.     constructor(context: Context) : super(context, DATABASE_NAME, null, DBVersion) {
  2.         this.context = context
  3.  
  4.         SQLiteDatabase.loadLibs(context)
  5.  
  6.         databaseFile = context.getDatabasePath(DATABASE_NAME)
  7.  
  8.         sqlObj = SQLiteDatabase.openOrCreateDatabase(databaseFile, DATABASE_PASSWORD, null)
  9.     }

W przypadku gdy chcemy usuwać starą bazę danych przed stworzeniem nowej to należy dodać dwie linijki przed stworzeniem:

  1. databaseFile.mkdirs()
  2. databaseFile.delete()

Aby stworzyć tabelę należy nadpisać funkcję onCreate oraz onUpgrade:

  1.     override fun onCreate(db: SQLiteDatabase?) {
  2.         val createTable = "CREATE TABLE if not exists " + TABLE_NAME + " (" +
  3.                 COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
  4.                 COL_NAME + " VARCHAR(20)," + COL_SURNAME + " VARCHAR(20)," + COL_AGE + " INTEGER);"
  5.         db?.execSQL(createTable)
  6.     }
  7.  
  8.     override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
  9.         db!!.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME)
  10.         onCreate(db)
  11.     }

Wprowadzenie danych do bazy:

  1.     fun insertData(user : User){
  2.         val db = this.getWritableDatabase(DATA_BASE_PASSWORD)
  3.         var cv = ContentValues()
  4.  
  5.         cv.put(COL_NAME,user.name)
  6.         cv.put(COL_SURNAME, user.surname)
  7.         cv.put(COL_AGE,user.age)
  8.         var result = db.insert(TABLE_NAME, null,cv)
  9.  
  10.         if(result == -1.toLong()){
  11.             Toast.makeText(context, "Failed",Toast.LENGTH_SHORT).show()
  12.         }
  13.         else{
  14.             Toast.makeText(context, "Success",Toast.LENGTH_SHORT).show()
  15.         }
  16.  
  17.         db.close()
  18.     }

Odczytanie wszystkich danych z bazy:

  1.     fun readData() : MutableList<User>{
  2.         var list : MutableList<User> = ArrayList()
  3.  
  4.         val db = this.getReadableDatabase(DATA_BASE_PASSWORD)
  5.         val query = "Select * from " + TABLE_NAME
  6.         val result = db.rawQuery(query, null)
  7.  
  8.         if(result.moveToFirst()){
  9.             do{
  10.                 var user = User()
  11.                 user.id = result.getString(result.getColumnIndex(COL_ID)).toInt()
  12.                 user.name = result.getString(result.getColumnIndex(COL_NAME))
  13.                 user.surname = result.getString(result.getColumnIndex(COL_SURNAME))
  14.                 user.age = result.getString(result.getColumnIndex(COL_AGE)).toInt()
  15.                 list.add(user)
  16.             }while(result.moveToNext())
  17.         }
  18.  
  19.         result.close()
  20.         db.close()
  21.  
  22.         return list
  23.     }

Usunięcie wszystkich danych z bazy:

  1.     fun deleteAllData(){
  2.         val db = this.getWritableDatabase(DATA_BASE_PASSWORD)
  3.         db.delete(TABLE_NAME, nullnull)
  4.         db.close()
  5.     }

Aktualizacja istniejących danych:

  1.     fun updateData(oldUser: User, newUser: User){
  2.         val db = this.getWritableDatabase(DATA_BASE_PASSWORD)
  3.         var cv = ContentValues()
  4.  
  5.         cv.put(COL_NAME,newUser.name)
  6.         cv.put(COL_SURNAME, newUser.surname)
  7.         cv.put(COL_AGE,newUser.age)
  8.  
  9.         db.update(TABLE_NAME, cv, COL_ID + "=" + oldUser.idnull)
  10.     }

Aby utworzyć nowy obiekt klasy DataBaseHandler wystarczy wywołać następującą linię, po czym wywoływać potrzebne funkcje do zarządzania danymi:

  1. val context = this
  2. var db = DataBaseHandler(context)