Android : DB 버전 업그레이드 및 새 테이블 추가


117

내 앱에 대한 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. db 버전 증가

다음과 같은 생성자가 필요합니다.

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 2011

3
@Greyson : 좋은 지적입니다! 솔직히 생각 해보지 않아서 좀 멍청 해. 때때로 나는 우리가 원하는 주장을 사용하고 나머지는 무시하는 습관이 있다고 생각합니다!
jkschneider 2011

1
당신은 데이터베이스를 제어하는데 왜 이름을 바꾸겠습니까?
jkschneider

3
newVersion어쨌든 생성자에서 항상 현재 데이터베이스 버전을 설정하고 (2 부 참조) 항상 일치하므로 쓸모가 없습니다. 여기서 핵심 아이디어 newVersion는 중간에 다른 모든 증분 업그레이드 를 거치지 않고 사용자가 곧바로 어디에서든 업그레이드하는 것을 원하지 않는다는 것 입니다.
jkschneider 2015 년

2
@kai CREATE_READINGS논리는 onCreate첫 번째 버전 의 메서드에 있었기 때문에 onUpgrade에 있어서는 안됩니다 . onUpgrade스위치 의 경우 를 "I am upgrade FROM oldVersion" 로 생각하십시오 . 이미 존재해야하므로 버전 1에서 업그레이드하는 경우 판독 테이블을 만들지 않습니다. 희망이 의미 ...합니다
jkschneider

9

코드가 올바른 것 같습니다. 내 제안은 데이터베이스가 이미 업그레이드되었다고 생각한다는 것입니다. 버전 번호를 늘린 후 execSQL호출을 추가하기 전에 프로젝트를 실행 한 경우 테스트 장치 / 에뮬레이터의 데이터베이스는 이미 버전 2라고 생각할 수 있습니다.

이를 확인하는 빠른 방법은 버전 번호를 3으로 변경하는 것입니다. 이후에 업그레이드하면 장치가 이미 업그레이드되었다고 믿었 기 때문이라는 것을 알 수 있습니다.


그러면 예상대로 코드가 괜찮 았습니다. 점진적으로 실행되었을 때가 아닙니다. onCreate()jkschneider가 지적한 것처럼 테이블 생성을 추가하는 것을 잊지 마십시오.
Greyson 2011

2

SQLiteOpenHelper의 onUpgrade방법을 사용할 수 있습니다 . onUpgrade 메소드에서 매개 변수 중 하나로 oldVersion을 가져옵니다.

에서 onUpgrade사용 switch와의 각각 case의 데이터베이스의 현재 버전을 추적하기 위해 버전 번호를 사용합니다.

에서 oldVersion로 반복 하여 한 번에 1 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 버전에서 이미 변경된 경우 테이블을 변경하면 DB 버전의 손실 시퀀스에 따라 쿼리가 실패 할 수 있습니다.
Neeraj Singh

2

@jkschneider의 대답이 맞습니다. 그러나 더 나은 접근 방식이 있습니다.

https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/ 링크에 설명 된대로 각 업데이트에 대해 SQL 파일에 필요한 변경 사항을 작성합니다.

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입니다. ( Stack Overflow SQLite 전문가) .
Suragch

내가 찾던 댓글입니다. SQL 스크립트, +1
blueware

1

데이터베이스 버전 처리는 애플리케이션 개발에서 매우 중요한 부분입니다. 나는 이미 AppDbHelper 확장 클래스가 있다고 가정합니다 SQLiteOpenHelper. 확장 할 때 구현 onCreateonUpgrade방법 이 필요합니다 .

  1. 호출 시기 onCreateonUpgrade메서드

    • 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. 마이그레이션 클래스 사용

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:이 메서드는 응용 프로그램이 이미 설치되어 있고 새 응용 프로그램 버전으로 업데이트 된 경우 호출됩니다. 응용 프로그램에 데이터베이스 변경 사항이 포함되어 있으면 모든 데이터베이스 변경 사항을 새 마이그레이션 클래스에 넣고 데이터베이스 버전을 증가시킵니다.

예를 들어 사용자가 데이터베이스 버전 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.