Android: อัปเกรดเวอร์ชัน DB และเพิ่มตารางใหม่


118

ฉันได้สร้างตาราง sqlite สำหรับแอปแล้ว แต่ตอนนี้ฉันต้องการเพิ่มตารางใหม่ในฐานข้อมูล

ฉันเปลี่ยนเวอร์ชัน DB ตามด้านล่าง

private static final int DATABASE_VERSION = 2;

และเพิ่มสตริงเพื่อสร้างตาราง

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

onCreateและonUpgradeด้านล่าง:

@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);

    }

แต่ด้วยเหตุผลบางประการจึงไม่มีการสร้างตารางใหม่ ผมทำอะไรผิดหรือเปล่า?


นี้ก็เป็นอีกหนึ่งวิธีการแก้ปัญหาที่น่าสนใจ แต่เพื่อให้ห่างไกลรุ่นที่แข็งแกร่งที่สุดที่ผมเคยเห็นก็คือที่นี่
Suragch

คำตอบ:


280

1. เกี่ยวกับ onCreate () และ onUpgrade ()

onCreate(..)ถูกเรียกทุกครั้งที่ติดตั้งแอปใหม่ onUpgradeจะถูกเรียกเมื่อใดก็ตามที่มีการอัปเกรดและเปิดใช้แอปและเวอร์ชันฐานข้อมูลจะไม่เหมือนกัน

2. การเพิ่มเวอร์ชันฐานข้อมูล

คุณต้องมีตัวสร้างเช่น:

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

สำคัญ: การเพิ่มเวอร์ชันของแอปเพียงอย่างเดียวไม่เพียงพอสำหรับonUpgradeการเรียกใช้!

3. อย่าลืมผู้ใช้ใหม่ของคุณ!

อย่าลืมเพิ่ม

database.execSQL(DATABASE_CREATE_color);

onCreate () ของคุณเช่นกันหรือแอพที่ติดตั้งใหม่จะไม่มีตาราง

4. วิธีจัดการกับการเปลี่ยนแปลงฐานข้อมูลหลายรายการในช่วงเวลาหนึ่ง

เมื่อคุณมีการอัปเกรดแอปอย่างต่อเนื่องหลายรายการมีการอัปเกรดฐานข้อมูลคุณต้องตรวจสอบให้แน่ใจว่า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); 
   }
}

ด้วยวิธีนี้เมื่อผู้ใช้อัปเกรดจากเวอร์ชัน 1 เป็นเวอร์ชัน 3 ผู้ใช้จะได้รับการอัปเดตทั้งสองอย่าง เมื่อผู้ใช้อัปเกรดจากเวอร์ชัน 2 เป็น 3 พวกเขาจะได้รับการอัปเดตรุ่นแก้ไข 3 ... ท้ายที่สุดแล้วคุณไม่สามารถนับฐานผู้ใช้ของคุณได้ 100% ในการอัปเกรดทุกครั้งที่เผยแพร่การอัปเดต บางครั้งพวกเขาข้ามการอัปเดตหรือ 12 :)

5. รักษาหมายเลขการแก้ไขของคุณภายใต้การควบคุมในขณะที่พัฒนา

และสุดท้าย ... โทร

adb uninstall <yourpackagename>

ถอนการติดตั้งแอปทั้งหมด เมื่อคุณติดตั้งอีกครั้งคุณรับประกันว่าจะได้รับผลกระทบonCreateซึ่งจะทำให้คุณไม่ต้องเพิ่มเวอร์ชันฐานข้อมูลลงในสตราโตสเฟียร์ในขณะที่คุณพัฒนา ...


5
เกี่ยวกับ # 4: จะไม่ควรใช้oldVersionข้อโต้แย้งที่ดีกว่านี้หรือไม่? หากคำสั่งอัพเกรดใด ๆ สามารถทำซ้ำได้คุณอาจต้องทำซ้ำในฐานข้อมูลส่วนใหญ่ที่เป็นปัจจุบัน ถ้างบอย่างใดอย่างหนึ่งไปตัดทอนตารางนั่นจะแย่มาก
Greyson

3
@Greyson: จุดดี! จริงๆแล้วฉันรู้สึกโง่เล็กน้อยที่ไม่เคยคิดถึงเรื่องนี้เลย บางครั้งฉันคิดว่าเรามีนิสัยชอบใช้ข้อโต้แย้งที่เราต้องการและเพิกเฉยต่อสิ่งที่เหลือ!
jkschneider

1
คุณเป็นผู้ควบคุมฐานข้อมูลทำไมคุณถึงเปลี่ยนชื่อ?
jkschneider

3
newVersionค่อนข้างไร้ประโยชน์เนื่องจากคุณตั้งค่าเวอร์ชันฐานข้อมูลปัจจุบันไว้ในตัวสร้าง (ดูตอนที่ 2) และจะตรงกันเสมอ แนวคิดหลักในที่นี้คือคุณไม่ต้องการเพียงแค่อัปเกรดจากที่ใดก็ตามที่ผู้ใช้ไปถึงnewVersionโดยไม่ต้องผ่านการอัปเกรดที่เพิ่มขึ้นอื่น ๆ ในระหว่างนั้น
jkschneider

2
@kai CREATE_READINGSตรรกะไม่ควรอยู่ใน onUpgrade เนื่องจากเป็นonCreateวิธีการของเวอร์ชันแรกของคุณ ให้คิดว่ากรณีต่างๆในonUpgradeสวิตช์เป็น "ฉันกำลังอัปเกรดจากoldVersion" คุณจะไม่สร้างตารางการอ่านหากคุณอัปเกรดจากเวอร์ชัน 1 เนื่องจากควรมีอยู่แล้ว หวังว่านี่จะสมเหตุสมผลนะ ...
jkschneider

9

รหัสของคุณถูกต้อง ข้อเสนอแนะของฉันคือฐานข้อมูลคิดว่าได้รับการอัปเกรดแล้ว หากคุณดำเนินการโครงการหลังจากเพิ่มหมายเลขเวอร์ชัน แต่ก่อนที่จะเพิ่มการexecSQLโทรฐานข้อมูลบนอุปกรณ์ทดสอบ / โปรแกรมจำลองของคุณอาจเชื่อว่าเป็นเวอร์ชัน 2 แล้ว

วิธีที่รวดเร็วในการตรวจสอบคือการเปลี่ยนหมายเลขเวอร์ชันเป็น 3 - หากอัปเกรดหลังจากนั้นคุณจะรู้ว่าเป็นเพราะอุปกรณ์ของคุณเชื่อว่าได้รับการอัปเกรดแล้ว


จากนั้นตามที่คาดไว้รหัสของคุณก็ใช้ได้ ไม่ใช่เมื่อมันถูกเรียกใช้ทีละน้อย อย่าลืมเพิ่มการสร้างตารางให้onCreate()เหมือนที่ jkschneider ชี้ให้เห็น
Greyson

2

คุณสามารถใช้onUpgradeวิธีการของ SQLiteOpenHelper ในเมธอด onUpgrade คุณจะได้รับ oldVersion เป็นหนึ่งในพารามิเตอร์

ในการonUpgradeใช้งานswitchและในแต่ละรายการให้caseใช้หมายเลขเวอร์ชันเพื่อติดตามฐานข้อมูลเวอร์ชันปัจจุบัน

เป็นการดีที่สุดที่คุณจะวนซ้ำจากที่หนึ่งoldVersionไปnewVersionทีละทีละversionครั้งแล้วอัปเกรดฐานข้อมูลทีละขั้นตอน สิ่งนี้มีประโยชน์มากเมื่อมีผู้ที่มีฐานข้อมูลเวอร์ชัน 1 อัปเกรดแอปหลังจากใช้เวลานานเป็นเวอร์ชันที่ใช้ฐานข้อมูลเวอร์ชัน 7 และแอปเริ่มหยุดทำงานเนื่องจากการเปลี่ยนแปลงที่เข้ากันไม่ได้

จากนั้นการอัปเดตในฐานข้อมูลจะดำเนินการอย่างชาญฉลาดครอบคลุมทุกกรณีที่เป็นไปได้กล่าวคือรวมการเปลี่ยนแปลงในฐานข้อมูลที่ทำสำหรับเวอร์ชันใหม่แต่ละเวอร์ชันและป้องกันไม่ให้แอปพลิเคชันของคุณหยุดทำงาน

ตัวอย่างเช่น:

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;
    }

}

หากคุณลบข้อความแบ่งเหล่านั้นคุณไม่จำเป็นต้องวนซ้ำ
Tash Pemhiwa

แต่ oldVersion ต้องเพิ่มขึ้นในแต่ละกรณีเพื่อผ่านกรณีต่อไป @TashPemhiwa
Beulah Ana

เหตุผลที่คำสั่ง switch ต้องหยุดพักก็คือสามารถเรียกใช้หลายเคสพร้อมกันได้และจะเป็นเช่นนั้นแม้ว่าจะไม่ตรงตามเงื่อนไขของเคสก็ตาม @BeulahAna
Tash Pemhiwa

หากคุณเพิ่มตัวแบ่งและบางฐานข้อมูลมีเวอร์ชันเก่าหรือล่าสุดการสืบค้นของคุณอาจล้มเหลวดังนั้นจึงไม่จำเป็นต้องใช้ตัวแบ่งตัวอย่างการแก้ไขตารางหากคอลัมน์บางคอลัมน์มีการเปลี่ยนแปลงในเวอร์ชัน db บางคอลัมน์แล้วการสืบค้นของคุณอาจล้มเหลวตามลำดับการสูญเสียของเวอร์ชัน db
Neeraj Singh

2

คำตอบของ @ jkschneider ถูกต้อง อย่างไรก็ตามมีแนวทางที่ดีกว่า

เขียนการเปลี่ยนแปลงที่จำเป็นในไฟล์ sql สำหรับการอัปเดตแต่ละครั้งตามที่อธิบายไว้ในลิงก์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) ;

ไฟล์. sql เหล่านี้จะถูกเรียกใช้ในเมธอด onUpgrade () ตามเวอร์ชันของฐานข้อมูล

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();
            }
        }
    }
}

ตัวอย่างโครงการมีให้ในลิงค์เดียวกัน: https://github.com/riggaroo/AndroidDatabaseUpgrades


1
ฉันเพิ่งจะมาที่นี่และเขียนคำแนะนำเดียวกันนี้ ฉันดีใจที่คุณทำมันแล้ว ผู้คนควรอ่านบทความที่คุณเชื่อมโยงอย่างแน่นอน นี่คือสิ่งที่Android SQLiteAssetHelperแนะนำสำหรับการอัปเกรด ยังเป็นสิ่งที่CL. ( ผู้เชี่ยวชาญ SQLite ที่นี่ในกองมากเกิน) ขอแนะนำ
Suragch

ความคิดเห็นนี้สิ่งที่ฉันกำลังมองหา สคริปต์ sql, +1
blueware

1

การจัดการเวอร์ชันฐานข้อมูลเป็นส่วนสำคัญในการพัฒนาแอปพลิเคชัน ฉันคิดว่าคุณมีคลาส AppDbHelper ที่ขยายSQLiteOpenHelperแล้ว เมื่อคุณขยายคุณจะต้องใช้onCreateและonUpgradeวิธีการ

  1. เมื่อไหร่onCreateและonUpgradeวิธีการที่เรียกว่า

    • onCreate เรียกว่าเมื่อติดตั้งแอปใหม่
    • onUpgrade เรียกว่าเมื่ออัปเดตแอป
  2. การจัดระเบียบเวอร์ชันฐานข้อมูลฉันจัดการเวอร์ชันด้วยวิธีการคลาส สร้างการใช้งานการย้ายอินเทอร์เฟซ เช่นสำหรับรุ่นแรกสร้างMigrationV1คลาสรุ่นที่สองสร้างMigrationV1ToV2(นี่คือหลักการตั้งชื่อของฉัน)


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

ตัวอย่างการโยกย้าย:

public class MigrationV1ToV2 implements Migration{
      public void run(SQLiteDatabase db){
        //create new tables
        //alter existing tables(add column, add/remove constraint)
        //etc.
     }
   }
  1. การใช้คลาส Migration

onCreate: เนื่องจากonCreateจะถูกเรียกเมื่อแอปพลิเคชันติดตั้งใหม่เราจึงจำเป็นต้องดำเนินการย้ายข้อมูลทั้งหมด (การอัปเดตเวอร์ชันฐานข้อมูล) ดังนั้นonCreateพระทัยลักษณะเช่นนี้

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: วิธีนี้จะถูกเรียกเมื่อติดตั้งแอปพลิเคชันแล้วและได้รับการอัปเดตเป็นเวอร์ชันแอปพลิเคชันใหม่ หากแอปพลิเคชันมีการเปลี่ยนแปลงฐานข้อมูลใด ๆ ให้ทำการเปลี่ยนแปลงฐานข้อมูลทั้งหมดในคลาส Migration ใหม่และเวอร์ชันฐานข้อมูลที่เพิ่มขึ้น

ตัวอย่างเช่นสมมติว่าผู้ใช้ได้ติดตั้งแอปพลิเคชันที่มีฐานข้อมูลเวอร์ชัน 1 และขณะนี้เวอร์ชันฐานข้อมูลได้รับการอัปเดตเป็น 2 (การอัปเดตสคีมาทั้งหมดจะถูกเก็บไว้ในMigrationV1ToV2) ตอนนี้เมื่อแอปพลิเคชันอัปเกรดเราจำเป็นต้องอัปเกรดฐานข้อมูลโดยใช้การเปลี่ยนแปลงสคีมาฐานข้อมูลในMigrationV1ToV2ลักษณะนี้:

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
    }
}

หมายเหตุ: การอัพเกรดทั้งหมด (ที่กล่าวถึงในonUpgrade) ในสคีมาฐานข้อมูลควรดำเนินการในonCreate

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.