24

It appears that SQLite does not enforce foreign keys by default. I'm using sqlitejdbc-v056.jar and I've read that using PRAGMA foreign_keys = ON; will turn on foreign key constraints, and that this needs to be turned on in a per-connection basis.

My question is: what Java statements do I need to execute to turn on this command? I've tried:

connection.createStatement().execute("PRAGMA foreign_keys = ON");

and

Properties properties = new Properties();
properties.setProperty("PRAGMA foreign_keys", "ON");
connection = DriverManager.getConnection("jdbc:sqlite:test.db", properties);

and

connection = DriverManager.getConnection("jdbc:sqlite:test.db;foreign keys=true;");

but none of those work. Is there something I am missing here?

I've seen this answer and I want to do exactly the same thing, only using JDBC.

2
  • 1
    What version of SQLite are you using? (Foreign key constraints were introduced in 3.6.19.)
    – dan04
    Commented Mar 22, 2012 at 23:18
  • 3.6.14.2, apparently. Didn't even realize.
    – Zarjio
    Commented Mar 22, 2012 at 23:25

8 Answers 8

39

Code like this:

DriverManager.getConnection("jdbc:sqlite:some.db;foreign keys=true;")

Does not work. You have to create org.sqlite.SQLiteConfig and set it as properties when call getConnection from DriverManager.

public static final String DB_URL = "jdbc:sqlite:database.db";  
public static final String DRIVER = "org.sqlite.JDBC";  

public static Connection getConnection() throws ClassNotFoundException {  
    Class.forName(DRIVER);  
    Connection connection = null;  
    try {  
        SQLiteConfig config = new SQLiteConfig();  
        config.enforceForeignKeys(true);  
        connection = DriverManager.getConnection(DB_URL,config.toProperties());  
    } catch (SQLException ex) {}  
    return connection;  
}

This code taken from this.

2
  • This works like a charm! It's again one of those answers that should have been accepted as the answer. Commented Mar 31, 2015 at 16:42
  • Thanks! Great answer.
    – user2030052
    Commented Aug 31, 2015 at 8:20
19
+150

When you look at the SQLite Foreign Key Support page I would interpret that

  1. SQLlite has to be compiled with foreign key support
  2. You still have to turn it on for each connection with PRAGMA
  3. You have to define the foreign key as constraint when you create the table

Ad 1) Quoted from here:

If the command "PRAGMA foreign_keys" returns no data instead of a single row containing "0" or "1", then the version of SQLite you are using does not support foreign keys (either because it is older than 3.6.19 or because it was compiled with SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined).

What is your result for PRAGMA foreign_keys;?

Update: from your comment I see you are using 3.6.14.2, this means your version is not supporting foreign key constraints! So you have to update SQLite, if this is possible!

Ad 2) Your first code snippet executes the PRAGMA as statement, I don't think this will work. The third snipped didn't work based on your comment: the SQLite driver interprets the whole string as the location of the database, instead of taking the "foreign keys=true" part as connection settings". So only the second snippet will work.

Ad 3) Did you create the table with foreign key support? Quoted from here:

CREATE TABLE artist(
  artistid    INTEGER PRIMARY KEY, 
  artistname  TEXT
);
CREATE TABLE track(
  trackid     INTEGER, 
  trackname   TEXT, 
  trackartist INTEGER,
  FOREIGN KEY(trackartist) REFERENCES artist(artistid)
);
2
  • Yes, the table was created with foreign key support. And I should quickly note that the third method doesn't work at all, the SQLite driver interprets the whole string as the location of the database, instead of taking the "foreign keys=true" part as connection settings.
    – Zarjio
    Commented Mar 23, 2012 at 14:25
  • The second OP's way with properties for every connection works, but it should be just .setProperty("foreign_keys", "true") - without the pragma word. I just found it in SQLiteConfig sources, that have an enum with all the pragmas and their values for properties (it has many more there). Maybe it as different before, but this is one of the top answers, so here
    – iwat0qs
    Commented Jun 10, 2020 at 23:46
5

I unfortunately can't comment on the previous poster's answer but as a heads up for anybody else who may come by here, the first code snippet:

connection.createStatement().execute("PRAGMA foreign_keys = ON");

absolutely does work when your version of SQLite is up to date and supports foreign key support.

5

this page was helpful when translating to Clojure, however my solution was different. So, for posterity, even though the OP asked for Java, this is how I did it in Clojure:

(def db-spec {:connection-uri "jdbc:sqlite:db.sqlite3?foreign_keys=on;"})

3
2

I use mybatis, in mybatis-config.xml :
<property name="url" value="jdbc:sqlite:example.db?foreign_keys=on;"/>
that's work for mybatis framework.

1

Try

connection = DriverManager.getConnection("jdbc:sqlite:test.db;foreign keys=true;");

Based on the question you linked, it looks to be a likely candidate.

1
  • 2
    Sorry, should've mentioned that I already tried that and it didn't work either.
    – Zarjio
    Commented Mar 19, 2012 at 19:04
1

On a linux desktop, when I tried,

connection = DriverManager.getConnection("jdbc:sqlite:/path/to/test.db;foreign keys=true;");

sqlite3 (3.7.13) thought that my database file was /path/to/test.db;foreign keys=true. This led to the strange, but I guess appropriate, error: table does not exist

See how to solve no such table while inserting exception in sqlite data base

I thought I had fixed both of these issues by stuffing the foreign key statement into a property like so:

private final Properties connectionProperties = new Properties();
connectionProperties.setProperty("PRAGMA foreign_keys", "ON");
private final String connectionString = String.format("jdbc:sqlite:%s", absolute_path_to_sqlite_db);
Connection connection = DriverManager.getConnection(connectionString, connectionProperties);

But even tho the database name issue was resolved, SQLite was still allowing constraint violations. Some more noodling with Xerial's driver and this is what finally worked:

private final Properties connectionProperties = new Properties();
SQLiteConfig config = new SQLiteConfig();
config.enforceForeignKeys(true);
connectionProperties = config.toProperties();
private final String connectionString = String.format("jdbc:sqlite:%s", absolute_path_to_sqlite_db);
Connection connection = DriverManager.getConnection(connectionString, connectionProperties);
0

I found this question while Googling the same problem. I was using sqlitejdbc-v056.jar from Zentus, which uses an older version of the SQLite driver, and doesn't support foreign keys.

I tried what seems to be the Xerial driver v3.7.2 from the Maven Repository

I have not researched the difference between these drivers, but a hot-switch between the two didn't break anything and fixed my issue, so, I hope this helps someone.

Not the answer you're looking for? Browse other questions tagged or ask your own question.