我已经为我的DatabaseUtilities类编写了一个addContacts方法,供我的Messenger应用中使用。相关的数据库表为帐户,联系人和帐户联系人。我在尝试3NF时添加了后者。通过较低的JUnit测试工具运行代码时,我收到此错误:
添加联系人失败:[SQLITE_CONSTRAINT_PRIMARYKEY]主键约束失败(唯一约束失败:accountContact.uid,accountContact.cid)
我已经尝试将负责停止自动提交的代码注释掉,这是因为认为提交不正常可能是问题所在,但这不是问题。我知道该SQL在直接在sqlite数据库中使用时会起作用,先将联系人一个接一个地添加,然后再添加accountContact记录。
以下代码是我的addContacts方法:
boolean addContacts(List<Contact> contacts, Account account) {
try {
try {
conn.setAutoCommit(false);
queryInsertContact.clearParameters();
queryInsertAccountContact.clearParameters();
for (Contact contact : contacts) {
if (ipv4Pattern.matcher(contact.getIpv4()).matches() && contact.getAlias().length() <= 256 &&
contact.getTlsPort() >= 1024 && contact.getTlsPort() <= 65535) {
queryInsertContact.setString(1, contact.getCid());
queryInsertContact.setString(2, contact.getAlias());
queryInsertContact.setString(3, contact.getIpv4());
queryInsertContact.setInt(4, contact.getTlsPort());
queryInsertContact.addBatch();
queryInsertAccountContact.setInt(1, account.getUid());
queryInsertAccountContact.setString(2, contact.getCid());
queryInsertAccountContact.addBatch();
} else {
throw new SQLException("Format incorrect");
}
}
queryInsertContact.executeBatch();
queryInsertAccountContact.executeBatch(); // THE TEST HARNESS FAILS HERE
conn.commit();
return true;
} catch (SQLException e) {
System.out.println("failed to add contacts: " + e.getMessage());
conn.rollback();
} finally {
conn.setAutoCommit(true);
}
} catch (SQLException e) {
}
return false;
}
以下代码是我的addContacts测试工具:
@org.junit.Test
public void addContacts() {
List<Contact> contacts = new ArrayList<>();
try {
byte[] salt = getSalt(), hashWithSalt = getKey(salt, "password");
String stringSalt = Base64.getEncoder().encodeToString(salt);
String tempHash = Base64.getEncoder().encodeToString(hashWithSalt);
Account account = new Account("james", tempHash, stringSalt, 100000);
Contact contact = new Contact("abc", "accountboi", "127.0.0.1", 1025);
databaseUtilities.addAccount(account, contact);
try { // THIS CODE JUST GETS THE ACCOUNT INSTANCE WITH ITS UID DEFAULT WAS -1
account = databaseUtilities.getAccount(account.getUsername());
}catch (SQLException e){}
contacts.add(new Contact("f1dg13d7f8sfd", "nrmad", "127.0.0.1", 1025));
assertTrue(databaseUtilities.addContacts(contacts, account));
contacts.clear();
contacts.add(new Contact("f1dg13d7f8sfd", "nrmad", "127.0.0.1", 1025));
assertFalse(databaseUtilities.addContacts(contacts, account));
contacts.clear();
contacts.add(new Contact("f1dg13d7f8sfd1", "nrmad", "1234-0-0-1", 1025));
assertFalse(databaseUtilities.addContacts(contacts, account));
contacts.clear();
contacts.add(new Contact("f1dg13d7f8sfd2", "nrmad", "127-a-a-a", 1));
assertFalse(databaseUtilities.addContacts(contacts, account));
contacts.clear();
contacts.add(new Contact("f1dg13d7f8sfd3", "nrmad", "127.0.0.0", 65536));
assertFalse(databaseUtilities.addContacts(contacts, account));
contacts.clear();
contacts.add( new Contact("f1dg13d7f8sfd4", "nrmad", "127-a-a-a", -1));
assertFalse(databaseUtilities.addContacts(contacts, account));
// UP TO HERE EVERYTHING IS WORKING. THE FOLLOWING CODE IS WHERE THE ERROR IS THROWN
contacts.clear();
contacts.add(new Contact("a1", "nrmad", "127.0.0.1", 1025));
contacts.add(new Contact("a2", "nrmad", "127.0.0.1", 1025));
contacts.add(new Contact("a3", "nrmad", "127.0.0.1", 1025));
assertTrue(databaseUtilities.addContacts(contacts, account)); // THIS STATEMENT
}catch(NoSuchAlgorithmException |InvalidKeySpecException e){
System.out.println(e.getMessage());
}
}
以下SQL语句分别是accounts,contacts和accountContact表的转储:
CREATE TABLE contacts(cid TEXT PRIMARY KEY, alias TEXT NOT NULL, ipv4 TEXT NOT NULL, tlsport INTEGER NOT NULL);
CREATE TABLE accounts(uid INTEGER PRIMARY KEY, username TEXT NOT NULL, pass TEXT NOT NULL, salt TEXT NOT NULL, iterations INTEGER NOT NULL);
CREATE TABLE accountContact( uid INTEGER, cid TEXT, PRIMARY KEY(uid, cid), FOREIGN KEY (uid) REFERENCES accounts(uid),FOREIGN KEY (cid) REFERENCES contacts(cid));
这些SQL语句用于初始化以上代码中使用的准备好的语句
private static final String INSERT_CONTACT = "INSERT INTO contacts(cid, alias, ipv4, tlsport) VALUES(?,?,?,?)";
private static final String INSERT_ACCOUNT = "INSERT INTO accounts(uid, username, pass, salt, iterations) VALUES(?,?,?,?,?)";
private static final String INSERT_ACCOUNTCONTACT = "INSERT INTO accountContact(uid, cid) VALUES(?,?)";
这使我感到困惑了一段时间,我们将不胜感激。
谢谢
[我当时使用PreparedStatement的clearParameters()方法,认为它将在每次测试addContacts()方法之后重置对象,但是有一个单独的方法可以对批处理clearBatch()进行此操作