#sqlite #android-studio #foreign-keys #android-sqlite
Вопрос:
package com.example.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHelper extends SQLiteOpenHelper {
//making db and setting tables 1-6 names
private static final int DATABASE_VERSION = 4;
private static final String DATABASE_NAME = "contacts.db";
private static final String TABLE1_NAME = "contacts";
private static final String TABLE2_NAME = "Uinfo";
private static final String TABLE3_NAME = "exercises";
private static final String TABLE4_NAME = "calories";
private static final String TABLE5_NAME = "calendar";
// private static final String TABLE6_NAME = "settings";
//these are for table 1
private static final String COLUMN_ID = "id";
private static final String COLUMN_EMAIL = "email";
private static final String COLUMN_PASS = "pass";
private static final String COLUMN_UNAME = "uname";
private static final String COLUMN_NAME = "name";
//for table 2
private static final String COLUMN_ID2 = "id2";
private static final String COLUMN_AGE = "age";
private static final String COLUMN_WEIGHT = "weight";
private static final String COLUMN_HEIGHT = "height";
private static final String COLUMN_SEX = "sex";
private static final String COLUMN_FK_CONTACTS_ID = "id";
// private static final String COLUMN_FK_SETTINGS_ID = "id_6";
private static final String COLUMN_FK_CALENDAR_ID = "id5";
//creating tables
private static final String TABLE_CREATE = "create table " TABLE1_NAME
" (id integer primary key AUTOINCREMENT not null ,"
" name text not null , "
" email text not null ,"
" pass text not null, "
" uname text not null);";
private static final String TABLE2_CREATE = "create table " TABLE2_NAME
"(id2 integer primary key AUTOINCREMENT not null ,"
" age text not null , "
" weight text not null ,"
" height text not null, "
" sex text not null, "
" FOREIGN KEY (" COLUMN_ID2 ") REFERENCES " TABLE1_NAME "(" COLUMN_ID "),"
" FOREIGN KEY (" COLUMN_ID2 ") REFERENCES " TABLE5_NAME "(" COLUMN_ID5 "));";
//" FOREIGN KEY (" COLUMN_ID2 ") REFERENCES " TABLE6_NAME "(" COLUMN_ID6 "));";
в основном внешние ключи не отображаются в базе данных при ее просмотре.
Я попытался добавить в
"id integer not null"
перед первой строкой внешнего ключа, и это заставило базу данных прекратить запись данных в эту таблицу. Я обновил версию базы данных, чтобы посмотреть, будет ли это тоже иметь значение, но безрезультатно.
Я называю это неправильно? Возможно, мне просто не хватает правильного способа записи в столбце, в который входит внешний ключ. Хотя я не уверен, как это будет вставлено правильно.
Ответ №1:
Я полагаю, что ваша проблема заключается в непонимании того, что делает предложение FOREIGN KEY…..
Он не создает столбец для хранения внешнего ключа. Скорее, он добавляет ограничение (правило), которое гласит, что столбцы в создаваемой таблице ДОЛЖНЫ содержать существующее значение в таблице(столбцах), указанных после ССЫЛОК.
Утверждения верны и на самом деле создают ограничения внешнего ключа; как будет показано ниже.
Рассмотрим следующее (выполняется путем запуска кода с помощью Android Studio, который вы предоставили (плюс код, чтобы сделать его пригодным для использования), и, таким образом, создается база данных и необходимые таблицы)
- использование ОТЛАДКИ для остановки при создании таблицы Uinfo (а также других таблиц) и копировании значения из переменной TABLE2_CREATE, полученное значение :-
create table Uinfo(id2 integer primary key AUTOINCREMENT not null , age text not null , weight text not null , height text not null, sex text not null, FOREIGN KEY (id2) REFERENCES contacts(id), FOREIGN KEY (id2) REFERENCES calendar(id5));
Поместите это значение в инструмент SQLite (navicat), а затем запустите следующий SQL:-
create table contacts (id integer primary key AUTOINCREMENT not null , name text not null , email text not null , pass text not null, uname text not null);
CREATE TABLE calendar(id5 INTEGER PRIMARY KEY,other TEXT);
create table Uinfo(id2 integer primary key AUTOINCREMENT not null , age text not null , weight text not null , height text not null, sex text not null,
FOREIGN KEY (id2) REFERENCES contacts(id),
FOREIGN KEY (id2) REFERENCES calendar(id5));
SELECT * FROM Uinfo;
INSERT INTO Uinfo VALUES(null,10,5.6,70,'Female');
Результаты в :-
Очевидно, что ВСТАВКА не вставляла данные. Взгляд на журнал показывает:-
create table Uinfo(id2 integer primary key AUTOINCREMENT not null , age text not null , weight text not null , height text not null, sex text not null,
FOREIGN KEY (id2) REFERENCES contacts(id),
FOREIGN KEY (id2) REFERENCES calendar(id5))
> OK
> Time: 0.074s
SELECT * FROM Uinfo
> OK
> Time: 0s
INSERT INTO Uinfo VALUES(null,10,5.6,70,'Female')
> FOREIGN KEY constraint failed
> Time: 0s
Таким образом, было добавлено ограничение внешнего ключа.
Выполнение эквивалента в тестируемом приложении для Android было предпринято с использованием кода:-
public class MainActivity extends AppCompatActivity {
DatabaseHelper DBHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DBHelper = new DatabaseHelper(this);
SQLiteDatabase db = DBHelper.getWritableDatabase();
db.execSQL("INSERT INTO Uinfo VALUES(null,10,5.6,70,'Female')");
Log.d("DBINFO","After the first insert");
db.execSQL("PRAGMA foreign_keys = true");
db.execSQL("INSERT INTO Uinfo VALUES(null,10,5.6,70,'Female')");
Log.d("DBINFO","After the second insert");
}
}
результаты в :-
2021-04-24 15:34:27.954 5644-5644/a.a.so67224090javafknotshowing D/DBINFO: After the first insert
2021-04-24 15:34:27.955 5644-5644/a.a.so67224090javafknotshowing D/AndroidRuntime: Shutting down VM
2021-04-24 15:34:27.957 5644-5644/a.a.so67224090javafknotshowing E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so67224090javafknotshowing, PID: 5644
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so67224090javafknotshowing/a.a.so67224090javafknotshowing.MainActivity}: android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:748)
at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1770)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1698)
at a.a.so67224090javafknotshowing.MainActivity.onCreate(MainActivity.java:20)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Приложение вышло из строя с исключением, сообщение об исключении FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
НО журнал также содержит D/DBINFO: After the first insert
.
т. е. первая ВСТАВКА не вызвала исключения, она была второй после ВАЖНОЙ db.execSQL("PRAGMA foreign_keys = true");
Без этой строки (или эквивалента) по умолчанию в Android (как и в SQLite по умолчанию) обработка внешних ключей отключена. Вышесказанное включает обработку внешнего ключа.
Так что, если ваша проблема в том, что внешние ключи ничего не делают, то это может быть причиной.