Primary key violation error code using ODBC with MS Access - java

I am using Java with Microsoft Access through an ODBC driver. When I insert a duplicate entry for primary key it gives me an error: java.sql.SQLException: General error. I want to show a message to the user that this record already exists, but I think that this exception can be thrown by ODBC in some other cases also. So I found that there are error codes against each message (ref), but I found no error code for primary key violation. Can anyone tell me what error code is for primary key violation for ODBC with MS Access?
Here is basic code
String qry = "INSERT INTO customers VALUES ('" + txtReg.getText()
+ "' ,'" + txtName.getText() + "', '" + txtCity.getText() + "' ,'" + txtCell.getText() + "')";
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:MyDB");
Statement st = con.createStatement();
st.executeQuery(qry);
st.close();
con.close();
} catch (ClassNotFoundException ex) {
JOptionPane.showMessageDialog(this, "Error: " + ex, "Error!", JOptionPane.ERROR_MESSAGE);
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error: " + ex, "Error!", JOptionPane.ERROR_MESSAGE);
}
These txtName and so on are JTextFields. Here is complete Stack trace
connected
st created
Error code: 0
SQLState: S1000
Messsage: General error
java.sql.SQLException: General error
at sun.jdbc.odbc.JdbcOdbc.createSQLException(JdbcOdbc.java:6986)
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7114)
at sun.jdbc.odbc.JdbcOdbc.SQLExecDirect(JdbcOdbc.java:3110)
at sun.jdbc.odbc.JdbcOdbcStatement.execute(JdbcOdbcStatement.java:338)
at sun.jdbc.odbc.JdbcOdbcStatement.executeUpdate(JdbcOdbcStatement.java:288)
at gui.InsertFileCode.btnInsertActionPerformed(insertFileCode.java:399)

The problem is in this line st.executeQuery(qry);
executeQuery(query) method is used mostly for SELECT statement and it returns the in form of ResultSet object.
Sence the statement is INSERT statement you have to use executeUpdate(query) , this method is generally used by INSERT, UPDATE, And DELETE statements. When table updated successfully then it returns 1.
For example
int result = st.executeUpdate(qry);
System.out.println(result);
UPDATE:
Due to the comments, I've fegured out you have another problem rather than the SQL statement. You must beware of when using java to Ms Access, you're actually connecting to a middleware server, so that, you must expect un-expected exception(s) while running the SQL statement, for example:
CRATE TABLE FOO (ID varchar (50) NOT NULL , NAME varchar (255) DEFAULT NULL)
This query runs on SQLite and MySQL (maybe SQL Server too as I didn't test it), gives Syntex error on Access, as DEFAULT NULL should be removed for running the statement.A nd maybe there are many other problem you have to prepare facing it with Access "database" file.
So, I am telling you to leave it, MS Access is suitable for its users, not for us as a programmer, we have to find best-general way because we must consider that some user uses this application that don't know anything neither about Programming Language nor Database.
Then, what should I do?
I am not an expert in database, but take my advice :
If your application need to share its database: MySQL, Oracle and SQL Server used for that purpose.
If your application is used only for some purposes and not need to share its records to other users, use an actually serverless database engine such as SQLite. This seems to be the best option for you as it's a file like Access, only needs an external driver for Java, see this.
I think there is a FireFox extension for designing the SQLite database if you search on google maybe you find it.

Related

SQLite - no errors, however, no data uploaded to DB

I have developed a mobile application using the CodeName One plugin for Java in the Netbeans IDE.
CodeName One uses the Database API. https://www.codenameone.com/javadoc/com/codename1/db/Database.html
I'm running some tests (there are around 10 values I would like to upload, however, just testing the connection ect by uploading ID, Fname and Lname values.
Database db = null;
Cursor cur = null;
String Fname = findTxtFirstn(c).getText();
String Lname = findTxtLastn(c).getText();
try{
Database ARdb = Display.getInstance().openOrCreate("RecordsDB.db");
System.out.println("Connection secured to database.");
ARdb.beginTransaction();
String createTable = "CREATE TABLE IF NOT EXISTS RecordsTable ("
+ "ID integer PRIMARY KEY,"
+ "First_Name text NOT NULL,"
+ "Last_Name text NOT NULL)";
String query = "insert into RecordsTable (ID,First_Name,Last_Name) values (3,'Test','Testerton')";
ARdb.execute(createTable);
ARdb.execute(query);
ARdb.commitTransaction();
} catch(Exception e){
System.out.println("Error! Connection Failed to DB" +e.getMessage());
} finally {
Util.cleanup (db);
Util.cleanup(cur);
}
I get no errors and everything runs, however, the values are not in the database when I check it. Am I missing something here? I have followed tutorials and looked over the Codename One API. I can't find the solution.
Edit: I need to change the value of the primary number each run (else I get an error: number needs to be unique), This tells me the values are being stored on the database, unfortunately, when I check the database in question there are no records on it, so where it the data going?
I used DB Browser for SQLite.
Have you tried performing your modifying change in a transaction? In databases with transactions all modifying operations (CREATE, INSERT, ...) need to be performed in a transaction.
ARdb.beginTransaction();
// your create code
ARdb.commitTransaction();

JDBC Instrumentation and ORA-01000: maximum open cursors exceeded

I am trying to better instrument which web applications make use of Oracle (11g) connections in our Tomcat JDBC connection pool when a connection is created and closed; this way, we can see what applications are using connections by monitoring the V$SESSION table. This is working, but since adding this "instrumentation" I am seeing ORA-01000: maximum open cursors exceeded errors being logged and noticing some connections being dropped out of the pool during load testing (which is probably fine as I have testOnBorrow enabled, so I'm assuming the connection is being flagged as invalid and dropped from the pool).
I have spent the better part of the week scouring the internet for possible answers. Here is what I have tried (all result in the open cursors error after a period of time)...
The below methods are all called the same way...
On Create
We obtain a connection from the pool
We call a method that executes the below code, passing in the context name of the web application
On Close
We have the connection being closed (returned to the pool)
Before we issue close() on the connection, we call a method that executes the code below, passing in "Idle" as the name to store in V$SESSION
Method 1:
CallableStatement cs = connection.prepareCall("{call DBMS_APPLICATION_INFO.SET_MODULE(?,?)}");
try {
cs.setString(1, appId);
cs.setNull(2, Types.VARCHAR);
cs.execute();
log.trace(">>> Executed Oracle DBMS_APPLICATION_INFO.SET_MODULE with module_name of '" + appId + "'");
} catch (SQLException sqle) {
log.error("Error trying to call DBMS_APPLICATION_INFO.SET_MODULE('" + appId + "')", sqle);
} finally {
cs.close();
}
Method 2:
I upgraded to the 12c OJDBC driver (ojdbc7) and used the native setClientInfo method on the connection...
// requires ojdbc7.jar and oraclepki.jar to work (setEndToEndMetrics is deprecated in ojdbc7)
connection.setClientInfo("OCSID.CLIENTID", appId);
Method 3:
I'm currently using this method.
String[] app_instrumentation = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
app_instrumentation[OracleConnection.END_TO_END_CLIENTID_INDEX] = appId;
connection.unwrap(OracleConnection.class).setEndToEndMetrics(app_instrumentation, (short)0);
// in order for this to be sent, a query needs to be sent to the database - this works fine when a
// connection is created, but when it is closed, we need a little something to get the change into the db
// try using isValid()
connection.isValid(1);
Method 4:
String[] app_instrumentation = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
app_instrumentation[OracleConnection.END_TO_END_CLIENTID_INDEX] = appId;
connection.unwrap(OracleConnection.class).setEndToEndMetrics(app_instrumentation, (short)0);
// in order for this to be sent, a query needs to be sent to the database - this works fine when a
// connection is created, but when it is closed, we need a little something to get the change into the db
if ("Idle".equalsIgnoreCase(appId)) {
Statement stmt = null;
ResultSet rs = null;
try {
stmt = connection.createStatement();
rs = stmt.executeQuery("select 1 from dual");
} finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
}
}
When I query for open cursors, I notice the following SQL being returned on the account being used in the pool (for each connection in the pool)...
select NULL NAME, -1 MAX_LEN, NULL DEFAULT_VALUE, NULL DESCR
This does not explicitly exist anywhere in our code, so I can only assume it is coming from the pool when running the validation query (select 1 from dual) or from the setEndToEndMetrics method (or from the DBMS_APPLICATION_INFO.SET_MODULE proc, or from the isValid() call). I tried to be explicit in creating and closing Statement (CallableStatement) and ResultSet objects in methods 1 and 4, but they made no difference.
I don't want to increase the number of allowed cursors, as this will only delay the inevitable (and we have never had this issue until I added in the "instrumentation").
I've read through the excellent post here (java.sql.SQLException: - ORA-01000: maximum open cursors exceeded), but I must still be missing something. Any help would be greatly appreciated.
So Mr. Poole's statement: "that query looks like it's getting fake metadata" set off a bell in my head.
I started to wonder if it was some unknown remnant of the validation query being run on the testOnBorrow attribute of the pool's datasource (even though the validation query is defined as select 1 from dual). I removed this from the configuration, but it had no effect.
I then tried removing the code that sets the client info in V$SESSION (Method 3 above); Oracle continued to show that unusual query and after only a few minutes, the session would hit the maximum open cursors limit.
I then found that there was a "logging" method in our DAO class that logged some metadata from the connection object (values for settings like current auto commit, current transaction isolation level, JDBC driver version, etc.). Within this logging was the use of the getClientInfoProperties() method on the DatabaseMetaData object. When I looked at the JavaDocs for this method, it became crystal clear where that unusual query was coming from; check it out...
ResultSet java.sql.DatabaseMetaData.getClientInfoProperties() throws SQLException
Retrieves a list of the client info properties that the driver supports. The result set contains the following columns
1. NAME String=> The name of the client info property
2. MAX_LEN int=> The maximum length of the value for the property
3. DEFAULT_VALUE String=> The default value of the property
4. DESCRIPTION String=> A description of the property. This will typically contain information as to where this property is stored in the database.
The ResultSet is sorted by the NAME column
Returns:
A ResultSet object; each row is a supported client info property
You can clearly see that unusual query (select NULL NAME, -1 MAX_LEN, NULL DEFAULT_VALUE, NULL DESCR) matches what the JavaDocs say about the DatabaseMetaData.getClientInfoProperties() method. Wow, right!?
This is the code that was performing the function. As best as I can tell, it looks correct from a "closing of the ResultSet" standpoint - not sure what was happening that would keep the ResultSet open - it clearly being closed in the finally block.
log.debug(">>>>>> DatabaseMetaData Client Info Properties (jdbc driver)...");
ResultSet rsDmd = null;
try {
boolean hasResults = false;
rsDmd = dmd.getClientInfoProperties();
while (rsDmd.next()) {
hasResults = true;
log.debug(">>>>>>>>> NAME = '" + rsDmd.getString("NAME") + "'; DEFAULT_VALUE = '" + rsDmd.getString("DEFAULT_VALUE") + "'; DESCRIPTION = '" + rsDmd.getString("DESCRIPTION") + "'");
}
if (!hasResults) {
log.debug(">>>>>>>>> DatabaseMetaData Client Info Properties was empty (nothing returned by jdbc driver)");
}
} catch (SQLException sqleDmd) {
log.warn("DatabaseMetaData Client Info Properties (jdbc driver) not supported or no access to system tables under current id");
} finally {
if (rsDmd != null) {
rsDmd.close();
}
}
Looking at the logs, when an Oracle connection was used, the >>>>>>>>> DatabaseMetaData Client Info Properties was empty (nothing returned by jdbc driver) line was logged, so an exception wasn't being thrown, but no record was being returned either. I can only assume that the ojdbc6 (11.2.0.x.x) driver doesn't properly support the getClientInfoProperties() method - it is weird (I think) that an exception wasn't being thrown, as the query itself is missing the FROM keyword (it won't run when executed in TOAD for example). And no matter what, the ResultSet should have at least been getting closed (the connection itself would still be in use though - maybe this causes Oracle to not release the cursors even though the ResultSet was closed).
So all of the work I was doing was in a branch (I mentioned in a comment to my original question that I was working in trunk - my mistake - I was in a branch that was already created thinking it was based on trunk code and not modified - I failed to do my due diligence here), so I checked the SVN commit history and found that this additional logging functionality was added by a fellow teammate a couple of weeks ago (fortunately it hasn't been promoted to trunk or to higher environments - note this code works fine against our Sybase database). My update from the SVN branch brought in his code, but I never really paid attention to what got updated (my bad). I spoke with him about what this code was doing against Oracle, and we agreed to remove the code from the logging method. We also put in place a check to only log the connection metadata when in our development environment (he said he added this code to help troubleshoot some driver version and auto commit questions he had). Once this was done, I was able to run my load tests without any open cursor issues (success!!!).
Anyway, I wanted to answer this question because when I searched for select NULL NAME, -1 MAX_LEN, NULL DEFAULT_VALUE, NULL DESCR and ORA-01000 open cursors no credible hits were returned (the majority of the hits returned were to make sure you are closing your connection resources, i.e., ResultSets, Statements, etc.). I think this shows it was the database metadata query through JDBC against Oracle was the culprit of the ORA-01000 error. I hope this is useful to others. Thanks.

How to suppress JDBC SQLWarnings?

I'm trying to convert a MySQL stored procedure to java code. It's pretty simple SQL. It creates three temporary tables and then does a select based on them. However, I'm having trouble with SQLWarnings related to Data Truncation being thrown while creating the last temp table.
I'm not looking for an explanation of the warnings themselves. I was getting the same warnings during execution of the stored procedure (due to improperly formatted datetime fields), only there it didn't cause me any problems. In the java version, after Statement.executeUpdate() throws an SQLWarning, the temp table I was attempting to create doesn't exist.
How can I suppress the SQLWarnings? I'd prefer to be able to log a message noting the warning text, but I want my temp table to successfully be created as well.
My code looks something like this:
sqlStatement = "create temporary table mytemptable as (select ...) ;";
try {
stmt.executeUpdate(sqlStatement);
// Throws SQLWarning about Data Truncation
} catch (SQLWarning sqlW) {
logInfo("SQLWarning Caught: " + sqlW.getMessage());
}
stmt.executeQuery("select * from mytemptable ");
// Throws SQLException because mytemptable doesn't exist
To me it's a little strange but the equivalent of this worked:
create temporary table mytemptable ignore select ...;
This way, no exception is thrown by jdbc, but you can still call getWarnings() on the Statement object to get a list of warnings that were ignored.
Thanks Norbert van Nobelen for helping me figure this out in the comments of the OP.

Is my JDBC error caused by my SQL query?

Currently i'm writing a JDBC application to manage a MySQL database. I have the delete, insert and select methods functioning with the correct queries. I'm having trouble with the Update method. When using using the following code I receive a MySQL error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near "",Street",Town",City",PostCode",Age",email",RunningFee'false'Where PID=" at line 1...
private void updateData()
{
Connection con;
try
{
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(
"jdbc:mysql://localhost/snr","root","");
String sql = "Update participant Set password='"+txtpassword.getText()+"'," +
"lastName='"+txtlastName.getText()+"',firstName='"+
txtfirstName.getText()+"',HouseNumber'"+txtHouseNumber.getText()+"',Street'"+txtStreet.getText()+"',Town'"+txtTown.getText()+"',City'"+txtCity.getText()+"',PostCode'"+txtPostCode.getText()+"',Age'"+txtAge.getText()+"',email'"+txtemail.getText()+"',RunningFee'"+cbRunningFee.isSelected()+"' Where PID='"+txtPID.getText()+"'";
Statement statement = con.createStatement();
statement.execute(sql);
createMessageBox("Updated Successfully");
clearControls();
}
catch(Exception e)
{
createMessageBox(e.getMessage());
}
}
Is there something wrong with my SQL query?
Yes, your query is wrong. You're missing = on a great big bunch of set column/value pairs.
(And please consider using prepared statements and bind variables, SQL injection is just not something you want to be open to.)
Yes there is something wrong with the query. Your way of building query is vulnerable to SQL Injection. Use Parameterized Queries instead of concatenating text like that.
Read this article: Preventing SQL Injection in Java
Not only is your query incorrect, but it may also open you to SQL Interjection Attacks.
You need to parameterize your query by replacing the pasted-in values with question marks, preparing the statement, and executing it. See the tutorial that I linked.
Finally, storing a password as plain text is a very, very bad idea.
String sql = "UPDATE participant SET "+
"password=?, lastName=?, firstName=?, HouseNumber=?, Street=?, Town=?, "+
"City=?,PostCode?,Age=?,email=?,RunningFee=? "+
"WHERE PID=?";
PreparedStatement upd = con.prepareStatement(sql);
upd.setString(1, txtpassword.getText());
upd.setString(2, txtlastName.getText());
// ... and so on
upd.executeUpdate();
con.commit();
You are forgetting some = in your query.
Try
String sql = "Update participant Set password='"+txtpassword.getText()+"'," +
"lastName='"+txtlastName.getText()+"',firstName='"+
txtfirstName.getText()+"',HouseNumber='"+txtHouseNumber.getText()+"',Street='"+
txtStreet.getText()+"',Town='"+txtTown.getText()+"',City='"+txtCity.getText()+
"',PostCode='"+txtPostCode.getText()+"',Age='"+txtAge.getText()+"',email='"+
txtemail.getText()+"',RunningFee='"+cbRunningFee.isSelected()+
"' Where PID='"+txtPID.getText()+"'";
The error 'you have an error in your SQL syntax' is from the sql server and indicates that yes, you do have an error in your query. In these cases I often find it useful to print the constructed query itself, just to check that it is being constructed correctly.
In your case I believe the problem is that you are missing a bunch of "="s, you also probably need to escape your single quotes in the java so they are passed through correctly (replace ' with \').

How to add records to databse via sql in Java

I am working a Airsoft application.
I'm trying to add records to a MS Access Database via SQL in Java. I have established a link to the database, with the following:
try
{
//String Driver = "sun.java.odbc.JdbcOdbcDriver";
Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
Connection conn = DriverManager.getConnection("jdbc:ucanaccess://" + URL,"","");
Statement stmt = conn.createStatement();
System.out.println("Connection Established!");
ResultSet rs = stmt.executeQuery("SELECT * FROM AirsoftGunRentals");
tblRent.setModel(DbUtils.resultSetToTableModel(rs));
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, "Error");
}
I am using Ucanaccess to access my MS database. It is reading the database and is displaying to a JTable. However, I need to create three JButtons to add, delete and update the table. I have tried to code the add button, and I have tried to add a record, but it crashes and gives me errors.
try
{
//String Driver = "sun.java.odbc.JdbcOdbcDriver";
Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
Connection conn = DriverManager.getConnection("jdbc:ucanaccess://" + URL,"","");
Statement stmt = conn.createStatement();
System.out.println("Connection Established!");
String Query= "INSERT INTO AirsoftGunRentals(NameOfGun, Brand, TypeOfGuns, NumberOfMagazines,Extras,NumberAvailable,UnitRent)"+
"VALUES('"+pName+"','"+pBrand+"','"+pTypeOfGun+"','"+pNumMags+"','"+pExtras+"','"+pNumberAvail+"','"+pRent+"');";
ResultSet rs = stmt.executeQuery(Query);
JOptionPane.showMessageDialog(null, "Success!");
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, "Error");
}
I have attempted all three, hoping for a result. But am still getting big errors. The only difference between the buttons is that one adds, one deletes and one updates the table. Other then that, the code is the same, minus variables.
As Brahim mentionned it, you should use stmt.executeUpdate(Query) whenever you update / insert or delete data. Also with this particular query, given your String concatenation (see end of line), there is no space between the ")" and the "VALUES" which probably causes a malformed query.
However, I can see from your code that you are not very experienced with such use-cases, and I'd like to add some pointers before all hell breaks loose in your project :
Use PreparedStatement instead of Statement and replace variables by placeholders to prevent SQL Injection.
The code that you are using here is extremely prone to SQL injection - if any user has any control over any of the variables, this could lead to a full database dump (theft), destruction of data (vandalism), or even in machine takeover if other conditions are met.
A good advice is to never use the Statement class, better be safe than sorry :)
Respect Java Conventions (or be coherent).
In your example you define the String Query, while all the other variables start with lower-case (as in Java Conventions), instead of String query. Overtime, such little mistakes (that won't break a build) will lead to bugs due to mistaking variables with classnames etc :)
Good luck on your road to mastering this wonderful language ! :)
First add a space before the quotation marks like this :
String Query= "INSERT INTO AirsoftGunRentals(NameOfGun, Brand, TypeOfGuns, NumberOfMagazines,Extras,NumberAvailable,UnitRent) "+
" VALUES('"+pName+"','"+pBrand+"','"+pTypeOfGun+"','"+pNumMags+"','"+pExtras+"','"+pNumberAvail+"','"+pRent+"');";
And use stmt.executeUpdate(Query); instead of : stmt.executeQuery(Query);in your insert, update and delete queries. For select queries you can keep it.
I managed to find an answer on how to add, delete and update records to a MS Access DB. This is what I found, after I declared the connection, and the prepped statement. I will try to explain to the best I can. I had to add values individually using this:
(pstmt = Prepped Statement Variable)
pstmt.setWhatever(1,Variable);
And it works fine now. I use the same method to delete and update records.
This is the basic query format:
String SQLInsert = "INSERT INTO Tbl VALUES(NULL,?,?,?,?)";
The NULL in the statement is the autonumber in the table. and .setWhatever() clause replaces the question marks with the data types. Thus manipulating the database.
Thank you everyone for all your contributions. It helped a lot, and made this section a lot more understandable.

Categories

Resources