简体   繁体   中英

SQLite - how to use foreign key

I've been trying to get foreign keys working within my Android SQLite database. I have tried the following syntax but the foreign key is empty.

here is my databasehelper:

    public static final String DATABASE_NAME ="popeye.db";
    public static final int DATABASE_VERSION = 1;
    public final Context context;
    public static final String table12 = "users";

    public DatabaseHelper(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

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

    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        String bookletinfo = "CREATE TABLE bookletinfo ( booklet_id INTEGER PRIMARY  KEY AUTOINCREMENT NOT NULL, type VARCHAR NOT NULL, province VARCHAR NOT NULL, citymunicipality NOT NULL, barangay VARCHAR NOT NULL, address VARCHAR NOT NULL, respondent_name VARCHAR NOT NULL, household_head VARCHAR NOT NULL, hh_mem_count INTEGER NOT NULL, created_at DATETIME NOT NULL, updated_at TIMESTAMP)";
        String demographiccharacteristics = "CREATE TABLE demographiccharacteristics (id INTEGER PRIMARY  KEY AUTOINCREMENT NOT NULL, Q1 VARCHAR , Q2 VARCHAR , Q3 VARCHAR , Q4 INTEGER , Q5 DATE , Q6 VARCHAR , Q7 VARCHAR , Q8 VARCHAR , Q9 VARCHAR , Q10 VARCHAR , Q11 VARCHAR , Q12 VARCHAR , Q13 VARCHAR , Q14 VARCHAR , currentbrgy VARCHAR , created_at TIMESTAMP  , updated_at TIMESTAMP, booklet_id INTEGER, FOREIGN KEY(booklet_id) REFERENCES bookletinfo(booklet_id))";
       
        db.execSQL(bookletinfo);
        db.execSQL(demographiccharacteristics);
       }
       
    @Override
    public void onOpen(SQLiteDatabase db){
        super.onOpen(db);
        db.setForeignKeyConstraintsEnabled(true);
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS bookletinfo");
        db.execSQL("DROP TABLE IF EXISTS demographiccharacteristics");
        onCreate(db);
    }

    public boolean addBooklet(String type1,String province,String municipality,String barangay, String address,String nameofrespondent, String householdhead,  String householdtotal, String date,String updated_at) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put("type",type1);
        contentValues.put("province",province);
        contentValues.put("citymunicipality",municipality);
        contentValues.put("barangay",barangay);
        contentValues.put("address",address);
        contentValues.put("respondent_name",nameofrespondent);
        contentValues.put("household_head",householdhead);
        contentValues.put("hh_mem_count", householdtotal);
        contentValues.put("created_at", date);
        contentValues.put("updated_at", updated_at);
        
        db.insert("bookletinfo",null,contentValues);
        return true;}

    public boolean addDemographic(String etQ1,String etQ2,String etQ3,String etQ4,String etQ5,String etQ6,String etQ7,String etQ8,String etQ9,String etQ10,String etQ11,String etQ12,String etQ13,String etQ14, String etbrgy,String created_at,String updated_at) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put("Q1",etQ1);
        contentValues.put("Q2",etQ2);
        contentValues.put("Q3",etQ3);
        contentValues.put("Q4",etQ4);
        contentValues.put("Q5",etQ5);
        contentValues.put("Q6",etQ6);
        contentValues.put("Q7", etQ7);
        contentValues.put("Q8",etQ8);
        contentValues.put("Q9",etQ9);
        contentValues.put("Q10",etQ10);
        contentValues.put("Q11",etQ11);
        contentValues.put("Q12",etQ12);
        contentValues.put("Q13",etQ13);
        contentValues.put("Q14", etQ14);
        contentValues.put("currentbrgy",etbrgy);
        contentValues.put("created_at",created_at);
        contentValues.put("updated_at", updated_at);

        db.insert("demographiccharacteristics",null,contentValues);
        return true;
    }

}

here is my code of taking and saving data to booklet info and it's working fine, the data is saved:

private static final String TAG = "home";
Button save, next, back, button;
EditText etprovince, etmunicipality, etbarangay, ethouseholdhead, etnameofrespondent, ettotalhouseholdmember, etaddress;
DatabaseHelper db;
private TextView mDisplayDate,tvdate2;
private DatePickerDialog.OnDateSetListener mDateSetListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);
    db = new DatabaseHelper(home.this);
    etprovince = findViewById(R.id.province);
    etmunicipality = findViewById(R.id.municipality);
    etbarangay = findViewById(R.id.barangay);
    ethouseholdhead = findViewById(R.id.q2);
    etnameofrespondent = findViewById(R.id.respondentname);
    ettotalhouseholdmember = findViewById(R.id.totalmember);
    etaddress = findViewById(R.id.address);
    tvdate2=findViewById(R.id.q5);

    Spinner type = (Spinner) findViewById(R.id.ettype);
    ArrayAdapter<String> myAdapter = new ArrayAdapter<String>(home.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.type));
    myAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    type.setAdapter(myAdapter);

    save = (Button) findViewById(R.id.save);
    save.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String province = etprovince.getText().toString();
            String municipality = etmunicipality.getText().toString();
            String barangay = etbarangay.getText().toString();
            String householdhead = ethouseholdhead.getText().toString();
            String nameofrespondent = etnameofrespondent.getText().toString();
            String householdtotal = ettotalhouseholdmember.getText().toString();
            String address = etaddress.getText().toString();
            String type1 = type.getSelectedItem().toString();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String date = sdf.format(new Date());

            String updated_at = "";
            if (db.addBooklet(type1,province, municipality, barangay,address,nameofrespondent,householdhead, householdtotal,date,updated_at)) {
                Toast.makeText(getApplicationContext(), "Data Inserted", Toast.LENGTH_SHORT).show();
                Intent registerIntent = new Intent(home.this, Survey2.class);
                startActivity(registerIntent);
            } else {
                Toast.makeText(getApplicationContext(), "error", Toast.LENGTH_SHORT).show();
            }

        }
    });

    back = (Button) findViewById(R.id.back);
    back.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent registerIntent = new Intent(home.this, menu.class);
            startActivity(registerIntent);
        }
    });
}

and here is another adding and saving data and it's working. but my booklet_id is empty everytime i save a data. what i want to do is booklet_idis automatically input and the "booklet_id INTEGER, FOREIGN KEY(booklet_id) REFERENCES bookletinfo(booklet_id)" is not working. i need help

private static final String TAG = "Survey2";
private TextView mDisplayDate;
private DatePickerDialog.OnDateSetListener mDateSetListener;

DatabaseHelper db;
Button next,save;
TextView tvDate;
EditText Q1,Q4,brgy,Q6,Q7,Q9,Q10,Q14;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_survey2);
    db = new DatabaseHelper(Survey2.this);

    brgy =(EditText) findViewById(R.id.brgy);
    
    Q1 = (EditText)findViewById(R.id.q1);
    Q4 = (EditText)findViewById(R.id.q4);
    Q14 = (EditText)findViewById(R.id.q14);
    Q6 = (EditText)findViewById(R.id.q6);
    Q7 = (EditText)findViewById(R.id.q7);
    Q9 = (EditText)findViewById(R.id.q9);
    Q10 = (EditText)findViewById(R.id.q10);
    tvDate=findViewById(R.id.q5);



    //household head spinner
    Spinner relationship = (Spinner) findViewById(R.id.q2);
    ArrayAdapter<String> myAdapter = new ArrayAdapter<String>(Survey2.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.relationship));
    myAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    relationship.setAdapter(myAdapter);
    
    //Gender spinner
    Spinner SEX = (Spinner) findViewById(R.id.q3);
    ArrayAdapter<String> sex = new ArrayAdapter<String>(Survey2.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.SEX));
    sex.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    SEX.setAdapter(sex);

    //marital spinner
    Spinner marital1 = (Spinner) findViewById(R.id.q8);
    ArrayAdapter<String> marital = new ArrayAdapter<String>(Survey2.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.marital));
    marital.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    marital1.setAdapter(marital);

    //education spinner
    Spinner Education = (Spinner) findViewById(R.id.q11);
    ArrayAdapter<String> education = new ArrayAdapter<String>(Survey2.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.education));
    education.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    Education.setAdapter(education);

    //enroled? spinner
    Spinner Enrolled = (Spinner) findViewById(R.id.q12);
    ArrayAdapter<String> enrolled = new ArrayAdapter<String>(Survey2.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.currentEnrolled));
    enrolled.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    Enrolled.setAdapter(enrolled);

    //spinner educ level
    Spinner level = (Spinner) findViewById(R.id.q13);
    ArrayAdapter<String> Level = new ArrayAdapter<String>(Survey2.this,
            android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.level));
    Level.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    level.setAdapter(Level);
    

    mDisplayDate = (TextView)findViewById(R.id.q5);
    mDisplayDate.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Calendar cal2 = Calendar.getInstance();
            int y2=cal2.get(Calendar.YEAR);
            int m2=cal2.get(Calendar.MONTH);
            int d2=cal2.get(Calendar.DAY_OF_MONTH);
            DatePickerDialog dialog2 = new DatePickerDialog(Survey2.this,
                    android.R.style.Theme_Holo_Light_Dialog_MinWidth,mDateSetListener,y2,m2,d2);
            dialog2.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            dialog2.show();
        }
    });
    mDateSetListener =new DatePickerDialog.OnDateSetListener() {
        @Override
        public void onDateSet(DatePicker datePicker, int y2, int m2, int d2) {
            m2 = m2+1;
            Log.d(TAG, "onDateSet: yyy/mm/dd" + y2 + "-" + m2 + "-" + d2 );
            String date2 = y2 + "-" + m2 + "-" +d2;
            mDisplayDate.setText(date2);
        }
    };

   
    
    save = (Button)findViewById(R.id.save);
    save.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String etQ1 = Q1.getText().toString();
            String etQ2 = relationship.getSelectedItem().toString();
            String etQ3 = SEX.getSelectedItem().toString();
            String etQ4 = Q4.getText().toString();
            String etQ5= tvDate.getText().toString();
            String etQ6 = Q6.getText().toString();
            String etQ7 = Q7.getText().toString();
            String etQ8 = marital1.getSelectedItem().toString();
            String etQ9 = Q9.getText().toString();
            String etQ10 =Q10.getText().toString();
            String etQ11 = Education.getSelectedItem().toString();
            String etbrgy = brgy.getText().toString();
            String etQ12 = Enrolled.getSelectedItem().toString();
            String etQ13 = level.getSelectedItem().toString();
            String etQ14 = Q14.getText().toString();
            String updated_at =  "";
            //current date

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String created_at = sdf.format(new Date());
            
            if (db.addDemographic(etQ1,etQ2,etQ3,etQ4,etQ5,etQ6,etQ7,etQ8,etQ9,etQ10,etQ11,etQ12,etQ13,etQ14,etbrgy,created_at,updated_at)) {
                Toast.makeText(getApplicationContext(), "Data Inserted", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "error", Toast.LENGTH_SHORT).show();
            }

        }
    });
    
    
    
}

It appears that you may be think that SQLite automatically associates children with parents when a Foreign Key clause is coded. It does not and can not.

  • can not is because there is no way that when inserting a child, that the parent of the child can be known without a means of indicating the parent. For example say there are parents 1 - 10 which parent should be the parent to child 100? (rhetorical)

What a Foreign Key clause does is add a constraint (a rule) that says that the value of the column in the child MUST be a value that exists in the specified column in one of the rows of the parent table. If this rule is broken then a conflict occurs, thus indicating a breach of referential integrity (aka a parentless child (orphans)).

It should be understood that the Foreign Key clause is not the same as the term Foreign Key used when describing databases. For a foreign key to exists there is no requirement for a Foreign key clause. Rather the Foreign Key clause supports the existence of the foreign key.

You have to determine what can be inserted.

First I would suggest changing methods addBooklet and addDemographic from returning a boolean to return a long and returning the result of the insert.

  • you can always use if (addBooklet(....) > 0) to be true if the row was inserted.

You also need to allow the booklet_id to be provided when inserting a Demographic and for the provideed value to be inserted (added to the ContentValues)

So the methods in the DatabaseHelper could be changed to :-

public /*boolean*/ long addBooklet(String type1,String province,String municipality,String barangay, String address,String nameofrespondent, String householdhead,  String householdtotal, String date,String updated_at) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put("type",type1);
    contentValues.put("province",province);
    contentValues.put("citymunicipality",municipality);
    contentValues.put("barangay",barangay);
    contentValues.put("address",address);
    contentValues.put("respondent_name",nameofrespondent);
    contentValues.put("household_head",householdhead);
    contentValues.put("hh_mem_count", householdtotal);
    contentValues.put("created_at", date);
    contentValues.put("updated_at", updated_at);

    return db.insert("bookletinfo",null,contentValues);
    /*return true;*/}

    public /*boolean*/ long addDemographic(/* ADDED >>>>> */long booklet_id,String etQ1,String etQ2,String etQ3,String etQ4,String etQ5,String etQ6,String etQ7,String etQ8,String etQ9,String etQ10,String etQ11,String etQ12,String etQ13,String etQ14, String etbrgy,String created_at,String updated_at) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put("booklet_id",booklet_id); /*<<<<< ADDED */
    contentValues.put("Q1",etQ1);
    contentValues.put("Q2",etQ2);
    contentValues.put("Q3",etQ3);
    contentValues.put("Q4",etQ4);
    contentValues.put("Q5",etQ5);
    contentValues.put("Q6",etQ6);
    contentValues.put("Q7", etQ7);
    contentValues.put("Q8",etQ8);
    contentValues.put("Q9",etQ9);
    contentValues.put("Q10",etQ10);
    contentValues.put("Q11",etQ11);
    contentValues.put("Q12",etQ12);
    contentValues.put("Q13",etQ13);
    contentValues.put("Q14", etQ14);
    contentValues.put("currentbrgy",etbrgy);
    contentValues.put("created_at",created_at);
    contentValues.put("updated_at", updated_at);

    return db.insert("demographiccharacteristics",null,contentValues);
    /*return true;*/
}
  • Note comments used to indicate the changes made

You now have a means of obtaining the booklet_id when adding a new Booklet to so this can be used when adding a Demographic child.

eg :-

public class MainActivity extends AppCompatActivity {

    DatabaseHelper db;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        db = new DatabaseHelper(this);
        long currentBooklet = db.addBooklet("Type1","Province1","municipality1","barangay1","address1","name1","househol1","1","2021-01-01","2021-01-01");
        if (currentBooklet > 0) {
            db.addDemographic(currentBooklet,"Demo1","Q2","Q3","Q4","Q5","Q6","Q7","Q8","Q9","Q10","Q11","Q12","Q13","Q14","etbrgy","2021-01-01","202-01-01");
            db.addDemographic(currentBooklet,"Demo2","Q2","Q3","Q4","Q5","Q6","Q7","Q8","Q9","Q10","Q11","Q12","Q13","Q14","etbrgy","2021-01-01","202-01-01");
        }
    }
}

This resulting in the following data in the database :-

The bookletinfo table (only partial data displayed) :-

在此处输入图片说明

The demographiccharecteristics table :-

在此处输入图片说明

ie the two rows have been inserted as the was no Foreign Key conflict.

Now if you attempted

db.addDemographic(10,"Demo2","Q2","Q3","Q4","Q5","Q6","Q7","Q8","Q9","Q10","Q11","Q12","Q13","Q14","etbrgy","2021-01-01","202-01-01");

ie 10 is not a value in the booklet_id column of any of the rows (there is only the 1 row and the booklet_id is 1) * then a Foreign Key conflict will occur. However the convenience insert method traps/catches the underlying exception (the insertOrThrow method would not) and returns -1 as no row is inserted.

If you look at the log you can see that exceptions are raised but caught. eg the lone of code above result in the log including :-

2021-10-30 09:19:43.455 22029-22029/a.a.so69769911javasqlitefk E/SQLiteDatabase: Error inserting Q1=Demo2 Q2=Q2 Q3=Q3 Q4=Q4 Q5=Q5 Q6=Q6 Q7=Q7 Q8=Q8 currentbrgy=etbrgy Q9=Q9 created_at=2021-01-01 Q11=Q11 Q10=Q10 Q13=Q13 Q12=Q12 updated_at=202-01-01 Q14=Q14 booklet_id=10
    android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:796)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1564)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1433)
        at a.a.so69769911javasqlitefk.DatabaseHelper.addDemographic(DatabaseHelper.java:120)
        at a.a.so69769911javasqlitefk.MainActivity.onCreate(MainActivity.java:20)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM