2011-11-15 35 views
103

Tôi đã tạo các bảng sqlite cho ứng dụng của mình, nhưng bây giờ tôi muốn thêm một bảng mới vào cơ sở dữ liệu.Android: nâng cấp phiên bản DB và thêm bảng mới

Tôi đã thay đổi phiên bản DB như sau

private static final int DATABASE_VERSION = 2; 

và gia tăng chuỗi để tạo bảng

private static final String DATABASE_CREATE_color = 
    "CREATE TABLE IF NOT EXISTS files(color text, incident_id text)"; 

onCreateonUpgrade như sau:

@Override 
    public void onCreate(SQLiteDatabase database) { 
     database.execSQL(DATABASE_CREATE_incident); 
     database.execSQL(DATABASE_CREATE_audio); 
     database.execSQL(DATABASE_CREATE_video); 
     database.execSQL(DATABASE_CREATE_image); 

    } 

    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     //drop table and add new tables when version 2 released. 
     db.execSQL(DATABASE_CREATE_color); 

    } 

Nhưng vì một lý do mới bảng không được tạo. Tôi đang làm gì sai?

+0

[This] (https://stackoverflow.com/a/3424444/3681880) là một giải pháp thú vị, nhưng cho đến nay phiên bản mạnh mẽ nhất mà tôi đã thấy là [ở đây] (https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correct /). – Suragch

Trả lời

243

1. Về onCreate() và onUpgrade()

onCreate(..) được gọi bất cứ khi nào ứng dụng được cài đặt mới. onUpgrade được gọi bất cứ khi nào ứng dụng được nâng cấp và khởi chạy và phiên bản cơ sở dữ liệu không giống nhau.

2. Tăng dần phiên bản db

Bạn cần một nhà xây dựng như:

MyOpenHelper(Context context) { 
    super(context, "dbname", null, 2); // 2 is the database version 
} 

QUAN TRỌNG: Tăng dần các phiên bản ứng dụng không thôi là không đủ cho onUpgrade được gọi!

3. Đừng quên người dùng mới của bạn!

Đừng quên để thêm

database.execSQL(DATABASE_CREATE_color); 

phương pháp onCreate() của bạn như các ứng dụng tốt hoặc mới được cài đặt sẽ thiếu bàn.

4. Làm thế nào để đối phó với nhiều thay đổi cơ sở dữ liệu theo thời gian

Khi bạn có nâng cấp ứng dụng liên tiếp, một số trong đó có việc nâng cấp cơ sở dữ liệu, bạn muốn chắc chắn để kiểm tra oldVersion:

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    switch(oldVersion) { 
    case 1: 
     db.execSQL(DATABASE_CREATE_color); 
     // we want both updates, so no break statement here... 
    case 2: 
     db.execSQL(DATABASE_CREATE_someothertable); 
    } 
} 

Bằng cách này khi người dùng nâng cấp từ phiên bản 1 lên phiên bản 3, họ sẽ nhận được cả hai bản cập nhật. Khi người dùng nâng cấp từ phiên bản 2 lên 3, họ chỉ nhận được bản cập nhật sửa đổi 3 ... Sau tất cả, bạn không thể đếm trên 100% cơ sở người dùng của mình để nâng cấp mỗi khi bạn phát hành bản cập nhật. Đôi khi họ bỏ qua một bản cập nhật hoặc 12 :)

5. Giữ số sửa đổi của bạn dưới sự kiểm soát trong khi phát triển

Và cuối cùng ... gọi

adb uninstall <yourpackagename> 

hoàn toàn gỡ bỏ ứng dụng. Khi bạn cài đặt lại, bạn được bảo đảm để đạt được số onCreate giúp bạn không phải tiếp tục tăng phiên bản cơ sở dữ liệu vào tầng bình lưu khi bạn phát triển ...

+6

Điểm tốt về việc thêm lời gọi vào 'onCreate()' – Greyson

+4

Về # 4: Sẽ không tốt hơn nếu sử dụng đối số 'oldVersion' được truyền? Nếu bất kỳ câu lệnh nâng cấp nào có thể lặp lại, bạn có thể sẽ lặp lại chúng trên cơ sở dữ liệu chủ yếu. Nếu một trong các câu lệnh là cắt một bảng, điều đó sẽ rất tệ. – Greyson

+3

@Greyson: Điểm tuyệt vời! Thành thật mà nói, tôi cảm thấy một chút câm vì không bao giờ thực sự nghĩ về nó. Đôi khi tôi nghĩ chúng ta có thói quen sử dụng những lý lẽ chúng ta muốn và bỏ qua phần còn lại! – jkschneider

9

Mã của bạn có vẻ chính xác. Đề nghị của tôi là cơ sở dữ liệu đã nghĩ rằng nó đã được nâng cấp. Nếu bạn đã thực hiện dự án sau khi tăng số phiên bản, nhưng trước khi thêm cuộc gọi execSQL, cơ sở dữ liệu trên thiết bị/trình mô phỏng thử nghiệm của bạn có thể đã tin nó ở phiên bản 2.

Cách nhanh chóng để xác minh điều này là thay đổi phiên bản số đến 3 - nếu nó nâng cấp sau đó, bạn biết đó là chỉ vì thiết bị của bạn tin rằng nó đã được nâng cấp.

+1

Cảm ơn. Đạt đã giúp :) Sau khi thay đổi thành 3 nó đã hoạt động :) –

+0

Sau đó, như dự kiến, mã của bạn là tốt; không chỉ khi nó được tăng dần. Hãy nhớ thêm việc tạo bảng vào 'onCreate()' như jkschneider đã chỉ ra. – Greyson

2

Bạn có thể sử dụng phương thức onUpgrade của SQLiteOpenHelper. Trong phương thức onUpgrade, bạn lấy oldVersion làm một trong các tham số.

Trong onUpgrade hãy sử dụng switch và trong mỗi số case s hãy sử dụng số phiên bản để theo dõi phiên bản cơ sở dữ liệu hiện tại. Tốt nhất là bạn lặp lại từ oldVersion đến newVersion, tăng version lên 1 tại một thời điểm và sau đó nâng cấp từng bước cơ sở dữ liệu. Điều này rất hữu ích khi ai đó có phiên bản cơ sở dữ liệu 1 nâng cấp ứng dụng sau một thời gian dài, lên phiên bản sử dụng cơ sở dữ liệu phiên bản 7 và ứng dụng bắt đầu gặp sự cố do một số thay đổi không tương thích.

Sau đó, cập nhật trong cơ sở dữ liệu sẽ được thực hiện từng bước, bao gồm tất cả các trường hợp có thể, tức là kết hợp các thay đổi trong cơ sở dữ liệu được thực hiện cho mỗi phiên bản mới và do đó ngăn ứng dụng của bạn bị lỗi.

Ví dụ:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    switch (oldVersion) { 
    case 1: 
     String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " + "name_of_column_to_be_added" + " INTEGER"; 
     db.execSQL(sql); 
     break; 

    case 2: 
     String sql = "SOME_QUERY"; 
     db.execSQL(sql); 
     break; 
    } 

} 
+0

Nếu bạn loại bỏ những tuyên bố phá vỡ, bạn sẽ không cần một vòng lặp –

1

@ câu trả lời jkschneider là đúng. Tuy nhiên có một cách tiếp cận tốt hơn.

Viết những thay đổi cần thiết trong một tập tin sql cho mỗi lần cập nhật như được mô tả trong các liên kết https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/

from_1_to_2.sql

ALTER TABLE books ADD COLUMN book_rating INTEGER; 

from_2_to_3.sql

ALTER TABLE books RENAME TO book_information; 

from_3_to_4.sql

ALTER TABLE book_information ADD COLUMN calculated_pages_times_rating INTEGER; 
UPDATE book_information SET calculated_pages_times_rating = (book_pages * book_rating) ; 

Các tệp .sql này sẽ được thực hiện trong phương thức onUpgrade() theo phiên bản của cơ sở dữ liệu.

DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper { 

    private static final int DATABASE_VERSION = 4; 

    private static final String DATABASE_NAME = "database.db"; 
    private static final String TAG = DatabaseHelper.class.getName(); 

    private static DatabaseHelper mInstance = null; 
    private final Context context; 

    private DatabaseHelper(Context context) { 
     super(context, DATABASE_NAME, null, DATABASE_VERSION); 
     this.context = context; 
    } 

    public static synchronized DatabaseHelper getInstance(Context ctx) { 
     if (mInstance == null) { 
      mInstance = new DatabaseHelper(ctx.getApplicationContext()); 
     } 
     return mInstance; 
    } 

    @Override 
    public void onCreate(SQLiteDatabase db) { 
     db.execSQL(BookEntry.SQL_CREATE_BOOK_ENTRY_TABLE); 
     // The rest of your create scripts go here. 

    } 


    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     Log.e(TAG, "Updating table from " + oldVersion + " to " + newVersion); 
     // You will not need to modify this unless you need to do some android specific things. 
     // When upgrading the database, all you need to do is add a file to the assets folder and name it: 
     // from_1_to_2.sql with the version that you are upgrading to as the last version. 
     try { 
      for (int i = oldVersion; i < newVersion; ++i) { 
       String migrationName = String.format("from_%d_to_%d.sql", i, (i + 1)); 
       Log.d(TAG, "Looking for migration file: " + migrationName); 
       readAndExecuteSQLScript(db, context, migrationName); 
      } 
     } catch (Exception exception) { 
      Log.e(TAG, "Exception running upgrade script:", exception); 
     } 

    } 

    @Override 
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 

    } 

    private void readAndExecuteSQLScript(SQLiteDatabase db, Context ctx, String fileName) { 
     if (TextUtils.isEmpty(fileName)) { 
      Log.d(TAG, "SQL script file name is empty"); 
      return; 
     } 

     Log.d(TAG, "Script found. Executing..."); 
     AssetManager assetManager = ctx.getAssets(); 
     BufferedReader reader = null; 

     try { 
      InputStream is = assetManager.open(fileName); 
      InputStreamReader isr = new InputStreamReader(is); 
      reader = new BufferedReader(isr); 
      executeSQLScript(db, reader); 
     } catch (IOException e) { 
      Log.e(TAG, "IOException:", e); 
     } finally { 
      if (reader != null) { 
       try { 
        reader.close(); 
       } catch (IOException e) { 
        Log.e(TAG, "IOException:", e); 
       } 
      } 
     } 

    } 

    private void executeSQLScript(SQLiteDatabase db, BufferedReader reader) throws IOException { 
     String line; 
     StringBuilder statement = new StringBuilder(); 
     while ((line = reader.readLine()) != null) { 
      statement.append(line); 
      statement.append("\n"); 
      if (line.endsWith(";")) { 
       db.execSQL(statement.toString()); 
       statement = new StringBuilder(); 
      } 
     } 
    } 
} 

Một dự án ví dụ được cung cấp trong liên kết tương tự cũng: https://github.com/riggaroo/AndroidDatabaseUpgrades

+0

Tôi sắp sửa đến đây và viết lời khuyên tương tự. Tôi rất vui vì bạn đã làm điều đó. Mọi người chắc chắn nên đọc bài viết bạn đã liên kết. Đây cũng là những gì [Android SQLiteAssetHelper] (https://github.com/jgilfelt/android-sqlite-asset-helper) đề xuất nâng cấp. Nó cũng là những gì [CL.] (Https://stackoverflow.com/users/11654/cl) (* chuyên gia SQLite ở đây trên Stack Overflow) [khuyến cáo] (https://stackoverflow.com/a/42361543/ 3681880). – Suragch

0

Xử lý các phiên bản cơ sở dữ liệu là một phần rất quan trọng của phát triển ứng dụng. Tôi cho rằng bạn đã có lớp AppDbHelper mở rộng SQLiteOpenHelper. Khi bạn mở rộng nó, bạn sẽ cần phải thực hiện phương thức onCreateonUpgrade.

  1. Khi onCreateonUpgrade phương pháp gọi là

    • onCreate gọi khi ứng dụng mới được cài đặt.
    • onUpgrade được gọi khi ứng dụng được cập nhật.
  2. Tổ chức phiên bản cơ sở dữ liệu Tôi quản lý phiên bản trong một phương pháp lớp học. Tạo triển khai di chuyển giao diện. Ví dụ. Đối với phiên bản đầu tiên tạo MigrationV1 lớp, phiên bản thứ hai tạo MigrationV1ToV2 (đây là những quy ước đặt tên của tôi)


    public interface Migration { 
     void run(SQLiteDatabase db);//create tables, alter tables 
    } 

Ví dụ di cư:

public class MigrationV1ToV2 implements Migration{ 
     public void run(SQLiteDatabase db){ 
     //create new tables 
     //alter existing tables(add column, add/remove constraint) 
     //etc. 
    } 
    } 
  1. Sử dụng lớp Migration

onCreate: Kể từ khi onCreate sẽ được gọi khi ứng dụng mới được cài đặt, chúng tôi cũng cần thực thi tất cả các lần di chuyển (cập nhật phiên bản cơ sở dữ liệu). Vì vậy, onCreate sẽ trông như thế này:

public void onCreate(SQLiteDatabase db){ 
     Migration mV1=new MigrationV1(); 
     //put your first database schema in this class 
     mV1.run(db); 
     Migration mV1ToV2=new MigrationV1ToV2(); 
     mV1ToV2.run(db); 
     //other migration if any 
    } 

onUpgrade: Phương pháp này sẽ được gọi khi ứng dụng đã được cài đặt và nó được cập nhật lên phiên bản ứng dụng mới. Nếu ứng dụng chứa bất kỳ thay đổi cơ sở dữ liệu nào thì hãy đặt tất cả các thay đổi cơ sở dữ liệu trong lớp Migration mới và phiên bản cơ sở dữ liệu gia tăng. Ví dụ: cho phép người dùng đã cài đặt ứng dụng có cơ sở dữ liệu phiên bản 1 và bây giờ phiên bản cơ sở dữ liệu được cập nhật thành 2 (tất cả các bản cập nhật lược đồ được giữ trong MigrationV1ToV2). Bây giờ khi ứng dụng được nâng cấp, chúng tôi cần phải nâng cấp cơ sở dữ liệu bằng cách áp dụng những thay đổi giản đồ cơ sở dữ liệu trong MigrationV1ToV2 như thế này:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    if (oldVersion < 2) { 
     //means old version is 1 
     Migration migration = new MigrationV1ToV2(); 
     migration.run(db); 
    } 
    if (oldVersion < 3) { 
     //means old version is 2 
    } 
} 

Lưu ý: Tất cả các nâng cấp (nêu tại onUpgrade) trong lược đồ cơ sở dữ liệu cần phải được thực hiện trong onCreate

Các vấn đề liên quan