[Flutter] sqflite SQLite Database #DB

2024. 1. 16. 17:29dev/flutter

728x90
반응형

sqflite package is possible as simple query in local device storage.

path : https://pub.dev/packages/sqflite

 

sqflite | Flutter Package

Flutter plugin for SQLite, a self-contained, high-reliability, embedded, SQL database engine.

pub.dev

 

add package

flutter pub add sqflite

 

 

import

import 'package:sqflite/sqflite.dart';

 

 

create database(db)

Database db = await openDatabase("my_db.db");

@override
void dispose() {
  db?.close();
  super.dispose();
}

 

 

know database path

var databasePath = await getDatabasesPath();
debugPrint('databasePath:$databasePath');

 

 

.execute : method is not return value, because type is void.

.rawQuery : method is return value, List<Map<String, Object?>>

 

so what is .transaction function?

일반적으로 rawQuery만 사용하면 해당 쿼리를 사용하지만,

transaction함수 내에서 여러 쿼리를 한 번에 처리할 때 사용된다.

여러 쿼리 중 하나라도 실패를 하면 이전에 성공했던 쿼리도 함께 실패처리되어 안전하다.

 

create table

StringBuffer stringBuffer = StringBuffer();
stringBuffer
..write("CREATE TABLE IF NOT EXISTS Friends(")
..write("id INTEGER PRIMARY KEY,")
..write("name TEXT,")
..write("value INTEGER")
..write(");");

await database?.execute(stringBuffer.toString());
StringBuffer stringBuffer = StringBuffer();
stringBuffer
..write("CREATE TABLE IF NOT EXISTS Family(")
..write("id INTEGER PRIMARY KEY AUTOINCREMENT,")
..write("name TEXT NOT NULL,")
..write("value INT")
..write(");");

final result = await database?.rawQuery(stringBuffer.toString());
debugPrint("result:$result");

 

2024-03-13-수

: 컬럼을 추가해서 그대로 execute(query)를 하면 적용되지 않는다.

 

 

버전확인

final String databasePath = await getDatabasesPath();
final String path = [databasePath, _databaseName].join('/');
final db  = await databaseFactory.openDatabase(path);
final version = await db.getVersion();

 

 

테이블 삭제

DROP TABLE [TABLENAME]

 

 

컬럼(Column)추가

: DB를 한 번 열게되면 이후로 openDatabase를 해주었을시 동작을 하지 않게된다.

: 열고 닫은 후 다시 열 때 비로소 onUpgrade, version등을 확인할 수 있다.

E/SQLiteLog(27678): (1) near ",": syntax error in "ALTER TABLE wifi ADD COLUMN register_date TEXT NOT NULL, update_date TEXT NOT NULL;"

위의 문제가 계속 발생되었고, 왜 문제가 발생하는지 보아하니 DB가 열리지 않았던 것이다.

- onUpgrade내부에서 받는 db객체는 open된 상태가 아닌 것.

결국 쿼리 String을 매개로 받아서 나중에 execute하는 방법을 택하였다.

ALTER TABLE table1, table2, table3
ADD COLUMN new_column_name datatype NOT NULL DEFAULT 'default_value';

 

near ",": syntax error in "ALTER TABLE wifi, idpw, toilet ADD register_date DATETIME NOT NULL, update_date DATETIME NOT NULL;"

 

NOT NULL인 경우 default값을 꼭 넣어주어야 한다. NOT NULL을 빼자

 

컬럼 삭제

컬럼은 따로 삭제되지 않고, bakup테이블을 만들어서 복사친 뒤.. 그 테이블을 RENAME하는 방법이 있는데 지금 그것을 알기 매우 싫다.

ALTER TABLE table_name
DROP COLUMN column_name;

 

 

- duplicate에러가 발생해도 쿼리가 잘반영되었을까?

(1) duplicate column name: register_date in "ALTER TABLE wifi ADD register_date DATETIME;"

: 결과 → 안된다

에러코드를 적용해주어야 다음 쿼리를 진행한다. 

  await database?.transaction((txn) async {
    for (var query in queryList) {
      try {
        var result = await txn.rawQuery(query);
        debugPrint("[DatabaseHelper].. result:$result, query:$query");
      } catch (e) {
        debugPrint("[DatabaseHelper].. [ERROR]:$e, query:$query");
      }
    }
  });

 

그리고 여러개의 컬럼을 한 번에 추가할 수 없으며,

각각의 추가하고 싶은 테이블 컬럼을 한 번만 명시한다.

 

 

select

result : List<Map<String, Object?>>?

void selectDatabase() async {
  database ??= await openDatabase("my_db.db");

  final result = await database?.rawQuery("SELECT * FROM Friends");
  debugPrint("result:$result");
}
I/flutter (29559): result:[{id: 1, name: PH, value: 31}, {id: 2, name: Hyun, value: 30}, {id: 3, name: gyu tae, value: 31}, {id: 4, name: SH, value: 30}, {id: 5, name: chul zz, value: 31}, {id: 6, name: joo hyun, value: 30}]
Future<List<Map<String, Object?>>?> select() async {

 

 

어떠한 데이터에 삽입해주고 싶은 경우

  factory WifiModel.fromQuery(Map query) {
    return WifiModel(
      place: query['place'],
      name: query['name'],
      password: query['password'],
      memo: query['memo'],
    );
  }

 

 

insert

void insertDatabase() async {
  database ??= await openDatabase("my_db.db");

  final result = await database?.transaction((txn) async {
    int id1 = await txn.rawInsert(
      'INSERT INTO Friends(name, value) VALUES("PH", 31)'
    );
    debugPrint("id1:$id1");

    int id2 = await txn.rawInsert(
      'INSERT INTO Friends(name, value) VALUES(?, ?)', ['Hyun', 30]
    );
    debugPrint('id2:$id2');
  });
  debugPrint('result:$result');
}
W/SQLiteLog(29559): (28) double-quoted string literal: "PH"
I/flutter (29559): id1:1
I/flutter (29559): id2:2
I/flutter (29559): result:null

 

int id1 = await txn.rawInsert(
  "INSERT INTO Friends(name, value) VALUES('chul zz', 31)"
);
debugPrint("id1:$id1");

int id2 = await txn.rawInsert(
  'INSERT INTO Friends(name, value) VALUES(?, ?)', ['joo hyun', 30]
);
I/flutter (29559): id1:5
I/flutter (29559): id2:6
I/flutter (29559): result:null

 

update

rawQuery is not return something values.

but rawUpdate is return update count.

void updateDatabase() async {
  database ??= await openDatabase ("my_db.db");

  String updateQuery = "UPDATE Friends SET name='Ze Pil', value=32 WHERE name='Hyun';";
  dynamic result = await database?.rawQuery(updateQuery);
  debugPrint('result[1]:$result');

  result = await database?.rawUpdate("UPDATE Friends SET name=?, value=? WHERE name=?",
    ['se won', 31, 'joo hyun']
  );
  debugPrint('result[2]:$result');
}
I/flutter (29559): result[1]:[]
I/flutter (29559): result[2]:1

한 가지 사실을 알게 되었는데, rawUpdate했을 때 Where조건절이 동일한 것들 모두가 한 번에 바뀐다

 

 

delete

void deleteDatabase() async {
  database ??= await openDatabase("my_db.db");

  dynamic result = await database?.rawQuery("DELETE FROM Friends WHERE name='Ze Pil'");
  debugPrint('result[1]:$result');

  result = await database?.rawDelete("DELETE FROM Friends WHERE name=?", ['gyu tae']);
  debugPrint('result[2]:$result');
}
I/flutter (29559): result[1]:[]
I/flutter (29559): result[2]:1
before after
    StringBuffer query = StringBuffer();
    query
      ..write("DELETE FROM ${tableName.wifi} ")
      ..write("WHERE place=? ")
      ..write("AND name=? ")
      ..write("AND password=? ")
      ..write("AND memo=? ");

    return query.toString();

 

테이블 내부 전체 데이터 삭제처리

따로 쿼리를 주지 않고 테이블 명만 rawDelete(테이블명)만 주면,

알아서 DELETE FROM절을 넣어서 처리한다.

위 쿼리처럼 뒤에 인자가 있는 경우에만 직접 DELETE FROM절을 넣어준다.

int? result = await database?.delete(table);

 

 

 

백업 (Backup)

: 백업을 local DB에 저장하려 했었지만, MalformedURLException이 발생되어 하지 못하였고, 

대신 sqflite에서 제공하는 json형태로 백업하는 로직 有

Android: Android에서는 android.database.sqlite.SQLiteDatabase 클래스를 사용하여 데이터베이스를 백업하고 복원할 수 있습니다.
iOS: iOS에서는 FMDatabase 클래스를 사용하여 데이터베이스를 백업하고 복원할 수 있습니다.

 

AOS는 MANAGER_EXTRENAL_STORAGE를 사용하지 않으면 안된다

 

backup 과정

// back db객체 생성
Database? backupDatabase = await openDatabase(backupDatabasePath);

var backupData = await backupDatabase.query([테이블명])
await backupDatabase.close();

backupData를 가져올때 query에 테이블명만 넣으면 된다. 정말 간편하다.

그리고 backupDatabase는 닫아준다.

 

기존 DB에 적용해보자

// database : 기존 DB
await database?.transaction((txn) async {
  backupData.forEach (element ->
    await txn.insert([테이블명], element)
})

이렇게 그냥 추가하면 문제가 된다.

기존에 있는 것은 걸러주고, 없는 것은 채워주는 것이 좋을 것 같다(OUTER JOIN채택)

 

 

2024-05-28-화 : 현재 테이블의 제일 높은 id의 값을 추출하기

더보기
String lastIdQuery(String tableName) => "SELECT MAX(id) FROM $tableName;";

Future<int> lastId({required String table}) async {
database ??= await openDatabase(databaseName);
String query = lastIdQuery(table);
Log.d("query:$query");
final result = await database?.rawQuery(lastIdQuery(table));
Log.d("lastId:$result");
return -1;
}

결과 : lastId:[{MAX(id): 2}]

728x90
반응형

'dev > flutter' 카테고리의 다른 글

[Flutter] SnackBar  (0) 2024.01.18
[Flutter] Splash  (0) 2024.01.17
[Flutter] MainAxisAlignment (Row, Column)  (0) 2024.01.16
[Flutter] PIP, Picture in Picture Mode  (0) 2024.01.15
[Flutter] Timer, custom delay point  (0) 2024.01.15