동도리 개발 로그

Flutter(플러터) - Drift(Moor)클라이언트 DB를 ORM으로 사용하기 (sqlite) 본문

개발/Flutter

Flutter(플러터) - Drift(Moor)클라이언트 DB를 ORM으로 사용하기 (sqlite)

동돌이 2022. 2. 17. 17:18
반응형

서버에 저정하는 것 말고 휴대폰 내부 저장공간에 사용 할 수 있는 DB는 주로 sqlliste 를 사용한다. 

이를 flutter에 ORM형식으로 사용할 수 있게 해주는 Drift(Moor)패키지에 대해서 정리하고자 한다.

-> ORM 방식 말고도 기존의 SQL 쿼리와, Dart Table을 이용한 방법도 있지만 지금은 ORM을 이용한 DB사용만 정리하도록한다.  

기존에 사용하던 패키지는 Moor인데 Moor라는 단어가 남에게 경멸하는 용어로 사용되기도 하고 Dart 와 Flutter에 사용되는 패키지라고 반영이 되는거 같지않다고 해서 rename을 결정하게되었다고 한다. 

 

1. 기존에 사용한 moor사용법을 정리하고

2. 이후에 moor 에서 drift로 마이그레이션 하는 방법을 정리 하도록 하겠다. 

 

유튜브 강의 코드팩토리 를 참조하여 진행하였다.

 


1. 패키지 설치

괄호안 drift로 변경한 뒤 패키지

drift로의 마이그레이션을 정리하기 전까지는 moor를 이용한 프로젝트 진행을 기술할 예정.

dependencies:
  moor: ^4.6.1 (drift: ^1.4.0)
  sqlite3_flutter_libs: ^0.5.0
  path_provider: ^2.0.0
  path: ^1.8.0
  
dev_dependencies:
  build_runner: ^2.1.7
  moor_generator: ^4.6.0 (drift_dev: ^1.4.0)

2. generator

sqlite 를 사용하기 위한 코드를 generate 해주는 기능을 사용하기위해서 해야하는 작업이 있다. 

database.dart 파일을 생성하고 아래와 같이 입력한다. (part 'datatbase.g.dart' 는 generator가 생성해주는데 '파일명.g.dart' 로 입력해놓으면 알아서 생성이된다. 

 

database.dart

import 'dart:io';
import 'package:moor/ffi.dart';
import 'package:moor/moor.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

part 'database.g.dart';

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'db.sqlite'));
    return VmDatabase(file, logStatements: true);
  });
}

@UseMoor()
class Database extends _$Database {
  Database(QueryExecutor e) : super(e);

  static Database init() {
    return Database(_openConnection());
  }

  @override
  int get schemaVersion => 1;
}

위와같이 파일을 생성한 뒤

flutter pub run build_runner build

를 사용하게되면 datatbase.dart 파일이 있는 폴더와 같은 위치에 database.g.dart파일이 생성이 된다. 

 

테이블 파일 생성을 하도록해보자 

db를 생성하는 파일과 같은 폴더에서 테이블을 관리하는 것보단 테이블 폴더를 관리하는게 깔끔하니 폴더를 생성하여 작성한다. 

model/accounts.dart

import 'package:{project}/database/database.dart';
import 'package:moor/moor.dart';

part 'accounts.g.dart';

class Accounts extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get address => text()();
  TextColumn get name => text().withDefault(Constant("Account")).withLength(min: 3, max: 10)();

  @override
  List<String> get customConstraints => [
    'UNIQUE (address)'
  ];
}

@UseDao(tables: [Accounts])
class AccountsDao extends DatabaseAccessor<Database> with _$AccountsDaoMixin {
  AccountsDao(Database db) : super(db);
}

account 테이블을 사용하기 위한 파일을 생성하고, database에서도 사용하기위해 table 이름과 dao를 추가해주어야한다. 

 

database.dart

import 'dart:io';
import 'package:moor/ffi.dart';
import 'package:moor/moor.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:{project}/model/accounts.dart';

part 'database.g.dart';

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'db.sqlite'));
    return VmDatabase(file, logStatements: true);
  });
}

@UseMoor(tables: [Accounts], daos: [AccountsDao])
class Database extends _$Database {
  Database(QueryExecutor e) : super(e);

  static Database init() {
    return Database(_openConnection());
  }

  @override
  int get schemaVersion => 1;
}

위와같이 생성하고 다시 generator를 실행시키는 명령어를 입력하고 나면 accounts.g.dart 파일이 생성될 것이다. 

 

accounts.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'accounts.dart';

// **************************************************************************
// DaoGenerator
// **************************************************************************

mixin _$AccountsDaoMixin on DatabaseAccessor<Database> {
  $AccountsTable get accounts => attachedDatabase.accounts;
}

위에 친절하게 손으로 변경하지말라고 써놨다.

 

3. CRUD

accounts.dart

@UseDao(tables: [Accounts])
class AccountsDao extends DatabaseAccessor<Database> with _$AccountsDaoMixin {
  AccountsDao(Database db) : super(db);

  Future<Account> findById(int id) async {
    return (select(accounts)..where((t) => t.id.equals(id))).getSingle();
  }

  Future<Account> findByAddress(String address) async {
    return (select(accounts)..where((t) => t.address.equals(address))).getSingle();
  }

  Future<int> createAccount(AccountsCompanion data) async {
    return into(accounts).insert(data);
  }

  Future<int> updateName(int id, String name) async {
    return (update(accounts)..where((t) => t.id.equals(id)))
        .write(AccountsCompanion(name: Value(name)));
  }

  Future<int> deleteAll() async {
    return delete(accounts).go();
  }
}

위와같이 변경 후 사용하고자 하는 곳에서 import하여 해당 함수들을 사용하면된다. 

 

 

 

반응형