SQLiteOpenHelper onCreate () / onUpgrade () ทำงานเมื่อใด


293

ฉันได้สร้างตารางของฉันในของฉันSQLiteOpenHelper onCreate()แต่ได้รับ

SQLiteException: no such table

หรือ

SQLiteException: no such column

ข้อผิดพลาด ทำไม?

บันทึก:

(นี่คือการสรุปของคำถามที่คล้ายกันหลายสิบรายการทุกสัปดาห์ความพยายามที่จะจัดหาคำถาม / คำตอบวิกิชุมชน "มาตรฐาน" ที่นี่เพื่อให้คำถามเหล่านั้นสามารถนำไปอ้างอิงที่ดีได้)


12
@Nupupza นี่ไม่ใช่ปัญหาที่เกิดขึ้นจริงของฉันเพิ่งเบื่อเขียนคำตอบ / ความคิดเห็นเดียวกันสำหรับเวลาที่ Nth
laalto

คำตอบ:


352

SQLiteOpenHelper onCreate()และเรียกกลับถูกเรียกเมื่อฐานข้อมูลถูกเปิดจริงเช่นโดยการโทรไปยังonUpgrade() getWritableDatabase()ฐานข้อมูลไม่ถูกเปิดเมื่อสร้างวัตถุตัวช่วยฐานข้อมูลเอง

SQLiteOpenHelperรุ่นไฟล์ฐานข้อมูล หมายเลขรุ่นเป็นintอาร์กิวเมนต์ส่งผ่านไปยังตัวสร้าง PRAGMA user_versionในไฟล์ฐานข้อมูลหมายเลขรุ่นถูกเก็บไว้ใน

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

onUpgrade()จะเรียกเฉพาะเมื่อไฟล์ฐานข้อมูลที่มีอยู่ แต่หมายเลขรุ่นที่เก็บไว้ต่ำกว่าที่ร้องขอในตัวสร้าง onUpgrade()ควรปรับปรุง schema ของตารางเพื่อรุ่นที่ร้องขอ

เมื่อเปลี่ยนตารางสคีมาในรหัส ( onCreate()) คุณควรตรวจสอบให้แน่ใจว่ามีการอัพเดทฐานข้อมูล สองแนวทางหลัก:

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

    • ถอนการติดตั้งแอปพลิเคชัน ใช้ตัวจัดการแอปพลิเคชันหรือadb uninstall your.package.nameจากเชลล์

    • ล้างข้อมูลแอปพลิเคชัน ใช้ตัวจัดการแอปพลิเคชัน

  2. เพิ่มรุ่นฐานข้อมูลเพื่อให้onUpgrade()มีการเรียกใช้ สิ่งนี้ซับซ้อนกว่านี้เล็กน้อยเมื่อต้องการรหัสเพิ่มเติม

    • สำหรับการอัพเกรดสคีมาเวลาการพัฒนาที่การสูญเสียข้อมูลไม่เป็นปัญหาคุณสามารถใช้execSQL("DROP TABLE IF EXISTS <tablename>")เพื่อลบตารางที่มีอยู่ของคุณและโทรonCreate()เพื่อสร้างฐานข้อมูลใหม่

    • สำหรับรุ่นที่วางจำหน่ายคุณควรใช้การโยกย้ายข้อมูลonUpgrade()เพื่อให้ผู้ใช้ของคุณไม่สูญเสียข้อมูล


2
@Laalto // การย้ายข้อมูลใน onUpgrade () // คุณช่วยอธิบายเรื่องนี้ได้ไหม
bCliks

2
@bala ไม่ได้อยู่ในขอบเขตของคำถาม / คำตอบนี้ หากคุณมีคำถามอย่าลังเลที่จะโพสต์เป็นคำถาม
laalto

2
@Jaskey หมายเลขเวอร์ชันสำหรับโค้ดของคุณเช่นเวอร์ชันสกีมาที่โค้ดต้องการรัน หากไฟล์นั้นเก่ากว่า (จากแอพเวอร์ชันก่อนหน้าของคุณ) ไฟล์นั้นจะต้องได้รับการอัพเกรด
laalto

4
ดังนั้นฉันจำเป็นต้องเขียนโค้ด DB VERSION ใน SQLiteHelper ทุกครั้งที่ฉันแก้ไข schema เพื่อที่ว่าเมื่อแอปเก่ารันและรับการเชื่อมต่อ db และพบว่าเป็นรุ่นเก่า ขวา?
Jaskey

2
ขอบคุณ ! เรื่องนี้ทำให้รู้สึกถึงฉัน โปรดตรวจสอบว่าฉันเข้าใจดีหรือไม่ดังนั้นเราต้องทำ 1. ทุกครั้งที่เราปรับปรุง schema ให้แก้ไขตัวแปร DB_VERSION (รหัสยาก) 2. ในonUpdate()ให้ตรวจสอบทุกเวอร์ชั่นเก่าและทำการโยกย้ายข้อมูลอย่างเหมาะสม และเมื่อผู้ใช้อัปเดตแอป (พวกเขามีไฟล์ db เก่า) onUpgradeจะถูกเรียกใช้และหากผู้ใช้เพิ่งติดตั้งใหม่onCreate()จะถูกเรียก
Jaskey

97

เพื่อเพิ่มคะแนนที่หายไปเพิ่มเติมที่นี่ตามคำขอโดย Jaskey

เวอร์ชันฐานข้อมูลถูกเก็บไว้ในSQLiteไฟล์ฐานข้อมูล

catch เป็นตัวสร้าง

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

ดังนั้นเมื่อตัวสร้างผู้ช่วยฐานข้อมูลถูกเรียกด้วยname(พารามิเตอร์ที่ 2) แพลตฟอร์มจะตรวจสอบว่าฐานข้อมูลมีอยู่หรือไม่และถ้าฐานข้อมูลนั้นมีอยู่จะได้รับข้อมูลรุ่นจากส่วนหัวของไฟล์ฐานข้อมูลและทริกเกอร์การโทรกลับด้านขวา

onCreateตามที่อธิบายไว้แล้วในคำตอบที่เก่าถ้าฐานข้อมูลที่มีชื่อไม่ได้ที่มีอยู่ก็ทริกเกอร์

คำอธิบายด้านล่างอธิบายonUpgradeกรณีและตัวอย่าง

สมมติว่าเวอร์ชันแรกของแอปพลิเคชันของคุณมีDatabaseHelper(ขยายSQLiteOpenHelper) พร้อมตัวสร้างผ่านเวอร์ชันเป็น1และจากนั้นคุณให้แอปพลิเคชันที่อัปเกรดพร้อมด้วยซอร์สโค้ดใหม่ที่มีเวอร์ชันผ่านเป็น2จากนั้นโดยอัตโนมัติเมื่อDatabaseHelperสร้างonUpgradeขึ้นมา แต่เวอร์ชันต่ำกว่าเวอร์ชันปัจจุบันที่คุณผ่าน

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

ตัวอย่างรหัสเทียมด้านล่าง:

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

สังเกตเห็นหายไปbreakคำสั่งในกรณีและ1 2นี่คือสิ่งที่ฉันหมายถึงโดยการอัพเกรดที่เพิ่มขึ้น

พูดถ้ารุ่นเก่าเป็น2และรุ่นใหม่4แล้วตรรกะจะอัพเกรดฐานข้อมูลจาก2เป็น3และจากนั้นเป็น4

ถ้าเป็นรุ่นเก่า3และรุ่นใหม่4มันก็จะทำงานตรรกะอัพเกรด3ไป4


1
ฉันคิดว่าคุณต้องการให้สวิตช์ของคุณ (newVersion) เป็นสวิตช์ (oldVersion) แทน คุณอาจต้องการตรวจสอบว่ารุ่นใหม่คือ 4 (และไม่ใช่ 5 หรือ 3 เนื่องจากตรรกะของคุณสมมติว่ารุ่นใหม่ควรเป็น 4) เนื่องจากเป็นถ้ารุ่นเก่าเป็น 2 และรุ่นใหม่คือ 5 คุณจะ กดปุ่มเคส 4: และอัพเกรดจาก 3 เป็น 4 (ซึ่งอาจไม่เป็นไปตามที่คาดไว้)
joe p

ถูกต้อง - พิมพ์ผิด .. แต่ถ้ารุ่นใหม่คือ 5 -> มันจะโยน IllegalStateException เสมอและนักพัฒนาจะแก้ไขโดยเพิ่มเคส 5 ..
Aun

1
จะเกิดอะไรขึ้นหากผู้ใช้อัปเกรดแอปของเขาจากรุ่น 2 เป็น 3 เท่านั้น ในกรณีนั้นกรณีทั้งหมดจนถึงกรณีที่ 4 จะทำงาน
Paramvir Singh

6
ผู้ใช้ @param ไม่สามารถทำเช่นนั้นได้ เขาสามารถอัพเกรด 2 เป็นล่าสุด (ที่นี่ 4) เท่านั้น
Habeeb Perwad

20

onCreate()

  1. เมื่อเราสร้างฐานข้อมูลในครั้งแรก (เช่นฐานข้อมูลไม่อยู่) onCreate()สร้างฐานข้อมูลด้วยรุ่นที่ส่งผ่านมา SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

  2. onCreate()method กำลังสร้างตารางที่คุณกำหนดและเรียกใช้งานโค้ดอื่น ๆ ที่คุณเขียน อย่างไรก็ตามวิธีนี้จะถูกเรียกก็ต่อเมื่อไฟล์ SQLite หายไปในไดเรกทอรีข้อมูลของแอป ( /data/data/your.apps.classpath/databases)

  3. วิธีนี้จะไม่ถูกเรียกถ้าคุณได้เปลี่ยนรหัสและเปิดใหม่ในอีมูเลเตอร์ หากคุณต้องการonCreate()เรียกใช้คุณต้องใช้ adb เพื่อลบไฟล์ฐานข้อมูล SQLite

onUpgrade()

  1. SQLiteOpenHelper ควรเรียก super constructor
  2. onUpgrade()วิธีจะถูกเรียกเมื่อรุ่นจำนวนเต็มมีขนาดใหญ่กว่ารุ่นปัจจุบันทำงานในการตรวจสอบ
  3. หากคุณต้องการonUpgrade()วิธีการที่จะเรียกว่าคุณจะต้องเพิ่มหมายเลขรุ่นในรหัสของคุณ

1
คุณช่วยอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับโซลูชันที่คุณให้ได้ไหม
abarisone

10

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

หากคุณมีความมั่นใจเกี่ยวกับไวยากรณ์ในการสร้างตารางอาจเกิดขึ้นเมื่อคุณเพิ่มคอลัมน์ใหม่ในตารางเดียวกันสำหรับ ...

1) ถอนการติดตั้งจากอุปกรณ์ของคุณและเรียกใช้อีกครั้ง

หรือ

2) การตั้งค่า -> แอพ -> ClearData

หรือ

3) การเปลี่ยนแปลงDATABASE_VERSIONในคลาส "DatabaseHandler" ของคุณ (หากคุณเพิ่มคอลัมน์ใหม่มากกว่าที่จะอัปเกรดโดยอัตโนมัติ)

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

หรือ

4) การเปลี่ยนแปลงDATABASE_NAMEในคลาส "DatabaseHandler" ของคุณ (ฉันประสบปัญหาเดียวกัน แต่ฉันประสบความสำเร็จจากการเปลี่ยนDATABASE_NAME)


ฉันมีฐานข้อมูลของตัวเองและใช้คลาส SQLiteAssetHelper ดังนั้นฉันได้สร้าง DB โดย sql script มาก่อนและ db ถูกสร้างขึ้น ด้วยการใช้ SQLiteAssetHelper จะไม่สามารถคัดลอกฐานข้อมูลได้จนกว่าจะถอนการติดตั้งแอปในรูปแบบจำลองหรืออุปกรณ์เนื่องจากเป็นฐานข้อมูลที่มีเวอร์ชันเดียวกัน
Behzad

4

จุดที่ต้องจำเมื่อขยาย SQLiteOpenHelper

  1. super(context, DBName, null, DBversion); - สิ่งนี้ควรเรียกใช้ตัวสร้างบรรทัดแรก
  2. แทนที่onCreateและonUpgrade(ถ้าจำเป็น)
  3. onCreateจะถูกเรียกใช้เมื่อgetWritableDatabase()หรือgetReadableDatabase()ถูกดำเนินการเท่านั้น และสิ่งนี้จะถูกเรียกใช้เพียงครั้งเดียวเมื่อไม่มีการDBNameระบุในขั้นตอนแรก คุณสามารถเพิ่มการสร้างตารางแบบสอบถามในonCreateวิธีการ
  4. เมื่อใดก็ตามที่คุณต้องการเพิ่มตารางใหม่เพียงแค่เปลี่ยนDBversionและทำแบบสอบถามในonUpgradeตารางหรือเพียงถอนการติดตั้งแล้วติดตั้งแอพ

3

onCreateถูกเรียกใช้เป็นครั้งแรกเมื่อต้องการสร้างตาราง เราจำเป็นต้องแทนที่วิธีนี้ที่เราเขียนสคริปต์สำหรับการสร้างตารางซึ่งดำเนินการโดย SQLiteDatabase วิธีการ execSQL หลังจากดำเนินการในการปรับใช้ครั้งแรกวิธีนี้จะไม่ถูกเรียกเป็นต้นไป

onUpgrade วิธีนี้เรียกว่าเมื่อมีการอัพเกรดรุ่นฐานข้อมูล สมมติว่าสำหรับการปรับใช้ครั้งแรกรุ่นฐานข้อมูลคือ 1 และในการปรับใช้ครั้งที่สองมีการเปลี่ยนแปลงในโครงสร้างฐานข้อมูลเช่นการเพิ่มคอลัมน์พิเศษในตาราง สมมติว่าเวอร์ชันฐานข้อมูลคือ 2 ในขณะนี้


2

คุณสามารถสร้างฐานข้อมูลและตารางเช่น

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

หมายเหตุ: หากคุณต้องการสร้างตารางใหม่หรือเพิ่มคอลัมน์หรือไม่มีตารางดังกล่าวเพียงแค่เพิ่ม VERSION


2

ฐานข้อมูล Sqlite แทนที่สองวิธี

1) onCreate (): วิธีการนี้เรียกใช้เพียงครั้งเดียวเมื่อแอปพลิเคชันเริ่มต้นในครั้งแรก ดังนั้นจึงเรียกเพียงครั้งเดียว

2) onUpgrade () วิธีการนี้เรียกว่าเมื่อเราเปลี่ยนรุ่นฐานข้อมูลจากนั้นวิธีนี้จะถูกเรียกใช้มันใช้สำหรับการเปลี่ยนแปลงโครงสร้างตารางเช่นการเพิ่มคอลัมน์ใหม่หลังจากสร้าง DB Schema


1

ไม่พบตารางดังกล่าวเป็นหลักเมื่อคุณไม่ได้เปิดSQLiteOpenHelperคลาสด้วยgetwritabledata()และก่อนหน้านี้คุณต้องเรียก make constructor ด้วย databasename & version และOnUpgradeถูกเรียกเมื่อใดก็ตามที่มีค่าการอัพเกรดในหมายเลขรุ่นที่กำหนดในSQLiteOpenHelperชั้นเรียน

ด้านล่างเป็นข้อมูลโค้ด (ไม่พบคอลัมน์ดังกล่าวอาจเป็นเพราะการสะกดในชื่อคอลัมน์):

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

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


0

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


0

ชื่อฐานข้อมูลของคุณจะต้องลงท้ายด้วย. db นอกจากนี้สตริงแบบสอบถามของคุณจะต้องมี terminator (;)


0

ตรวจสอบคำค้นหาของคุณอีกครั้งในคลาส DatabaseHandler / DatabaseManager (ซึ่งคุณเคยทำ)


0

ในกรณีของฉันฉันได้รับรายการจากไฟล์ XML ด้วย<string-array>ที่ฉันเก็บ<item>ของ ในเหล่านี้<item>s ฉันถือสตริง SQL databaseBuilder.addMigrations(migration)และใช้อย่างใดอย่างหนึ่งโดยหนึ่งด้วย ฉันทำผิดพลาดหนึ่งครั้งลืมเพิ่ม\ก่อนที่จะพูดและได้รับการยกเว้น:

android.database.sqlite.SQLiteException: ไม่มีคอลัมน์ดังกล่าว: some_value (รหัส 1 SQLITE_ERROR): ขณะที่รวบรวม: INSERT INTO table_name (id, ชื่อ) ค่า (1, some_value)

ดังนั้นนี่คือตัวแปรที่ถูกต้อง:

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

-2

วิธีการของ Sqliteopenhelper มีวิธีการสร้างและอัพเกรดสร้างจะใช้เมื่อตารางใดเป็นครั้งแรกที่สร้างขึ้นและวิธีการอัพเกรดจะเรียกว่าทุกครั้งที่มีการเปลี่ยนแปลงจำนวนคอลัมน์ของตาราง


เมธอด onUpgrade จะถูกเรียกใช้เมื่อเวอร์ชันฐานข้อมูลเพิ่มขึ้นไม่ใช่เมื่อจำนวนคอลัมน์ถูกเปลี่ยน Ref: developer.android.com/reference/android/database/sqlite/… , int, int)
Roger Huang
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.