Flutter(二十九)——封裝SQLHelpers

居天下之廣居,立天下之正位,行天下之大道;得志,與民由之;不得志,獨行其道。富貴不能淫,貧賤不能移,威武不能屈,此之謂大丈夫。

前言

在實際的項目中,如果碰到一個數據庫操作,就使用一條SQL語句對數據庫操作,那麼不僅僅代碼非常雜亂,而且弄不好可能造成內存溢出,所以我們常常需要封裝SQL操作,封裝成SQLHelpers來進行操作。
最終實現效果
有過後端開發經驗的程序員來說,都或多或少使用過SQLHelpers的幫助類,下面,博主通過一個例子詳解SQLHelpers如何封裝。

封裝SQLHelpers

假如現在博主這裏有一個需求,需要做一個新聞類的App,新聞有標題和詳細信息,那麼如何創建一個SQLHelpers來操作新聞數據呢?不妨直接上代碼把:

import 'package:sqflite/sqflite.dart';

final String tableNew='news';
final String columnId='_Id';
final String columnTitle='title';
final String columnDet='detailed';

class News{
  int id;
  String title;
  String detailed;

  /***
   * 將數據轉換爲鍵值對
   */
  Map<String,dynamic> toMap() {
    var map = <String, dynamic>{
      columnTitle: title,
      columnDet: detailed,
    };
    if (id != null) {
      map[columnId] = id;
    }
    return map;
  }
    
    
    News();

  /***
   * 將數據轉換爲類
   */
  News.fromMap(Map<String,dynamic> map){
      id=map[columnId];
      title=map[columnTitle];
      detailed=map[detailed];
    }
}

class NewsProvider{
  Database db;

  /***
   * 打開數據庫並創建表格
   */
  Future open(String path) async{
    db=await openDatabase(path,version: 1,onCreate: (Database db,int version) async{
      await db.execute('''
        create table $tableNew(
        $columnId integer primary key autoincrement,
        $columnTitle text not null,
        $columnDet text not null)
      ''');
    });
  }

  /***
   * 插入數據
   */
  Future<News> insert(News news) async{
    news.id=await db.insert(tableNew, news.toMap());
    return news;
  }

  /***
   * 根據ID獲取數據
   */
  Future<News> getNews(int id)async{
    List<Map> maps=await db.query(
        tableNew,
        columns: [columnId,columnTitle,columnDet],
        where: '$columnId=?',
        whereArgs: [id],
    );
    if(maps.length>0){
      return News.fromMap(maps.first);
    }
    return null;
  }

  /***
   * 根據ID刪除數據
   */
  Future<int> delete(int id)async{
    return await db.delete(tableNew,where: '$columnId=?',whereArgs: [id]);
  }

  /***
   * 根據ID更新數據
   */
  Future<int> update(News news)async{
    return await db.update(tableNew, news.toMap(),where: '$columnId=?',whereArgs: [news.id]);
  }
  
  Future close() async=>db.close();//關閉數據庫
}

這裏我們將新聞的數據庫封裝成了SQLHelpers:NewsProvider,這樣我們就可以針對數據進行反覆的操作,不需要額外的其他的代碼,這樣程序執行起來就比較高效了。

實戰上班打卡

知道怎麼封裝SQLHelpers類後,我們接着通過實戰員工打卡系統,熟練掌握操作。大致的需求是用戶在登錄應用時有一個輸入框,可以輸入員工的姓名,然後點擊保存,保存完之後可以在列表記錄並顯示所有已打卡的用戶,打卡完成之後可以進入打卡瀏覽界面瀏覽所有打過卡的員工,也可以在保存界面清楚打卡數據。

這一次,我們直接封裝成一個DBProvider類,通過直接調用這個類就可以操作數據庫,首先,我們需要定義一個用戶類,代碼如下所示:

final String tableUser='user';
final String columnId='_id';
final String columnName='name';

class User{
  int id;
  String name;

  /***
   * 將類轉換爲鍵值對
   */
  Map<String,dynamic> toMap(){
    var map=<String,dynamic>{
      columnName:name,
    };
    if(id!=null){
      map[columnId]=id;
    }
    return map;
  }
  
  User();

  /***
   * 將鍵值對轉換爲類
   */
  User.fromMap(Map<String,dynamic> map){
    id=map[columnId];
    name=map[columnName];
  }
}

定義了一個Model類後,接着就是我們的SQLHelpers的實現,代碼如下:

class DBProvider{
  DBProvider._();
  static final DBProvider db=DBProvider._();
  Database _database;
  
  Future<Database> get database async{
    if(_database!=null){
      return _database;
    }
    _database=await initDB();
    return _database;
  }
  
  initDB() async{
    Directory documentsDirectory=await getApplicationDocumentsDirectory();
    String path=join(documentsDirectory.path,'UserDB.db');
    return await openDatabase(path,version: 1,onOpen: (db){},onCreate: (Database db,int version)async{
      await db.execute('''
      create table $tableUser(
      $columnId integer primary key autoincrement,
      $columnName text not null)
      ''');
    });
  }
  
  Future<User> insert(User user) async{
    final db=await database;
    user.id=await db.insert(tableUser, user.toMap());
    return user;
  }
  
  Future<User> getUser(int id) async{
    final db=await database;
    List<Map> maps=await db.query(tableNew,columns: [columnId,columnName],
    where: '$columnId=?',
    whereArgs: [id]);
    if(maps.length>0){
      return User.fromMap(maps.first);
    }
    return null;
  }
  
  Future<List<User>> getAllUser() async{
    final db=await database;
    var res=await db.query("User");
    List<User> list=res.isNotEmpty?res.map((c)=>User.fromMap(c)).toList():[];
    return list;
  }
  
  Future<int> delete(int id) async{
    final db=await database;
    return await db.delete(tableUser,where: '$columnId=?',whereArgs: [id]);
  }
  
  Future<int> update(User user)async{
    final db=await database;
    return await db.update(tableUser, user.toMap(),where: '$columnId=?',whereArgs: [user.id]);
  }
  
  removeAll() async{
    final db=await database;
    db.delete(tableUser);
  }

  Future close() async{
    final db=await database;
    db.close();
  }
}

這段代碼與前面基本類似,唯一不同的是這裏我們使用了單例模式,接着,我們要定義界面,首先是打卡界面,代碼如下:

class _MyHomePageState extends State<MyHomePage> {

  final TextEditingController textEditingController=new TextEditingController();

  _saveData(){
    User user=User();
    user.name=textEditingController.text;
    DBProvider.db.insert(user);
    Navigator.of(context).push(
      new MaterialPageRoute(builder: (context){
        return new PageResult();
      })
    );
  }

  _removeData(){
    DBProvider.db.removeAll();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            TextField(
              controller: textEditingController,
              autofocus: true,
              decoration: new InputDecoration(hintText: '請輸入打卡姓名'),
            ),
            RaisedButton(
              child: Text('保存'),
              onPressed: _saveData,
            ),
            RaisedButton(
              child: Text('清除數據'),
              onPressed: _removeData,
            ),
          ],
        ),
      ),
    );
  }
}

上面代碼界面控件有三個,一個輸入文本框,兩個按鈕,一個按鈕記錄打卡數據,所以點擊後saveData,一個清楚數據庫所有數據removeData,基本都是常用的代碼,這裏就不贅述了,接着就是打卡後,顯示的詳細打卡詳情列表的界面,代碼如下:

class PageResult extends StatelessWidget{

  PageResult({Key key}):super(key:key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('已打卡人員'),
      ),
      body: FutureBuilder<List<User>>(
        future: DBProvider.db.getAllUser(),
        builder: (BuildContext context,AsyncSnapshot<List<User>> snapshot){
          if(snapshot.hasData){
            return ListView.builder(
                itemBuilder: (BuildContext context,int index){
                  User item=snapshot.data[index];
                  return ListTile(
                    title: Text(item.name),
                  );
                },
                itemCount: snapshot.data.length,
            );
          }else{
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }

}

這段代碼就是獲取數據庫中的數據以ListView的形式顯示在界面上,如果數據庫沒有數據,則顯示圓形進度條。顯示的效果圖,如博文首圖所示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章