Unnecessary Foreign Key Constraint Failure - java

So I have a mock database for a conference where I'm creating tables for the authors, papers, reviewers, etc.
The reviewers provide an email which refers to the Program Committee emails. This is the key constraint I put in place. Then I add data to the PC table and then attempt to add data to the reviewer table. This is the error I get:
Exception encountered com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`sampledb`.`review`, CONSTRAINT `review_ibfk_2` FOREIGN KEY (`email`) REFERENCES `pcmember` (`email`))
Here are the other functions:
public int loadPCMember(){
String tablename = "pcmember";
String create = "CREATE TABLE IF NOT EXISTS pcmember(email VARCHAR(100), name VARCHAR(50), PRIMARY KEY (email));";
makeTable(create);
System.out.println("made table pcmember");
//CSV Reader
String[][] content = CSVReader(tablename,2);
for(int i = 0 ; i < content.length; i++){
try{
String query = "INSERT INTO pcmember(email,name) VALUES (?,?)";
PreparedStatement ps2 = net.prepareStatement(query);
ps2.setString(1, content[i][0]);
ps2.setString(2, content[i][1]);
ps2.executeUpdate();
System.out.println((i+1)+ " done");
// Throw exception
}catch (SQLException e){System.out.println("Exception encountered");return 0;}
}
System.out.println("PC Member Done");
return 1;
}
//Load next
public int loadReview(){
String tablename = "review";
String create = "CREATE TABLE IF NOT EXISTS review(reportid INTEGER, sdate DATE, comment VARCHAR(250), recommendation VARCHAR(6), paperid INTEGER NOT NULL, email VARCHAR(100) NOT NULL, PRIMARY KEY(reportid), FOREIGN KEY (paperid) REFERENCES paper(paperid), FOREIGN KEY(email) REFERENCES pcmember(email));";
makeTable(create);
System.out.println("made table review");
//CSV Reader
String[][] content = CSVReader(tablename,6);
for(int i = 0 ; i < content.length; i++){
System.out.println("" + content[i][0] + "\t" +content[i][1] + "\t" + content[i][2] + "\t" +content[i][3] + "\t" +content[i][4] + "\t" +content[i][5]);
try{
//SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy");
//java.util.Date date = sdf.parse(content[i][1]);
//System.out.println(""+date);
//Date newDate = new Date(date.getTime());
//System.out.println(""+newDate);
String query = "INSERT INTO review(reportid,sdate,comment,recommendation,paperid,email) VALUES (?,?,?,?,?,?)";
PreparedStatement ps2 = net.prepareStatement(query);
ps2.setInt(1, Integer.parseInt(content[i][0]));
ps2.setDate(2, java.sql.Date.valueOf(content[i][1]));
ps2.setString(3, content[i][2]);
ps2.setString(4, content[i][3]);
ps2.setInt(5, Integer.parseInt(content[i][4]));
ps2.setString(6, content[i][5]);
ps2.executeUpdate();
System.out.println((i+1)+ " done");
// Throw exception
}catch (SQLException e){System.out.println("Exception encountered "+ e);return 0;
}//catch (ParseException e){System.out.println("Parse Exception encountered "+e);}
}
System.out.println("Review Done");
return 1;
}
I have a decent understanding of the key constraints and I'm pretty spot on with the CSV files having the same exact emails, so what could be causing this error?

I'm a SQL guy, not a Java guy. If I ask or suggest anything that doesn't make sense because of that - you know why. To be clear - based on that error message and the sql you have included it looks like you trying to insert a record into the review table that has an email address that doesn't exist in the pcmember table thus violating the foreign key constraint on the review table. This seems like a data problem with the CVSs you are using. However since you mentioned that you are confident in the data files is it possible that the it's trying to INSERT the rows into the review table before the pcmember INSERT has successfully completed?

Related

Java prepared statement get generated keys do not work in DB2 but work in mySQL?

I have a very simple table that is in both MySQL and DB2 called STUDENT. The columns are: ID (primary key, auto incrementing), FIRST_NAME, LAST_NAME, AGE.
The table is replicated in both databases, so they should be the same syntactically. However, I have spent the entire day trying to figure out why, when I write a simple Java program to insert into the database, the MySQL version returns back the generated key via PreparedStatement.getGeneratedKeys(), while the DB2 version does not return anything.
My code looks like:
String sql = "INSERT INTO STUDENT (FIRST_NAME, LAST_NAME, AGE) VALUES ('Jacob', 'Eldy', 19)"
final Connection connection = getConnection(dataSource.get());
int[] insertedRows = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.addBatch();
insertedRows = ps.executeBatch();
rs = ps.getGeneratedKeys();
while(rs.next()) {
LOGGER.info(rs.getString(1));
}
connection.commit();
} catch (Exception e) {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
close(ps, connection);
}
After committing the connection for both databases DB2 and MySQL, both actually show the row being inserted, and the more I insert, a new row appears with an auto incremented ID, however only the MySQL database has a value in while(rs.next()), the DB2 version just skips over it since it is empty.
Am I doing something wrong? Is this just an incompatibility issue with DB2 and it just does not return the generated value? If so, what would be the best solution for tackling this issue?
UPDATE, adding the two DDLs for DB2 & mySQL:
mySQL DDL:
CREATE TABLE 'STUDENT'
...
`ID` int NOT NULL AUTO_INCREMENT
PRIMARY KEY('ID')
AUTO_INCREMENT=19073
DB2 DDL:
CREATE TABLE STUDENT
(
ID INTEGER DEFAULT IDENTITY GENERATED ALWAYS NOT NULL
PRIMARY KEY (ID)
)
CREATE TABLE STUDENT
(
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
, FIRST_NAME VARCHAR (20)
, LAST_NAME VARCHAR (20)
, AGE SMALLINT
);
The following code based on the Making batch updates in JDBC applications links (this one is for Db2 for LUW) provided by others works as expected with the table definition above:
PreparedStatement ps = con.prepareStatement
(
"INSERT INTO STUDENT (FIRST_NAME, LAST_NAME, AGE) " +
"VALUES (?,?,?)"
, Statement.RETURN_GENERATED_KEYS
);
ps.setString (1, "Jacob");
ps.setString (2, "Eldy");
ps.setShort (3, (short) 19);
ps.addBatch();
ps.setString (1, "Jacob");
ps.setString (2, "Eldy");
ps.setShort (3, (short) 19);
ps.addBatch();
int [] numUpdates = ps.executeBatch();
for (int i=0; i < numUpdates.length; i++)
if (numUpdates[i] == Statement.SUCCESS_NO_INFO)
System.out.println("Execution " + i + ": unknown number of rows updated");
else
System.out.println("Execution " + i + " successful: " + numUpdates[i] + " rows updated");
ResultSet[] resultList = ((com.ibm.db2.jcc.DB2PreparedStatement) ps).getDBGeneratedKeys();
if (resultList.length != 0)
for (int i = 0; i < resultList.length; i++)
{
while (resultList[i].next())
System.out.println("Automatically generated key value = " + resultList[i].getBigDecimal(1));
resultList[i].close();
}
else
System.out.println("Error retrieving automatically generated keys");
Am I doing something wrong? Is this just an incompatibility issue with DB2 and it just does not return the generated value? If so, what would be the best solution for tackling this issue?
Yes, you are doing wrong. It's not incompatibility issue, and it's not an issue. DB2 is different than MySQL. You can't handle both because you have incompatible DDL. Since no records are inserted into DB2 the value of the key is not available.
The solution to the issue is to create a trigger on insert a record to make sure the primary key is inserted into DB. If you are missing a key then select it from the sequence and substitute the value.
Now if identity is generated into DB2 like this
CREATE TABLE STUDENT
(
ID INTEGER DEFAULT IDENTITY GENERATED ALWAYS NOT NULL
PRIMARY KEY (ID)
)
So it will always return getGeneratedKeys().

Java display records from a table based on records in other tables

I'm very new to using databases and SQL in general and I'm having some trouble figuring out a function that will allow me to display records from a table in my jdbc database based on data from other tables in the database. I will illustrate below:
Example of "DEMANDS" table (column headers, "ID" is the primary key):
NAME|ADDRESS|DESTINATION|DATE|TIME|ID
Example of "DRIVERS" table ("REGISTRATION" is the primary key):
USERNAME|PASSWORD|REGISTRATION|NAME
Example of "JOURNEY" table ("JID" is the primary key,"REGISTRATION" is a foreign key)
JID|NAME|ADDRESS|DESTINATION|DISTANCE|REGISTRATION|DATE|TIME|STATUS
Below is the code that I have that is used to display tables on a jsp file:
public String retrieve(String query) throws SQLException {
select(query);
return makeTable(rsToList());//results;
}
private void select(String query){
try {
statement = connection.createStatement();
rs = statement.executeQuery(query);
//statement.close();
}
catch(SQLException e) {
System.out.println("way way"+e);
//results = e.toString();
}
}
private String makeTable(ArrayList list) {
StringBuilder b = new StringBuilder();
String[] row;
b.append("<table border=\"3\">");
for (Object s : list) {
b.append("<tr>");
row = (String[]) s;
for (String row1 : row) {
b.append("<td>");
b.append(row1);
b.append("</td>");
}
b.append("</tr>\n");
} // for
b.append("</table>");
return b.toString();
}//makeHtmlTable
private ArrayList rsToList() throws SQLException {
ArrayList aList = new ArrayList();
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount(); //number of column
String columnName[] = new String[count];
for (int i = 1; i <= count; i++)
{
columnName[i-1] = metaData.getColumnLabel(i);
}
aList.add(columnName);
int cols = rs.getMetaData().getColumnCount();
while (rs.next()) {
String[] s = new String[cols];
for (int i = 1; i <= cols; i++) {
s[i-1] = rs.getString(i);
}
aList.add(s);
} // while
return aList;
} //rsToList
All of this code works fine and if I pass in a query into the "Retrieve" function such as:
String query = "select * from DRIVERS";
It will display all of the records of the "DRIVERS" table.
What I am wanting to do though, is only list drivers from the driver table that are available at the time specified in the demand (meaning their registration is not currently in a record in the journey table at the same time as the demand) If possible, I would also only like to display the "NAME" and "REGISTRATION" columns as oppose to the whole record.
I would really appreciate some help with this as I've searched around for solutions for quite some time and have not been able to work out a function that will achieve the desired outcome.
Cheers,
Creation of tables script:
-- --------------------------------------------------------
--DROP Table Demands;
CREATE TABLE Demands (
Name varchar(20),
Address varchar(60),
Destination varchar(60),
Date date DEFAULT NULL,
Time time DEFAULT NULL,
Status varchar(15) NOT NULL,
id INT primary key
);
-- --------------------------------------------------------
--DROP Table Drivers;
CREATE TABLE Drivers (
username varchar(20),
password varchar(20),
Registration varchar(10),
Name varchar(20),
PRIMARY KEY (Registration)
);
-- --------------------------------------------------------
--DROP Table Journey;
CREATE TABLE Journey (
jid INT primary key
Destination varchar(60),
Distance integer NOT NULL DEFAULT 1,
Registration varchar(10) NOT NULL,
Date date NOT NULL,
Time time DEFAULT NULL
);
The following query may answer your question.
SELECT Drivers.Name, Drivers.Registration
FROM Drivers
LEFT JOIN Journey ON Journey.Registration = Drivers.Registration
LEFT JOIN Demands ON Demands.Date = Journey.Date
WHERE Demands.id IS NULL;
This joins JOURNEY and DRIVER based on the foreign key relation. It then outer-joins DEMANDS and JOURNEY based on an implicit relation that is DATE. Finally we only keep records that fail the outer join condition.
The model has a major flaw though as the relation between DEMANDS and JOURNEY is based on a field of type Date, as far as one can tell by what your provided.

How to manage SQL database using associations and not using keys/ids?

I need some help.
We get an assignment and the teacher required that we use associations and not using keys/ids. I don't really understand the concept, and how I solve it using SQL database. What I had learned in the database course that we use keys/id to link the tables together. A brief deception of the problem, we have a yacht club, in the club, we can register, delete and edit member. For every member, we can register one or more boat. A member can edit and delete a boat. The problem Should be solved by Model-View separation.
I have solved the problem by creating two tables and link the boat table to the member by memberId. The teacher told me it is not correct to use ID(He said: Basically the UI should add Boats to a Member object, not insert boats in a database with a member id). Can you please give me suggestions on how to solve the issue without using Id.
public void createTables() {
Statement tablesSt;
try {
tablesSt = conn.createStatement();
tablesSt.executeUpdate( "CREATE TABLE IF NOT EXISTS "+ Boat +
" (Boat_ID INT PRIMARY KEY," +
" Member_ID INT NOT NULL,"+
" Size INT NOT NULL ,"+
" Type TEXT NOT NULL ,"+
" FOREIGN KEY (Member_ID) REFERENCES " + Member + "(Member_ID) ON DELETE CASCADE) ");
tablesSt.close();
tablesSt = conn.createStatement();
tablesSt.executeUpdate( "CREATE TABLE IF NOT EXISTS "+ Member +
" (Member_ID INT PRIMARY KEY," +
" Member_Name TEXT NOT NULL ," +
" Personal_Number INT UNIQUE )");
tablesSt.close();
A code from boat class
public void newBoat(int memberID, String type, int size ) {
int BoatId= 0;
try {
ResultSet rc= Control.conn.createStatement().executeQuery("SELECT * FROM Boat ORDER BY Boat_ID DESC LIMIT 1");
BoatId =rc.getInt("Boat_ID") +1;
Statement registerBoat = Control.conn.createStatement();
registerBoat.executeUpdate("INSERT OR IGNORE INTO Boat VALUES("+BoatId+" , "+memberID+" , '"+size+"', '"+type+"')");
Control.conn.commit();
Control.conn.setAutoCommit(false);
/* ResultSet rs2 = Control.conn.createStatement().executeQuery(" SELECT * FROM Boat WHERE Boat_ID ="+ BoatId);
System.out.println(rs2.getString("type"));*/ // test code to check if the Boat is Succesfully registered
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Convert PostgreSQL Serial Primary Key to Oracle Statement

I have a java file that connects to a database and this is the code:
package movies;
import java.sql.*;
public class CreateTable {
public static void main(String args[]) {
Connection c = null;
Statement stmt = null;
String sql;
try {
Class.forName("oracle.jdbc.OracleDriver");//driver
c = DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521/XE", "username", "password");//PUT DATABASE CONNECTION INFO
System.out.println("Opened database successfully from within CreateTable.java");
stmt = c.createStatement();
sql = "CREATE TABLE MOVIES "
//+ "(ID SERIAL PRIMARY KEY,"//can't figure out this statement and it works if I comment it out.
+ " (NAME NVARCHAR2(255) PRIMARY KEY,"//I don't want this to be the primary key.
+ " YEAR NVARCHAR2(255),"
+ " RATING NVARCHAR2(16),"
+ " ACTORS NVARCHAR2(1024))";
stmt.executeUpdate(sql);
stmt.close();
c.close();
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
System.out.println("Table created successfully");
}
}
The commented part is where I'm running into problems. I'm trying to convert a PostgreSQL statement to an Oracle 11g XE statement. I would like to have an ID with a sequential number to identify it. (ie. 0001, 0002, 0003, etc.) How can I do this? Could you provide an example? I'm at a loss right now. I'm using Netbeans 8.02, Oracle 11g XE, and Apache Tomcat 8.0.15.0.
edit I'm trying to have the ID column as the only primary key. I would eliminate the NAME column as a primary key and make the ID column the new primary key.
I don't think you can have two separate columns with primary key like that, if you want a primary key on two columns use,
edit :
sql = "CREATE TABLE MOVIES "
+ "(ID INT PRIMARY KEY," -- CHANGES
+ " NAME NVARCHAR2(255),"
+ " YEAR NVARCHAR2(255),"
+ " RATING NVARCHAR2(16),"
+ " ACTORS NVARCHAR2(1024))";
i have created the table to show the syntax works in oracle now..
SQL> create table movies
2 (id int primary key,
3 name NVARCHAR2(255),
4 year NVARCHAR2(255),
5 rating NVARCHAR2(255),
6 actors NVARCHAR2(1024));
Table created.
This code worked. See the comments below to view the changes:
try {
Class.forName("oracle.jdbc.OracleDriver");//driver
c = DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521/XE", "username", "password");//PUT DATABASE CONNECTION INFO
System.out.println("Opened database successfully from within CreateTable.java");
stmt = c.createStatement();
sql = "CREATE TABLE MOVIES "
//THE FOLLOWING LINE WAS CHANGED:
+ "(\"ID\" INTEGER PRIMARY KEY, \"TYPE\" VARCHAR2(32),"
+ " NAME NVARCHAR2(255),"
+ " YEAR NVARCHAR2(255),"
+ " RATING NVARCHAR2(16),"
+ " ACTORS NVARCHAR2(1024))";
//THE FOLLOW TWO STATEMENTS WERE ADDED:
String sql1 = "CREATE SEQUENCE MOVIES_SEQ START WITH 1 INCREMENT BY 1 NOMAXVALUE";
String sql2 = "CREATE TRIGGER MOVIES_TRIGGER BEFORE INSERT ON MOVIES FOR EACH ROW BEGIN SELECT MOVIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; END;";
stmt.executeUpdate(sql);
//THESE TWO WERE ALSO ADDED:
stmt.executeUpdate(sql1);
stmt.executeUpdate(sql2);
stmt.close();
c.close();
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}

JDBC Exception when enter data in the table with foreign keys

When I enter data with my java program (simple dictionary ) it throws an error:
MySQLIntegrityConstraintViolationException: Cannot add or update a
child row: a foreign key constraint fails (singlehaw.card,
CONSTRAINT card_ibfk_1 FOREIGN KEY (wordId) REFERENCES word
(wordId))
But when I enter data through query in command prompt I don't face any problem.
here I post my method:
public boolean insert(Card card) {
Connection connection = MySqlUtils.getInstance().getConnection();
PreparedStatement statement = null;
ResultSet resultSet = null;
int cardId = -1;
try {
String INSERT_INTO_TABLE_CARD_QUERY = "INSERT INTO "
+ TBL_CARD + " ("
+ STATUS + ", "
+ RATING + ", "
+ INSERT_TIME + ", "
+ DIC_ID + ", "
+ WORD_ID
+ ") VALUES (?,?,NOW(),?,?)";
statement = connection.prepareStatement(INSERT_INTO_TABLE_WORDS_QUERY, Statement.RETURN_GENERATED_KEYS);
statement.setString(1, card.getStatus().name());
statement.setInt(2, card.getRating());
statement.setInt(3, card.getDictionaryId());
statement.setInt(4, card.getWordId());
statement.execute();
// get last inserted id
resultSet = statement.getGeneratedKeys();
if (resultSet.next())
cardId = resultSet.getInt(1);
} catch (SQLException e) {
e.printStackTrace();
return false;
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
card.setCardId(cardId);
return true;
}
and also scripts of creating tables:
CREATE TABLE dictionary (
dictionaryId SERIAL,
dictionary VARCHAR(128) NOT NULL,
PRIMARY KEY (dictionaryId)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE word (
wordId SERIAL,
word VARCHAR(255) NOT NULL UNIQUE,
transcription VARCHAR(255),
PRIMARY KEY (wordId)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE card (
cardId SERIAL,
status ENUM ('EDIT', 'POSTPONED', 'TO_LEARN', 'LEARNT') NOT NULL DEFAULT 'TO_LEARN',
rating TINYINT DEFAULT '0' NOT NULL,
insert_time TIMESTAMP DEFAULT NOW(),
update_time TIMESTAMP,
dictionaryId BIGINT UNSIGNED NOT NULL,
wordId BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (cardId),
FOREIGN KEY (wordId) REFERENCES word (wordId),
FOREIGN KEY (dictionaryId) REFERENCES dictionary (dictionaryId) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Perhaps, the fact your wordID fields on the tables have different data types is affecting your program. SERIAL is an alias for Bigint. Idea discarded.
Print somwehere in the logs the actual statement being executed. Maybe there's something that's not being included.
Thnx guys a lot. Understood many things from this topic. Right now the problem has gone. The problem was due to populating tables via JUnit tests and because of maven my tests gone in a wrong order so it was difficult to recognize the real problem.
You can switch off your constraints ,execute your query, and switch constraints on.
SET FOREIGN_KEY_CHECKS=0;
... here is your sql ...
SET FOREIGN_KEY_CHECKS=1;

Categories