Hướng dẫn sử dụng SQLite trong ios

Sqlite là gì? Công cụ hỗ trợ tạo ra một file Sqlite ? Truy vấn sqlite trong lập trình IOS… Tất cả những câu hỏi trên mình sẽ trả lời cho các bạn trong hướng dẫn này.

SQLite là một thư viện phần mềm mà triển khai một SQL Database Engine truyền thống, không cần Server, không cần cấu hình và nhỏ gọn. SQLite là một trong những Database Engine phát triển nhanh nhất, nhưng sự phát triển của nó là về mặt tính phổ biến, không phải là về mặt kích cỡ. SQLite là mã nguồn mở.

SQLite là gì?

Nói một cách đơn giản SQLite là phần mềm quản lý cơ sở dữ liệu (DBMS) tương tự như Mysql, PostgreSQL… Đặc điểm của SQLite là gọn, nhẹ, đơn giản. Chương trình gồm 1 file duy nhất vỏn vẹn chưa đến 400kB, không cần cài đặt, không cần cấu hình hay khởi động mà có thể sử dụng ngay. Dữ liệu Database cũng được lưu ở một file duy nhất. Không có khái niệm user, password hay quyền hạn trong SQLite Database.

SQLite không thích hợp với những hệ thống lớn nhưng ở quy mô vừa tầm thì SQLite phát huy uy lực và không hề yếu kém về mặt chức năng hay tốc độ. Với các đặc điểm trên SQLite được sử dụng nhiều trong việc phát triển, thử nghiệm … và là sự lưa chọn phù hợp cho những người bắt đầu học Database.

SQLite Engine không là một Standalone Process giống như các cơ sở dữ liệu khác, bạn có thể liên kết nó một cách tĩnh hoặc một cách động tùy theo yêu cầu với ứng dụng của bạn. SQLite truy cập các file lưu giữ của nó một cách trực tiếp.

Tại sao nên sử dụng SQLite?

  • SQLite không yêu cầu một tiến trình Server riêng rẽ để hoạt động.
  • SQLite không cần cấu hình, nghĩa là không cần thiết phải cài đặt.
  • Một SQLite Database đầy đủ được lưu giữ trong một disk file đơn.
  • SQLite là rất nhỏ gọn, nhỏ hơn 400kB đã đươc cấu hình đầy đủ hoặc nhỏ hơn 250kB khi đã bỏ qua các tính năng tùy ý.
  • SQLite là tự chứa, nghĩa là không có sự phụ thuộc vào ngoại vi.
  • Các Transaction trong SQLite là tuân theo đầy đủ chuẩn ACID, đảm bảo truy cập an toàn từ nhiều tiến trình hoặc thread.
  • SQLite hỗ trợ hầu hết các tính năng của một ngôn ngữ truy vấn trong chuẩn SQL92.
  • SQLite được viết bằng ANSI-C và cung cấp API đơn giản và dễ dàng sử dụng.
  • SQLite là có sẵn trên UNIX (Linux, Mac OS-X, Android, iOS) và Windows (Win32, WinCE, WinRT).

 

Công cụ tạo file sqlite:

Tải về firefox và cài đặt:

https://www.mozilla.org/vi/firefox/new/

Sau khi cài đặt xong mở fireFox: Chọn vào biểu tượng 4 gạch như hình vẽ, Chọn Add- ons, Vào ô search tìm kiếm “sqlite manager” và Thêm nó vào fireFox.

Screen Shot 2016-07-26 at 14.18.13 Screen Shot 2016-07-26 at 14.19.26

 

Sau khi cài đặt xong chọn vào customize  thêm app sqlite Manager kéo  vào menu. Ok như thế chúng ta đã có công cụ sqlite manager!!!

Tạo một File sqlite :

Mở ứng dụng : Chọn vào Database-> New Database-> đặt tên “infor” chọn Ok sau đó chọn vị trí lưu.! Như thế chúng ta đã tao file thành công.

Screen Shot 2016-07-26 at 14.33.01 Screen Shot 2016-07-26 at 14.33.10

Tạo một bảng dữ liệu mới để truy vấn : phải chuột vào Table, chọn create Table

Screen Shot 2016-07-26 at 14.42.59

Ta sẽ xuất hiện hộp thoại sau:

Screen Shot 2016-07-26 at 14.44.19

 

Đặt tên cho bảng dữ liệu ở ” Table Name” :

Column Name: Tên các trường.

Data type: Kiểu dữ liệu của trường.

Primary Key: Khoá chính.

Autoinc: Tự động tăng.

Allow Null: không được phép Null.

Unique: Giá trị không được phép trùng nhau.

Screen Shot 2016-07-26 at 14.52.53

 

Bảng trên đặt tên là: “ThongTin” với các trường mẫu như ví dụ trên. Đây là bảng mẫu mình sẽ truy vấn trong bài này.

Sau khi tạo xong như trên chúng ta chọn vào Nút OK -> sau đó nhấn vào YES thế  là chúng ta đã tạo bảng “ThongTin” thành công.

Chọn vào table “ThongTin”, chọn menu Browse & search Nhấn vào nút Add để thêm dữ liệu cho bảng: Ở Đây mình đã thêm dữ liệu như trong bảng.

Screen Shot 2016-07-26 at 15.09.36

Add thêm dữ liệu rồi nhấn Ok sau đó chọn YES: dữ liệu tuỳ các bạn thêm vào, Ở đây mình lấy mẫu dữ liệu ở bảng trên.

Screen Shot 2016-07-26 at 15.09.50

 

=> Đầu tiên,  chúng ta đã có file Sqlite bao gồm một bảng:”ThongTin” gồm các trường id, Name, Age, và các dữ liệu mẫu truyền vào.

Thêm Framework sqlite3, và thêm file sqlite vào dự án:

Tạo một dự án mới và đặt tên “sqlite”.

Screen Shot 2016-07-26 at 15.22.32

 

Kéo file sqlite vừa tạo ra vào trong Project, chọn các dấu tick như hình vẽ!Screen Shot 2016-07-26 at 15.24.05

 

– Thêm frameWork sqlite:

click vào file sqlite-> chọn built phases-> Chọn link binary như hình vẽ-> nhấn vào nút + để thêm thư viện hỗ trợ,

Screen Shot 2016-07-26 at 15.26.18

 

sau đó tìm đến sqlite3.tbd nhấn vào Add để thêm framwork.

 

 

 

 

 

 

 

Screen Shot 2016-07-26 at 15.25.44

 

Như vậy chúng ta đã thêm được file sqlite, và framework sqlite3.dtb.

Sau khi thực hiện xong nó sẽ trông như thế này:

Screen Shot 2016-07-26 at 15.32.16

 

 

 

Kết nối đến sqlite 3:

Chúng ta tạo ra một class chuyên sử lý kết nối đến CSDL, và các chuẩn truy vấn và lấy dữ liệu.

Vào New file->chọn source-> Chọn Cocoa Touch next,

Screen Shot 2016-07-26 at 15.55.45

 

Đặt tên class quản lý là “DBManager”,  kiểu kế thừa là NSObject:

sau đó chọn next ,vị trí lưu!
Screen Shot 2016-07-26 at 15.51.48Như thế chúng ta đã tạo được class quản lý:

Nhiệm vụ của chúng ta khai báo và thực thi chuẩn sqlite.

  • Đầu tiên là sao chép file “infor.sql” vào trong thư mục document đây là thư mục lưu lại các file dùng cho tất cả ứng dụng có thể truy cập, Ta không làm việc trực tiếp với “infor.sql” trong source code. Bởi vì khi ta truy vấn dữ liệu trong file bị thay đổi, ảnh hưởng đến dữ liệu ban đầu chúng ta truyền vào!.
    Sao chép như thế nào?  Để sao chép được chúng ta phải xác định được đường dẫn file file trong source code, và đường dẫn đích trong thư mục docments.
    Trong DBManager.h:

    #import <Foundation/Foundation.h>
    
    @interface DBManager : NSObject// class quản lý vào ra truy vấn CSDL
    -(instancetype)initWithDatabaseFilename:(NSString *)dbFilename;// hàm khởi tạo có tham số.
    
    @end

    Hàm khởi tạo có tham số: tham số truyền vào là tên file.
    Trong file DBManager.m: Ta khai báo thêm 2 biến,  đường dẫn đến file documents, và tên file sqlite.

    // đường dẫn đến thư mục documents
    @property (nonatomic, strong) NSString *documentsDirectory;
    // tên file + đuôi file
    @property (nonatomic, strong) NSString *databaseFilename;

    Thực thi hàm khởi tạo có tham số: Mình đã comment rất cụ thể từng dòng lệnh trong code, Ở đây thực thi lấy đường dẫn đến thư mục document, và tên file, gán vào 2 biến chúng ta khai báo phía trên:

    -(instancetype)initWithDatabaseFilename:(NSString *)dbFilename{
        self = [super init];
        if (self) {
            // Lấy đường dẫn đến thư mục documents, Kết quả trả về mảng có một phần tử!
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            // đường dẫn gán vào property documentsDirectory
            self.documentsDirectory = [paths objectAtIndex:0];
            
            // Tên file được gán vào property databaseFilename
            self.databaseFilename = dbFilename;
            
            // sao chép file ở source code vào trong Thư mục documents
            [self copyDatabaseIntoDocumentsDirectory];
        }
        return self;
    }

    Hàm cho phép sao chép: Các bạn đọc kỹ phần comment code :

    -(void)copyDatabaseIntoDocumentsDirectory{
        // Lấy đường dẫn cụ thể đến file đích bao gồm đường dẫn documents+ tên file + đuôi file
        NSString *destinationPath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
        // Kiểm tra nếu file chưa tồn tại thì cho phép sao chép
        if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
            NSLog(@"file chưa tồn tại! Thực hiện sao chép");
            // Đường dẫn file ở trong source code
            NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseFilename];
            NSError *error;// tạo một đối tượng error kiểm tra khi đang sao chép có sinh ra lỗi không
            // sao chép file từ sourcePath -> documents
            [[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:&error];
            
            // Nếu có lỗi sao chép đưa ra thông báo!
            if (error != nil) {
                NSLog(@"%@", [error localizedDescription]);
            }
        }else{// ngược lại file đã tồn tại và không cần sao chép gì cả!
            NSLog(@"file đã tồn tại");
        }
    }
    

    Như vậy trong hàm khởi tạo có tham số chúng ta đã thực hiện sao chép file sqlite vào trong thư mục docments.

  • Mình sẽ giới thiệu cho các bạn khái quát một số hàm trong Sqlite:
    • sqlite3_open : Chức năng này được sử dụng để tạo ra và mở một tập tin cơ sở dữ liệu. Nếu file không tồn tại nó sẽ tạo ra và sau đó mở file, nếu tồn tại sẽ mở file.
    • sqlite3_prepare_v2 : Mục đích của chức năng này là để có được một câu lệnh SQL (truy vấn) ở định dạng chuỗi, và chuyển nó sang một định dạng thực thi được nhận biết bởi SQLite 3.
    • sqlite3_step : Chức năng này thực sự thực hiện một câu lệnh SQL (query) được chuẩn bị đúng chuẩn sqlite.
    • sqlite3_column_count :  Nó trả về tổng số cột (trường)  trong một bảng.
    • sqlite3_column_text : Phương pháp này trả về nội dung của một Ô trong mẫu giá trị của cột đó , trả về định dạng chuỗi C ( char * ).
    • sqlite3_column_name : Nó trả về tên của một cột(trường).
    • sqlite3_changes : Nó thực sự trả về số lượng các hàng bị ảnh hưởng, sau khi thực hiện một truy vấn.
    • sqlite3_last_insert_rowid : Nó trả về ID được chèn vào hàng cuối cùng của bảng.
    • sqlite3_errmsg : Nó trả về lỗi mô tả trong SQLite.
    • sqlite3_finalize : Nó xóa một câu truy vấn chuẩn bị trước đó.
    • sqlite3_close : Đóng CLDL.
  • DBManager.h
    @property (nonatomic, strong) NSMutableArray *arrColumnNames;// lưu lại tổng số cột trong một bảng
    
    @property (nonatomic) int affectedRows;// trả về số lượng hàng bị ảnh hưởng.
    
    @property (nonatomic) long long lastInsertedRowID;// trả vê ID
  • Kết nối: trong file DBManager.m:

Mình đã comment rất cụ thể từng dòng lệnh bên trong đoạn mã sau:

Mình khái quát lại nguyên lý của đoạn mã sau:

Hàm truyền vào tham số : câu lệnh truy vấn, và một biến trạng thái truy vấn:

– Chuẩn bị đường dẫn đích đến file, Ban đầu mở file, nếu không thành công thì nó tạo file và không làm gì cả, Nếu mở file thành công:

-> Chuẩn bị một câu truy vấn đúng chuẩn nhận dạng của Sqlite3, Nếu câu truy vấn sai thông báo  lỗi mô tả sqlite3,  Nếu đúng chuẩn:

Ở đây ta tách ra hai hướng:

Hướng 1 :(Nếu tham số truyền queryExecutable==NO)  Truy vấn và có lấy thông tin bản ghi, như trích lọc dữ liệu, lấy danh sách bản ghi(select)…

Và ta thực hiện truy vấn để lấy ra bản ghi.

Hướng 2: (Nếu ta truyền tam số queryExecutable==YES) chỉ truy vấn nhưng không lấy ra bản ghi (UPDATE,DELETE,INSERT,CREATE TABLE);

Cụ thể từng dòng code trong đoạn mã :

// Chuẩn mở file sqlite và truy vấn sqlite
// truyền vào 2 tham số : câu truy vấn và đối tượng bool để kiểm tra: update,delete, insert, create không lấy dữ liệu ra,chỉ truy vấn: Trích lọc lấy dữ liệu truy vấn và lấy ra bảng dữ liệu.
// ở đây mình truyền vào kiểu char bởi vì Sqlite không biết NSString là gì, chỉ cho phép làm việc với char

-(void)runQuery:(const char *)query isQueryExecutable:(BOOL)queryExecutable{
  // Khởi tạo một đối tượng của class sqlite.
  sqlite3 *sqlite3Database;
    // Lấy đường dẫn đích đến file.sqlite
  NSString *databasePath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
    
    // Khởi tạo mảng kết quả
    if (self.arrResults != nil) {// nếu mảng tồn tại(lưu) đối tượng
        [self.arrResults removeAllObjects];//xoá tất cả đối tượng
        self.arrResults = nil;// set về nil
    }
  self.arrResults = [[NSMutableArray alloc] init];// khởi tạo lại vùng nhớ cho mảng
    
    // Tương tự đối với mảng chứa các trường tên cột
    if (self.arrColumnNames != nil) {
        [self.arrColumnNames removeAllObjects];
        self.arrColumnNames = nil;
    }
    self.arrColumnNames = [[NSMutableArray alloc] init];
    
    
  // Mở cơ sở dữ liệu
    // truyền vào 2 tham số đường dẫn đích đến file định dạng UTF8, Đối tượng sqlite
    BOOL openDatabaseResult = sqlite3_open([databasePath UTF8String], &sqlite3Database);
  if(openDatabaseResult == SQLITE_OK) {// nếu mở csdl thành công
    // Đối tượng lưu trữ các truy vấn prepare statement
    sqlite3_stmt *compiledStatement;
    
        // Chuyển đổi câu truy vấn ở định dạng chuỗi sang câu truy vấn mà sqlite3 có thể nhận dạng được!
        // các tham số truyền vào đối tượng sqlite3, câu truy vấn,Lấy độ dài câu truy vấn, ở đây -1 độ dài tuỳ ý, đối tượng sqlite3_stmt lưu trữ truy vấn, Con trỏ trỏ tới phần chưa sử dụng của câu truy vấn Sql.
        // sau khi chuyển đổi câu truy vấn được lưu lại trong compiledStatement
        BOOL prepareStatementResult = sqlite3_prepare_v2(sqlite3Database, query, -1, &compiledStatement, NULL);
        // Nếu câu truy vấn được chuyển đổi thành công sang dạng sqlite nhận dạng đc.
    if(prepareStatementResult == SQLITE_OK) {
      // Kiểm tra nếu truyền vào QueryExecutable NO thì ta cần trích lọc dữ liệu , đọc dữ liệu ra.
      if (!queryExecutable){
                // Tạo một mảng lưu lại thông tin truy vấn!
                NSMutableArray *arrDataRow;
                
        // Thực thi truy vấn cho phép đọc thành công!
        while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
          // Khởi tạo mảng.
                    arrDataRow = [[NSMutableArray alloc] init];
                    
                    // trả về tổng số cột
                    int totalColumns = sqlite3_column_count(compiledStatement);
                    
                    //lặp hết các cột
          for (int i=0; i<totalColumns; i++){
                        // Trả về nội dung một cột kiểu char
            char *dbDataAsChars = (char *)sqlite3_column_text(compiledStatement, i);
                        
                        // If there are contents in the currenct column (field) then add them to the current row array.
            if (dbDataAsChars != NULL) {
                            // chuyển đổi định sang kiểu string
              [arrDataRow addObject:[NSString  stringWithUTF8String:dbDataAsChars]];
            }
                        
                        // Lưu tên của các cột !
                        if (self.arrColumnNames.count != totalColumns) {
                            dbDataAsChars = (char *)sqlite3_column_name(compiledStatement, i);
                            [self.arrColumnNames addObject:[NSString stringWithUTF8String:dbDataAsChars]];
                        }
                    }
          
          // Store each fetched data row in the results array, but first check if there is actually data.
          if (arrDataRow.count > 0) {// môt đối tượng trong arrResults là một mảng!.
                        [self.arrResults addObject:arrDataRow];
          }
        }
      }
      else {
                // Nếu chỉ truy vấn Update , Delete, insert không cần đưa ra dữ liệu
                
        // Execute the query.
                int executeQueryResults = sqlite3_step(compiledStatement);
                if (executeQueryResults == SQLITE_DONE) {// Nếu truy vấn thành công "chỉ truy vấn không đọc dữ liệu".
//                    // Trả về  số lượng hàng bị ảnh hưởng.
//                    self.affectedRows = sqlite3_changes(sqlite3Database);
//                    
//                    // trả về số đối tượng được chèn vào ở dòng cuối cùng.
//                    self.lastInsertedRowID = sqlite3_last_insert_rowid(sqlite3Database);
        }
        else {
          // Lỗi mô tả sqlite.
                    NSLog(@"DB Error: %s", sqlite3_errmsg(sqlite3Database));
        }
      }
    }
    else {
            // In the database cannot be opened then show the error message on the debugger.
            // Nếu xảy ra lỗi mô tả sqlite.
      NSLog(@"%s", sqlite3_errmsg(sqlite3Database));
    }
    
    // Giải phóng một truy vấn được chuẩn bị
    sqlite3_finalize(compiledStatement);
    
  }
    
    // Đóng lại CSDL
  sqlite3_close(sqlite3Database);
}

Như thế chúng ta có thể truy vấn sqlite, Tuy nhiên để cụ thể cọn gàng mình thêm hai hàm sau trong DBManager.h:

2 hàm giúp ta thực hiện đúng 2 loại truy vấn đã tách thành hướng 2 ở phía trên:

-(NSArray *)loadDataFromDB:(NSString *)query;// Truy vấn có đọc dữ liệu ra

-(void)executeQuery:(NSString *)query;// chỉ truy vấn không đọc dữ liệu

DBManager.m  để thực thi :

– Đối với hàm cần trích lọc bản ghi: Ta sẽ trả về một mảng lưu lại bản ghi truy vấn được, truyền vào tham số: câu truy vấn ,và queryExecutable==NO;

– Đối với hàm chỉ truy vấn: ta chỉ cần truyền vào Câu truy vấn ,queryExecutable==YES, không cần trả về bản ghi!

-(NSArray *)loadDataFromDB:(NSString *)query{
    
    [self runQuery:[query UTF8String] isQueryExecutable:NO];
    
    return (NSArray *)self.arrResults;
}


-(void)executeQuery:(NSString *)query{
    
    [self runQuery:[query UTF8String] isQueryExecutable:YES];
}

=> Như thế đối vơi class bên ngoài chúng ta chỉ cần truyền vào câu truy vấn và các chuẩn sqlite được thực thi!

Bắt đầu truy vấn nào: 🙂

vào ViewController.h :

#import "DBManager.h"

Tạo đối tượng dbManager.

@property DBManager *dbManager;

Trong ViewDidLoad:

Truyền vào tên file sqlite. Tạo 2 hàm:

excuteQueryInsert: chèn thêm đối tượng vào trong bảng

loadDataInfor: trả về thông tất cả thông tin của bảng

- (void)viewDidLoad {
    [super viewDidLoad];
    self.dbManager = [[DBManager alloc] initWithDatabaseFilename:@"infor.sql"];
    [self executeQueryInsert];
    [self loadDataInfor];
    
}

Thực thi hai hàm khởi tạo bên trên:

Tạo câu truy vấn như trong sql có thể tham khảo tại đây: sau đó gọi 2 hàm truy vấn  bên class DBManager.

-(void) loadDataInfor{
    // Form the query.
    NSString *query = @"SELECT * FROM ThongTin";
//NSString *query = @"select * from peopleInfo";
    NSArray *arr;//=[[NSArray alloc]init];
    arr = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];
    NSLog(@"%@",arr);
    // Reload the table view.
}


-(void) executeQueryInsert{
    NSString *query=@"INSERT INTO ThongTin VALUES ('5','Nam','22')";
    NSArray *arr;//=[[NSArray alloc]init];
    arr = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];
    
}

Như vậy mình đã hướng dẫn cụ thể về sqlite!

link full code:

http://www.mediafire.com/download/b1efpiyht6ceftp/sqlite+2.zip

Hãy để lại suy nghĩ của bạn, để chúng tôi chỉnh sửa cố gắng hơn !!!

Có gì trong phần tiếp theo : Mình sẽ giới thiệu cho các bạn vê AutoLayout một khái niệm tuyệt vời chỉ có trong IOS.

  • Nếu các bạn muốn biết thêm các kỹ thuật khác về ios, các bạn có thể tham khảo thêm các khóa học ios tại đây.

 

Add a Comment

Your email address will not be published. Required fields are marked *