There is a select query that I am executing with DB2 JDBC. I am using Prepared Statement to pass in the value for the parameter. The column length of that parameter in the database is 12 so everything works fine until the length of the value is 12 and then it fails. Throws an exception with the error message as in the title. I did some searching and found an explanation in the following link http://www-01.ibm.com/support/docview.wss?uid=swg21319477 and the resolution mentioned in there is as below
Resolving the problem
Add additional client side validation code to prevents queries, with values that are larger than the allowed maximum length to be ran.
I don't want to do this. Why wouldn't the query just return back with no results. Any idea how do I go about this?
EDIT
String sql = "select student_id, student_name from student where student_id = ?";
try (Connection connection = DBUtils.GetConnection)
{
try (PreparedStatement statement = connection.prepareStatement(sql))
{
statement.setString(1, student_id);
ResultSet result = statement.executeQuery();
while (result.next())
{
//...
}
}
}
Even though I do not recommend doing it: We had a similar problem and found that - at least in our case -, if you really want that empty result, you can use a native SQL query instead of the prepared statement. Apparently, it is the argument binding for the prepared statement which runs into the argument validation. The native query (which you would have manually constructed using your arguments) seemed to sidestep this validation, and just returned an empty result.
(For completeness' sake: If really manually constructing your SQL query from given arguments, be sure to know what you are doing, validate your arguments, and specifically beware of SQL injection.)
The correct answer here would be what #Gaius Gracchus offers up as an alternative suggestion in his comment to #Hans's answer. You try/catch the SQLException, gather its SQL State (always better than an SQL Code), and handle/throw a custom exception to indicate invalid input to the client. An empty result set (even though that is what the OP desires) is not accurate. The only other real alternative is to increase the size of the column or procedural input/input-output (not likely).
try {
// sql bind or execute
}
catch (SQLException e) {
String sqlState = e.getSQLState();
if (sqlState != null && sqlState.equals("22001")) {
throw new CustomException("Invalid input, etc");
}
throw e;
}
Related
I have a function
public void executeMyQuery(Connection con) {
PreparedStatement ps = con.prepareStatement("SELECT * FROM STUDENT WHERE ID = ?");
ps.setInt(1, 7);
ps.executeQuery();
}
if i will run this it will work fine. But I want to do like this.
if I will setInt it should include WHERE clause. (returns matched
row)
if I don't setInt it should exclude WHERE clause. (returns whole table)
Or is there is any way to dynamically remove or modify the string after WHERE Clause.
Disadvantages of using string based SQL for dynamic SQL
Other answers have shown how to achieve dynamic SQL using string based JDBC usage. There are many disadvantages to building SQL strings dynamically using string concatenation, including:
High risk of SQL injection if you accidentally concatenate user input to your SQL queries
Difficult to avoid syntax errors in non-trivial cases, when dynamic SQL gets more complex
Also, when you're using plain JDBC (which only supports indexed bind variables), rather than some utility such as Spring JdbcTemplate, MyBatis, jOOQ, etc. you will have to manually match ? placeholders with their corresponding indexes, which is another subtle source of errors.
Using a query builder
At some point, when you implement dynamic SQL queries more often, query builders will definitely help. The most popular ones are:
jOOQ (for dynamic SQL querying)
JPA Criteria Query (for dynamic JPAL querying)
There are many other options that are more or less well maintained. For very trivial cases (like the one in your question), you can also build your own simple predicate builder.
Disclaimer: I work for the company behind jOOQ.
You have to build your query dynamically, at the beginning of the method check whether id is null or equal 0. To make it easier you can use trick in where clause with 1=1 so where clause can be included all the time in the query.
public void executeMyQuery( Connection con, Integer id) {
String query = "SELECT *FROM STUDENT WHERE 1=1";
if(id != null){
query += "AND ID = ?";
}
PreparedStatement ps = con.prepareStatement(query);
if(id != null){
ps.setInt(1, id);
}
ps.executeQuery();}
ifNull/Coalesce work nicely for this, if you pass a null, it will select where the field equals itself.
SELECT *
FROM STUDENT
WHERE 1 = 1
and ID = ifNull(:ID, ID)
I'd also suggest something other than using ? for your variables, fine when you a couple but as you get a ton of them, difficult to keep track or modify. I've found https://github.com/dejlek/jlib/blob/master/src/main/java/com/areen/jlib/sql/NamedParameterStatement.java pretty easy, made a few modifications to fit my particular needs but SQL is much easier to read and doing substitutions in intellij db console are much easier when developing the SQL.
You can have two PreparedStatements defined in your program - one without the WHERE ID = ? clause, and another one with it.
Moreover, you are supposed to keep your PreparedStatements and re-use, so you better store them as a field, etc.
And then, when needing to get the data - call either the first prepared statement, or the second one.
Michael Dz is close to the solution in his answer, but there is a problem in the code : he calls setInt on a non existing preparedStatement.
Try something like this :
public void executeMyQuery( Connection con, int Id) {
StringBuffer sql = new StringBuffer();
sql.append("Select * FROM STUDENT");
if(Id > -1) {
sql.append(" Where ID = ?");
}
preparedStatement ps = con.prepareStatement(sql.toString());
if(ID > -1) {
ps.setInt(1, Id);
}
ps.executeQuery(); // You might want to catch the result of the query as well
}
Hope this helps !
I have some old C code I'm moving to Java and I have to port along existing SQL functions into JDBC. The commands are written like this (two of many examples):
RESULT:=1 + ?
IF ? > 0 THEN RESULT:=0 ELSE RESULT:= 1; END IF;
Those are two examples (separate commands).
Changing the syntax of the commands isn't an option, but some clever runtime replacement is valid. Note the ? are values that will get populated at runtime from other data.
I've tried this as statements, prepared statements, and callable statements, and I can't seem to get the syntax correct for getting "RESULT" back (actually, can't get it to execute() on any statement.
For test purposes I've been trying a simple command of either:
RESULT:=1+2
RESULT:=1+?
Just to see if I can get the type of statement working. But no luck.
The most common answer, from code that otherwise looks reasonable, is this:
String queryString = "declare result integer; begin RESULT:= 1 + 3; ? := RESULT; end";
try (CallableStatement cst = conn.prepareCall(queryString))
{
cst.registerOutParameter(1, Types.INTEGER);
cst.execute();
}
When I run that, on the execute (or executeUpdate, doesn't make a difference) I get back an Oracle 17002/08006 (Size Data Unit mismatch, network issue / invalid connection). But I have to believe that error is somewhat of a red herring, because the connection is definitely valid, and a trivially statement works fine.
Does anyone know the correct JDBC approach to calling that and getting a valid result (out of RESULT)?
I'm using Java 7 and Oracle 11g, if it matters.
Here's an example of calling a stored procedure that accepts an Integer argument and returns a single value (my Java method returns an Object and you can cast it to whatever you like).
public Object getResultFromStoredProcedure(String query,Integer param) {
Object obj=null;
ResultSet rs=null;
PreparedStatement stmt=null;
try{
stmt=conn.prepareStatement(query);
stmt.setInt(1,param);
rs = stmt.executeQuery();
if(rs.next()){
obj=rs.getObject(1);
}
}catch(SQLException e){
//catch exception and process it or log it
}finally{
rs.close();
stmt.close()
}
return obj;
}
Provided that you called your procedure OrclProc, you'd call it thus
String query = "select OrclProc(?) from dual";
Object result = getResultFromStoredProcedure(query, 5);
This question already has answers here:
java.sql.SQLException: Column count doesn't match value count at row 1
(3 answers)
Closed 6 years ago.
try {
Statement stmt = con.createStatement();
stmt.executeUpdate("INSERT into emailacc(fname,lname,uname,mail,passwd,passwd2,date,month,year) values('"+fname+","+lname+","+uname+","+mail+","+passwd+","+passwd2+","+date+","+selectMonth+","+year+"')");
out.println("<h3><font color='green'>Information Added Successfully.</font><br> You Are a registered User now.</h3><br>");
con.close();
} catch(Exception e) {
out.println("Exception caught : "+e);
}
Why is it happening?
Last time I did the same but it didn't happen, whats wrong with it?
Well to start with what's wrong with it is that you're including the values directly into your SQL. Don't do that. Ever. Use a parameterized SQL statement via PreparedStatement, and set the parameter values appropriately. That way you don't need to worry about SQL injection attacks, and it'll also be a lot easier to look at what the actual SQL is, without worrying about where the values come from (or rather, separating those two concerns).
I suspect the immediate problem is that you're not quoting any values, so you've got a SQL statement like a longer version of:
INSERT into Foo(name) VALUES (jon)
rather than
INSERT into Foo(name) VALUES ('jon')
... but using parameterized SQL will fix this anyway, so please don't just change the SQL to include single quotes everywhere.
Is because you are omitting single quotes, for avoid this mistakes my recommendation is to use PreraredStatement, also in order to proper close connection it mus be in a finally block , you code must look at this:
try {
PreparedStatement stmt = con.prepareStatement("INSERT into emailacc(fname,lname,uname,mail,passwd,passwd2,date,month,year) values(?,?,?,?,?,?,?,?,?)");
stmt.setString(1,fname);
stmt.setString(2,lname);
stmt.setString(3,uname);
stmt.setString(4,mail);
stmt.setString(5,passwd);
stmt.setString(6,passwd2);
stmt.setDate(7,date); //you need convert your date to java.sql.Date if 'date' field of database is of type date. If not setString is fine
stmt.setInt(8,selectMonth);
stmt.setInt(9,year);
stmt.executeUpdate();
out.println("<h3><font color='green'>Information Added Successfully.</font><br> You Are a registered User now.</h3><br>");
} catch (Exception e) {
con.rollback();
out.println("Exception caught : " + e);
} finally {
if (con != null) {
try {
con.close();
} catch(SQLException ex){
//DO NOTHING
}
}
}
You can learn more of PreparedStatemt in:
http://download.oracle.com/javase/tutorial/jdbc/basics/prepared.html
A final note: PreparedStament are more efficent thant Statement and avoid the SQL Injection hack so PrepararedStatement is more secure. Try use always a PreparedStatement
Your insert statement is missing quotes between the string insert statement should be:
INSERT into emailacc(fname,lname,uname,mail,passwd,passwd2,date,month,year) values('"+fname+"','"+lname+"','"+uname+"','"+mail+"','"+passwd+"','"+passwd2+"',"+date+","+selectMonth+","+year+")");
every column varchar or text should be between single quotes also double check your date format you might have to use the to_date function : to_date(,'DD-MM-YYYY') just a sample
How can I get SQL query that was received by MySQL server when it raises exception?
I have a code similar to the following:
Connection con = null;
PreparedStatement ps = null;
try {
con = DbConnectionManager.getConnection();
ps = con.prepareStatement(query);
setStatementParameters(ps, params);
ps.executeUpdate();
} catch (SQLExeption e) {
// How to get wrong query here?
} finally {
DbConnectionManager.closeConnection(ps, con);
}
Where query variable is like "INSERT into someTable (qwe, asd) VALUES (?, ?)"
The question is how can I get query string in the catch block?
The SQLException may or may not have the query string contained in its exception message. You can't depend on it. If you just want to see it for debugging purposes, though, then that's probably your best bet. However, in this particular example that's not a problem because you have direct access to the query variable that you used to set up the query in the first place.
I've run across another solution to this problem.
The MySQL JDBC driver overrides the toString of PreparedStatement to display the query as it is sent to the database. This is implementation dependent so it may not the best thing to rely on, but it's very simple to get at. I'm now using this to dump query text to a log file for debugging purposes. While there are probably other solutions that are more portable and future-proof, this has the advantage of getting exactly the string that the MySQL driver says it's sending to the database.
The string comes back with an object ID, then a colon, then the SQL string. You can split it on the colon to get just the SQL.
The type com.mysql.jdbc.PreparedStatement also exposes a protected method call asSql(). You could override the class with your own implementation that gives public access to this method. From looking at the disassembly of the class's toString() method, it seems to be using asSql() to get the actual SQL string. This approach adds the problem of how to instantiate your subclass, though; the simplest approach is just to use the toString that you already have access to, without even having to downcast your PreparedStatement to a MySQL-specific subtype.
Again, just be aware that the maintainers of the MySQL API probably don't consider this part of the public interface to their software (JDBC defines the standard interface), so they may make changes later that would break this mechanism. For the time being, though, it will get the job done.
This is true for the version of the MySQL driver I'm currently using, which is 5.1.7.
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.