如何通过 Java 在 SQLite 中强制执行外键约束?

Posted

技术标签:

【中文标题】如何通过 Java 在 SQLite 中强制执行外键约束?【英文标题】:How do you enforce foreign key constraints in SQLite through Java? 【发布时间】:2012-04-04 05:15:15 【问题描述】:

默认情况下,SQLite 似乎不强制执行外键。我正在使用sqlitejdbc-v056.jar,并且我读到使用PRAGMA foreign_keys = ON; 将打开外键约束,并且需要在每个连接的基础上打开它。

我的问题是:我需要执行哪些 Java 语句才能打开此命令?我试过了:

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

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

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

但这些都不起作用。我在这里有什么遗漏吗?

我见过this answer,我想做完全相同的事情,只使用 JDBC。

【问题讨论】:

您使用的是什么版本的 SQLite? (外键约束在 3.6.19 中引入。) 3.6.14.2,显然。甚至没有意识到。 【参考方案1】:

这样的代码:

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

不起作用。 从 DriverManager 调用 getConnection 时,您必须创建 org.sqlite.SQLiteConfig 并将其设置为属性。

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。

【讨论】:

这就像一个魅力!这又是应该被接受为答案的答案之一。 谢谢!很好的答案。【参考方案2】:

当您查看 SQLite Foreign Key Support page 时,我会解释

    SQLlite 必须在编译时支持外键 您仍然需要为每次与 PRAGMA 的连接打开它 创建表时必须将外键定义为约束

广告 1) 引用自here:

如果命令“PRAGMA foreign_keys”没有返回数据,而是返回包含“0”或“1”的单行,那么您使用的 SQLite 版本不支持外键(或者是因为 它比3.6.19 或因为它是使用 SQLITE_OMIT_FOREIGN_KEY 或 SQLITE_OMIT_TRIGGER 定义编译的)。

PRAGMA foreign_keys; 的结果是什么?

更新:从您的评论中我看到您使用的是 3.6.14.2,这意味着您的版本不支持外键约束!因此,如果可能的话,您必须更新 SQLite!

Ad 2) 您的第一个代码 sn-p 执行 PRAGMA as 语句,我认为这行不通。根据您的评论,第三个片段不起作用:SQLite 驱动程序将整个字符串解释为数据库的位置,而不是将“外键=true”部分作为连接设置”。所以只有第二个 sn-p 可以工作。

Ad 3) 您是否创建了支持外键的表?引用自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)
);

【讨论】:

是的,该表是使用外键支持创建的。我应该很快注意到第三种方法根本不起作用,SQLite 驱动程序将整个字符串解释为数据库的位置,而不是将“外键=true”部分作为连接设置。 第二种 OP 的每个连接都具有属性的方式有效,但它应该只是 .setProperty("foreign_keys", "true") - 没有编译指示词。我刚刚在SQLiteConfig 来源中找到了它,它有一个包含所有编译指示及其属性值的枚举(那里还有更多)。也许它和以前不同,但这是最重要的答案之一,所以在这里【参考方案3】:

这个页面在翻译到 Clojure 时很有帮助,但是我的解决方案有所不同。因此,对于后代,即使 OP 要求使用 Java,我在 Clojure 中也是这样做的:

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

【讨论】:

谢谢!!!这一定是魔法发生的地方github.com/clojure/java.jdbc/blob/master/src/main/clojure/… 太棒了。这是我在看一个 Java 问题——但我真的很想知道 Clojure 的答案。感谢发帖! (虽然我不确定它是否适合我——但至少这给了我一个起点)【参考方案4】:

很遗憾,我无法评论之前发帖者的答案,但作为对可能来到这里的其他人的提醒,第一个代码 sn-p:

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

当您的 SQLite 版本是最新版本并支持外键支持时,绝对可以工作。

【讨论】:

【参考方案5】:

试试

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

根据您链接的问题,它看起来是一个可能的候选人。

【讨论】:

对不起,应该提到我已经尝试过了,但也没有用。【参考方案6】:

在 linux 桌面上,当我尝试时,

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

sqlite3 (3.7.13) 认为我的数据库 file/path/to/test.db;foreign keys=true。这导致了奇怪但我认为是适当的错误:table does not exist

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

我认为我已经通过将外键语句填充到如下属性中来解决这两个问题:

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

但即使数据库名称问题得到解决,SQLite 仍然允许违反约束。使用 Xerial 的驱动程序进行更多操作,这就是最终起作用的方法:

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

【讨论】:

【参考方案7】:

我使用 mybatis,在 mybatis-config.xml 中:<property name="url" value="jdbc:sqlite:example.db?foreign_keys=on;"/> 这适用于 mybatis 框架。

【讨论】:

【参考方案8】:

我在谷歌搜索同样的问题时发现了这个问题。我使用的是 Zentus 中的 sqlitejdbc-v056.jar,它使用的是旧版本的 SQLite 驱动程序,并且不支持外键。

我尝试了来自Maven Repository 的Xerial 驱动程序v3.7.2

我没有研究这些驱动程序之间的区别,但是两者之间的热切换并没有破坏任何东西并解决了我的问题,所以,我希望这对某人有所帮助。

【讨论】:

以上是关于如何通过 Java 在 SQLite 中强制执行外键约束?的主要内容,如果未能解决你的问题,请参考以下文章

在 SQLite 中启用外键约束

未强制执行外键

使用clojure.java.jdbc在Clojure中使用外键约束

如何在连接表上的两个外键之间强制实施数据库约束?

sqlite中如何通过外键关联两个数据库?

运用sqlite数据库在查询一张表的具体信息时,要通过外键从另外一张表查询分类信息,出现异常。