SQLite를 사용하는 Android의 외래 키 제약? 계단식 삭제시


91

두 개의 테이블이 있습니다. 트랙과 웨이 포인트, 트랙에는 여러 웨이 포인트가있을 수 있지만 웨이 포인트는 하나의 트랙에만 할당됩니다.

way points 테이블에는 트랙이 만들어지면 track_ID를 삽입하는 "trackidfk"라는 열이 있지만이 열에 외래 키 제약 조건을 설정하지 않았습니다.

트랙을 삭제할 때 할당 된 웨이 포인트를 삭제하고 싶은데 이것이 가능합니까?. 트리거 사용에 대해 읽었지만 Android에서 지원되지 않는다고 생각합니다.

웨이 포인트 테이블을 생성하려면 :

public void onCreate(SQLiteDatabase db) {
    db.execSQL( "CREATE TABLE " + TABLE_NAME 
                + " (" 
                + _ID         + " INTEGER PRIMARY KEY AUTOINCREMENT, " 
                + LONGITUDE   + " INTEGER," 
                + LATITUDE    + " INTEGER," 
                + TIME        + " INTEGER,"
                + TRACK_ID_FK + " INTEGER"
                + " );"
              );

    ...
}

답변:


237

on delete cascade가있는 외래 키 제약 조건이 지원되지만이를 활성화해야합니다.
난 그냥 내 SQLOpenHelper에 다음을 추가 했는데 트릭을 수행하는 것 같습니다.

@Override
public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (!db.isReadOnly()) {
        // Enable foreign key constraints
        db.execSQL("PRAGMA foreign_keys=ON;");
    }
}

내 참조 열을 다음과 같이 선언했습니다.

mailbox_id INTEGER REFERENCES mailboxes ON DELETE CASCADE

59
즉, SQLite 3.6.22가있는 Android 2.2 Froyo 이후에만 작동합니다
Intrications

@RedPlanet-이 제약이 적용되는 유일한 시간은 무언가가 데이터베이스에 기록 될 때이기 때문입니다. (당신이하는 모든 것이 db에서 읽는 것이라면이 제약을 깨뜨릴 수 없습니다.) 또한 Phil은 onOpen 메소드 대신 onConfigure 메소드에서하는 것이 더 낫습니다. 출처 : developer.android.com/reference/android/database/sqlite/…
Aneem

12
Google은 PRAGMA에서 구문을 작성하는 것이 좋지만 onConfigure()API 레벨 16 (Android 4.1)이 필요하므로 setForeignKeyConstraintsEnabled.
Pang

이전의 onCreate/ onDowngrade/ 에서 외래 키 제약 조건을 활성화하는 것도 고려해야 합니다. Android 4.1.1의 소스 코드를 참조하세요 . onUpgradeonOpen
Pang

1
super에 대한 호출을 포함하는 @Natix는 구현 된 클래스와 부모 사이에 중간 클래스가 도입되는 경우 올바른 기능을 보장합니다.
tbm

55

Android 4.1 (API 16) 이후 SQLiteDatabase 는 다음을 지원합니다.

public void setForeignKeyConstraintsEnabled (boolean enable)

26

e.shishkin의 게시물이 API 16에서 말했듯이 다음을 사용하여 SqLiteOpenHelper.onConfigure(SqLiteDatabase)메서드 에서 외래 키 제약 조건을 활성화해야 합니다.db.setForeignKeyConstraintsEnabled(boolean)

@Override
public void onConfigure(SQLiteDatabase db){
    db.setForeignKeyConstraintsEnabled(true);
}

10

더 완전한 답변으로 대답하기에는 너무 오래된 질문이 없습니다.

@Override public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (!db.isReadOnly()) {
        setForeignKeyConstraintsEnabled(db);
    }
    mOpenHelperCallbacks.onOpen(mContext, db);
}

private void setForeignKeyConstraintsEnabled(SQLiteDatabase db) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        setForeignKeyConstraintsEnabledPreJellyBean(db);
    } else {
        setForeignKeyConstraintsEnabledPostJellyBean(db);
    }
}

private void setForeignKeyConstraintsEnabledPreJellyBean(SQLiteDatabase db) {
    db.execSQL("PRAGMA foreign_keys=ON;");
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setForeignKeyConstraintsEnabledPostJellyBean(SQLiteDatabase db) {
    db.setForeignKeyConstraintsEnabled(true);
}

6

@phil이 언급 한 것이 무엇이든 좋습니다. 그러나 데이터베이스 자체에서 사용 가능한 다른 기본 방법을 사용하여 외래 키를 설정할 수 있습니다. 이것이 바로 setForeignKeyConstraintsEnabled (true)입니다.

@Override
public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (!db.isReadOnly()) {
        // Enable foreign key constraints
        db.execSQL("PRAGMA foreign_keys=ON;"); 
              //(OR)
        db.setForeignKeyConstraintsEnabled (true)
    }
}

문서의 경우 SQLiteDatabase.setForeignKeyConstraintsEnabled를 참조하십시오.


3
게시 한 문서는 다음과 같이 제안합니다. A good time to call this method is right after calling openOrCreateDatabase(File, SQLiteDatabase.CursorFactory) or in the onConfigure(SQLiteDatabase) callback. 따라서 대신 , 올바른 장소 onOpenonConfigure것 같습니다.
Paul Woitaschek

4

나는 SQLite가 이것을 즉시 지원한다고 생각하지 않습니다. 내 앱에서 내가하는 일은 :

  1. 거래 생성
  2. 상세 데이터 삭제 (예시의 웨이 포인트)
  3. 마스터 데이터 삭제 (예시의 트랙)
  4. 성공시 트랜잭션 커밋

이렇게하면 모든 데이터가 삭제되거나 전혀 삭제되지 않습니다.


그러나 한 가지 방법을 사용하여 두 테이블에서 삭제합니까?
jcrowson 2010 년

예, API의 Notes 샘플을 거의 사용했습니다. 귀하의 경우에 트랙이 될 것을 삭제할 때 트랜잭션을 생성하고 트랙과 웨이 포인트를 삭제하고 트랜잭션을 커밋합니다. 이 모든 것이 한 번에 이루어집니다.
Thorsten Dittmar

4

트리거는 Android에서 지원되며 해당 유형의 계단식 삭제는 sqlite에서 지원되지 않습니다. Android에서 트리거를 사용하는 예는 여기 에서 찾을 수 있습니다 . Thorsten이 말한 것처럼 트랜잭션을 사용하는 것은 아마도 트리거만큼 쉽습니다.


3

Android 1.6의 SQLite 버전은 3.5.9이므로 외래 키를 지원하지 않습니다.

http://www.sqlite.org/foreignkeys.html "이 문서는 SQLite 버전 3.6.19에 도입 된 SQL 외래 키 제약에 대한 지원을 설명합니다."

Froyo에서는 SQLite 버전 3.6.22이므로 ...

편집 : sqlite 버전을 보려면 : adb shell sqlite3 -version


그래서 그런 제약을 강요 할 수있는 방법이 있습니다 .. 제 말은 sqlite 버전을 업그레이드 할 수있는 방법이 있다는 것입니다. 위와 같이 sqlite 버전 3.5.9가있는 android 2.1로 소프트웨어 버전을 지원해야하기 때문입니다
NullPointerException

아니요, 모든 것을 스스로 처리해야합니다. (
GBouerat

1

"on delete cascade"가있는 외래 키는 Android 2.2 이상의 SQLite에서 지원됩니다. 그러나 그것들을 사용할 때는주의하십시오. 한 열에서 하나의 외래 키를 실행할 때 오류가보고되는 경우가 있지만 실제 문제는 자식 테이블의 다른 열 외래 키 제약 조건이나이 테이블을 참조하는 다른 테이블에 있습니다.

SQLite는 제약 조건 중 하나를 실행할 때 모든 제약 조건을 확인하는 것처럼 보입니다. 실제로 문서에 언급되어 있습니다. DDL 대 DML 제약 조건 검사.


0

Android Room을 사용하는 경우 다음과 같이하십시오.

Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
    .addCallback(object : RoomDatabase.Callback() {
        // Called when the database has been opened.
        override fun onOpen(db: SupportSQLiteDatabase) {
            super.onOpen(db)
            //True to enable foreign key constraints
            db.setForeignKeyConstraintsEnabled(true)
        }

        // Called when the database is created for the first time. 
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
        }
    }).build()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.