Khi nào SQLiteOpenHelper onCreate () / onUpTHER () chạy?


293

Tôi đã tạo các bảng của mình trong SQLiteOpenHelper onCreate()nhưng nhận

SQLiteException: no such table

hoặc là

SQLiteException: no such column

lỗi. Tại sao?

GHI CHÚ:

(Đây là bản tóm tắt tổng hợp của hàng chục câu hỏi tương tự mỗi tuần. Cố gắng cung cấp câu hỏi / câu trả lời wiki cộng đồng "chính tắc" ở đây để tất cả những câu hỏi đó có thể được chuyển đến một tài liệu tham khảo tốt.)


12
@Ndupza Đây không phải là vấn đề thực sự của tôi, tôi đã chán ngấy việc viết cùng một câu trả lời / bình luận cho lần thứ N.
laalto

Câu trả lời:


352

SQLiteOpenHelper onCreate()và các onUpgrade()cuộc gọi lại được gọi khi cơ sở dữ liệu thực sự được mở, ví dụ như bằng một cuộc gọi đến getWritableDatabase(). Cơ sở dữ liệu không được mở khi chính đối tượng trợ giúp cơ sở dữ liệu được tạo.

SQLiteOpenHelperphiên bản các tập tin cơ sở dữ liệu. Số phiên bản là intđối số được truyền cho hàm tạo . Trong tệp cơ sở dữ liệu, số phiên bản được lưu trữ trong PRAGMA user_version.

onCreate()chỉ chạy khi tệp cơ sở dữ liệu không tồn tại và chỉ được tạo. Nếu onCreate()trả về thành công (không ném ngoại lệ), cơ sở dữ liệu được coi là được tạo với số phiên bản được yêu cầu. Là một ngụ ý, bạn không nên bắt SQLExceptions trong onCreate()chính mình.

onUpgrade()chỉ được gọi khi tệp cơ sở dữ liệu tồn tại nhưng số phiên bản được lưu trữ thấp hơn yêu cầu trong hàm tạo. Các onUpgrade()nên cập nhật các lược đồ bảng lên phiên bản yêu cầu.

Khi thay đổi lược đồ bảng trong mã ( onCreate()), bạn nên đảm bảo cơ sở dữ liệu được cập nhật. Hai cách tiếp cận chính:

  1. Xóa tệp cơ sở dữ liệu cũ để onCreate()chạy lại. Điều này thường được ưa thích tại thời điểm phát triển nơi bạn có quyền kiểm soát các phiên bản đã cài đặt và mất dữ liệu không phải là vấn đề. Một số cách để xóa tệp cơ sở dữ liệu:

    • Gỡ cài đặt ứng dụng. Sử dụng trình quản lý ứng dụng hoặc adb uninstall your.package.nametừ trình bao.

    • Xóa dữ liệu ứng dụng. Sử dụng trình quản lý ứng dụng.

  2. Tăng phiên bản cơ sở dữ liệu để onUpgrade()được gọi. Điều này hơi phức tạp hơn vì cần nhiều mã hơn.

    • Để nâng cấp lược đồ thời gian phát triển trong đó mất dữ liệu không phải là vấn đề, bạn chỉ cần sử dụng execSQL("DROP TABLE IF EXISTS <tablename>")để xóa các bảng hiện có và gọi onCreate()để tạo lại cơ sở dữ liệu.

    • Đối với các phiên bản đã phát hành, bạn nên triển khai di chuyển dữ liệu onUpgrade()để người dùng của bạn không bị mất dữ liệu của họ.


2
@Laalto // di chuyển dữ liệu trong onUpgrad () // Bạn có thể vui lòng giải thích về điều này.
bCliks

2
@bala Không thuộc phạm vi của câu hỏi / câu trả lời này. Nếu bạn có một câu hỏi, hãy đăng nó như một câu hỏi.
laalto

2
@Jaskey Số phiên bản dành cho mã của bạn, tức là phiên bản lược đồ mà mã dự kiến ​​sẽ chạy. Nếu tệp cũ hơn (từ phiên bản trước của ứng dụng của bạn), tệp cần được nâng cấp.
laalto

4
Vì vậy, tôi cần mã hóa VERSION DB trong SQLiteHelper mỗi lần tôi sửa đổi lược đồ, để khi ứng dụng cũ chạy và nhận kết nối db và thấy nó đã cũ, thì onUpTHER sẽ được xử lý thay vì onCreate, đây là đúng?
Jaskey

2
Cảm ơn bạn ! Điều này có ý nghĩa với tôi. Vui lòng xác minh nếu tôi hiểu rõ. Vì vậy, chúng tôi cần phải làm 1. mỗi khi chúng tôi cập nhật lược đồ, sửa đổi biến DB_VERSION (mã cứng). 2. Vào onUpdate(), kiểm tra mọi phiên bản cũ và thực hiện di chuyển dữ liệu phù hợp. Và sau đó khi người dùng cập nhật ứng dụng của họ (họ có tệp db cũ), onUpgradesẽ được kích hoạt và nếu người dùng mới cài đặt, onCreate()sẽ được kích hoạt.
Jaskey

97

Để thêm các điểm còn thiếu ở đây, theo yêu cầu của Jaskey

Phiên bản cơ sở dữ liệu được lưu trữ trong SQLitetệp cơ sở dữ liệu.

bắt là người xây dựng

SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

Vì vậy, khi trình xây dựng trình trợ giúp cơ sở dữ liệu được gọi bằng name(param 2), nền tảng sẽ kiểm tra xem cơ sở dữ liệu có tồn tại hay không và nếu cơ sở dữ liệu tồn tại, nó sẽ lấy thông tin phiên bản từ tiêu đề tệp cơ sở dữ liệu và kích hoạt lại cuộc gọi đúng

Như đã giải thích trong câu trả lời cũ hơn, nếu cơ sở dữ liệu với tên không tồn tại, nó sẽ kích hoạt onCreate.

Dưới đây giải thích onUpgradetrường hợp với một ví dụ.

Giả sử, phiên bản ứng dụng đầu tiên của bạn có (phiên bản DatabaseHelpermở rộng SQLiteOpenHelper) với phiên bản chuyển qua hàm tạo 1và sau đó bạn đã cung cấp một ứng dụng được nâng cấp với mã nguồn mới có phiên bản được chuyển qua 2, sau đó tự động khi DatabaseHelperđược xây dựng, kích hoạt nền tảng onUpgradebằng cách thấy tệp đã tồn tại, nhưng phiên bản thấp hơn phiên bản hiện tại mà bạn đã vượt qua.

Bây giờ hãy nói rằng bạn đang dự định cung cấp phiên bản ứng dụng thứ ba với phiên bản db vì 3(phiên bản db chỉ được tăng lên khi lược đồ cơ sở dữ liệu được sửa đổi). Trong các nâng cấp gia tăng như vậy, bạn phải viết logic nâng cấp từ mỗi phiên bản tăng dần để có mã duy trì tốt hơn

Ví dụ mã giả dưới đây:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  switch(oldVersion) {
    case 1:
       //upgrade logic from version 1 to 2
    case 2:
       //upgrade logic from version 2 to 3
    case 3:
       //upgrade logic from version 3 to 4
       break;
    default:
       throw new IllegalStateException(
                "onUpgrade() with unknown oldVersion " + oldVersion);
  }
}

Lưu ý các breaktuyên bố còn thiếu trong trường hợp 12. Đây là những gì tôi có nghĩa là bằng cách nâng cấp gia tăng.

Nói nếu phiên bản cũ là 2và phiên bản mới 4, thì logic sẽ nâng cấp cơ sở dữ liệu từ 2đến 3và sau đó thành4

Nếu phiên bản cũ là 3và phiên bản mới 4, nó sẽ chỉ chạy logic nâng cấp cho 3tới4


1
Tôi nghĩ rằng bạn muốn chuyển đổi của bạn (newVersion) được chuyển đổi (oldVersion) thay thế. Bạn cũng có thể muốn xác minh rằng newVersion là 4 (chứ không phải 5 hoặc 3; vì logic của bạn giả sử phiên bản mới là 4). Vì vậy, nếu phiên bản cũ là 2 và phiên bản mới là 5, bạn sẽ nhấn trường hợp 4: và nâng cấp từ 3 lên 4 (có lẽ không nên có hành vi mong đợi).
joe p

đúng - lỗi đánh máy .. nhưng nếu phiên bản mới là 5 -> thì nó sẽ luôn ném IllegalStateException và nhà phát triển sẽ sửa nó bằng cách thêm trường hợp 5 ..
Aun

1
Nếu người dùng chỉ nâng cấp ứng dụng của mình từ phiên bản 2 lên 3 thì sao? Trong trường hợp đó cũng vậy, tất cả các trường hợp tối đa trường hợp 4 sẽ chạy.
Paramvir Singh

6
@param người dùng không thể làm điều đó. Anh ta chỉ có thể nâng cấp 2 lên mới nhất (ở đây 4).
Habeeb Perwad

20

onCreate()

  1. Khi chúng tôi tạo DataBase lần đầu tiên (nghĩa là Cơ sở dữ liệu không tồn tại), hãy onCreate()tạo cơ sở dữ liệu với phiên bản được truyền vào SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

  2. onCreate()phương thức đang tạo các bảng bạn đã xác định và thực thi bất kỳ mã nào khác mà bạn đã viết. Tuy nhiên, phương pháp này sẽ chỉ được gọi nếu tệp SQLite bị thiếu trong thư mục dữ liệu của ứng dụng ( /data/data/your.apps.classpath/databases).

  3. Phương thức này sẽ không được gọi nếu bạn đã thay đổi mã của mình và khởi chạy lại trong trình giả lập. Nếu bạn muốn onCreate()chạy, bạn cần sử dụng adb để xóa tệp cơ sở dữ liệu SQLite.

onUpgrade()

  1. SQLiteOpenHelper nên gọi siêu xây dựng.
  2. Các onUpgrade()phương pháp sẽ chỉ được gọi khi phiên bản số nguyên lớn hơn phiên bản hiện tại chạy trong ứng dụng.
  3. Nếu bạn muốn onUpgrade()phương thức được gọi, bạn cần tăng số phiên bản trong mã của mình.

1
Bạn có thể vui lòng giải thích thêm câu trả lời của bạn thêm một chút mô tả về giải pháp bạn cung cấp không?
abarisone

10

Có thể tôi đã quá muộn nhưng tôi muốn chia sẻ câu trả lời ngắn gọn và ngọt ngào của mình. Vui lòng kiểm tra Trả lời cho cùng một vấn đề. Nó chắc chắn sẽ giúp bạn. Không có thông số kỹ thuật sâu hơn.

Nếu bạn tự tin về cú pháp tạo bảng, thì điều đó có thể xảy ra khi bạn thêm cột mới vào cùng bảng, cho điều đó ...

1) Gỡ cài đặt khỏi thiết bị của bạn và chạy lại.

HOẶC LÀ

2) Cài đặt -> ứng dụng -> ClearData

HOẶC LÀ

3) Thay đổi DATABASE_VERSIONtrong lớp "DatabaseHandler" của bạn (Nếu bạn đã thêm cột mới thì nó sẽ tự động nâng cấp)

public DatabaseHandler(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

HOẶC LÀ

4) Thay đổi DATABASE_NAMEtrong lớp "DatabaseHandler" của bạn (Tôi gặp phải vấn đề tương tự. Nhưng tôi đã thành công bằng cách thay đổi DATABASE_NAME.)


Tôi có DB riêng và sử dụng lớp SQLiteAssetHelper. Vì vậy, tôi đã tạo DB theo kịch bản sql trước đó và db đã được tạo. Bằng cách sử dụng SQLiteAssetHelper, nó không thể sao chép DB cho đến khi gỡ cài đặt ứng dụng dưới dạng trình giả lập hoặc thiết bị, bởi vì đó là một db có cùng phiên bản.
Behzad

4

Những điểm cần nhớ khi gia hạn SQLiteOpenHelper

  1. super(context, DBName, null, DBversion); - Điều này nên được gọi dòng đầu tiên của hàm tạo
  2. ghi đè onCreateonUpgrade(nếu cần)
  3. onCreatesẽ chỉ được gọi khi getWritableDatabase()hoặc getReadableDatabase()được thực thi. Và điều này sẽ chỉ được gọi một lần khi không DBNamexác định trong bước đầu tiên. Bạn có thể thêm truy vấn tạo bảng trên onCreatephương thức
  4. Bất cứ khi nào bạn muốn thêm bảng mới, chỉ cần thay đổi DBversionvà thực hiện các truy vấn trong onUpgradebảng hoặc chỉ cần gỡ cài đặt sau đó cài đặt ứng dụng.

3

onCreate được gọi lần đầu tiên khi cần tạo bảng. Chúng ta cần ghi đè phương thức này trong đó chúng ta viết tập lệnh để tạo bảng được thực thi bởi SQLiteDatabase. phương thức execfox. Sau khi thực hiện trong lần triển khai đầu tiên, phương thức này sẽ không được gọi trở đi.

onUpgrade Phương pháp này được gọi khi phiên bản cơ sở dữ liệu được nâng cấp. Giả sử lần đầu tiên triển khai, phiên bản cơ sở dữ liệu là 1 và trong lần triển khai thứ hai có sự thay đổi về cấu trúc cơ sở dữ liệu như thêm cột bổ sung trong bảng. Giả sử phiên bản cơ sở dữ liệu là 2 bây giờ.


2

Bạn có thể tạo cơ sở dữ liệu và bảng như

public class DbHelper extends SQLiteOpenHelper {
private static final String DBNAME = "testdatbase.db";
private static final int VERSION = 1;

public DbHelper(Context context) {
    super(context, DBNAME, null, VERSION);
    // TODO Auto-generated constructor stub
}

@Override
public void onCreate(SQLiteDatabase db) {
    // TODO Auto-generated method stub
    db.execSQL("create table BookDb(id integer primary key autoincrement,BookName text,Author text,IssuedOn text,DueDate text,Fine text,Totalfine text");

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS BookDb");
    onCreate(db);
  }
}

Lưu ý: nếu bạn muốn tạo một bảng khác hoặc thêm các cột hoặc không có bảng đó, chỉ cần tăng VERSION


2

Cơ sở dữ liệu Sqlite ghi đè hai phương thức

1) onCreate (): Phương thức này chỉ được gọi một lần khi ứng dụng được khởi động lần đầu tiên. Vì vậy, nó chỉ được gọi một lần

2) onUpgrad () Phương thức này được gọi khi chúng ta thay đổi phiên bản cơ sở dữ liệu, sau đó phương thức này được gọi. Nó được sử dụng để thay đổi cấu trúc bảng như thêm cột mới sau khi tạo Lược đồ DB


1

không tìm thấy bảng như vậy chủ yếu là khi bạn chưa mở SQLiteOpenHelperlớp getwritabledata()và trước đó, bạn cũng phải gọi hàm tạo với databasename & phiên bản. Và OnUpgradeđược gọi bất cứ khi nào có giá trị nâng cấp trong số phiên bản được đưa ra trong SQLiteOpenHelperlớp.

Dưới đây là đoạn mã (Không tìm thấy cột nào như vậy vì chính tả trong tên cột):

public class database_db {
    entry_data endb;
    String file_name="Record.db";
    SQLiteDatabase sq;
    public database_db(Context c)
    {
        endb=new entry_data(c, file_name, null, 8);
    }
    public database_db open()
    {
        sq=endb.getWritableDatabase();
        return this;
    }
    public Cursor getdata(String table)
    {
        return sq.query(table, null, null, null, null, null, null);
    }
    public long insert_data(String table,ContentValues value)
    {
        return sq.insert(table, null, value);
    }
    public void close()
    {
        sq.close();
    }
    public void delete(String table)
    {
        sq.delete(table,null,null);
    }
}
class entry_data extends SQLiteOpenHelper
{

    public entry_data(Context context, String name, SQLiteDatabase.CursorFactory factory,
                      int version) {
        super(context, name, factory, version);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase sqdb) {
        // TODO Auto-generated method stub

        sqdb.execSQL("CREATE TABLE IF NOT EXISTS 'YOUR_TABLE_NAME'(Column_1 text not null,Column_2 text not null);");

    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
          onCreate(db);
    }

}

1

Nếu bạn quên cung cấp một chuỗi "tên" làm đối số thứ hai cho hàm tạo, nó sẽ tạo ra một cơ sở dữ liệu "trong bộ nhớ" bị xóa khi bạn đóng ứng dụng.


0

Gỡ cài đặt ứng dụng của bạn khỏi trình giả lập hoặc thiết bị. Chạy lại ứng dụng. (OnCreate () không được thực thi khi cơ sở dữ liệu đã tồn tại)


0

Tên cơ sở dữ liệu của bạn phải kết thúc bằng .db, chuỗi truy vấn của bạn phải có dấu kết thúc (;)


0

Kiểm tra lại truy vấn của bạn trong lớp DatabaseHandler / DatabaseManager của bạn (mà bạn đã từng lấy)


0

Trong trường hợp của tôi, tôi nhận được các mục từ tệp XML với <string-array>, nơi tôi lưu trữ <item>s. Trong các <item>s này , tôi giữ các chuỗi SQL và áp dụng từng cái một databaseBuilder.addMigrations(migration). Tôi đã mắc một lỗi, quên thêm \trước khi trích dẫn và có ngoại lệ:

android.database.sqlite.SQLiteException: không có cột như vậy: some_value (mã 1 SQLITE_ERROR) :, trong khi biên dịch: INSERT INTO tên_bảng (id, tên) VALUES (1, some_value)

Vì vậy, đây là một biến thể đúng:

<item>
    INSERT INTO table_name(id, name) VALUES(1, \"some_value\")
</item>

-2

Phương thức của Sqliteopenhelper có các phương thức tạo và nâng cấp, tạo được sử dụng khi bất kỳ bảng nào được tạo lần đầu tiên và phương thức nâng cấp sẽ được gọi mọi lúc bất cứ khi nào số cột của bảng được thay đổi.


Phương thức onUpgrad được gọi khi phiên bản cơ sở dữ liệu tăng, không phải khi số lượng cột được thay đổi. Tham chiếu: developer.android.com/reference/android/database/sqlite/ Kẻ, int, int)
Roger Huang
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.