for some reason my SQL connection seems to be returning a completely invalid response. Somehow between one call and the next (after a brief period of time, about a second) it returns an entirely different value. I should note that I have other rows set up the exact same way that do not have this problem.
I should note that I'm using apache dbcp2 for the SQL datasource which is where the getConnection() is calling. I should note that everything else works just fine but not this for some reason.
Hope I included enough information. I'm not asking for you to do the work for me, I just have no idea what the issue could be here.
//First call
//Note Global.TOKEN_TTL is a constant value of 300000
System.out.println((System.currentTimeMillis() + Global.TOKEN_TTL));
Start.getSQLConnection().setTTL(userID, (System.currentTimeMillis() + Global.TOKEN_TTL));
System.out.println(Start.getSQLConnection().getTTL(userID));
//Second Call
System.out.println(System.currentTimeMillis());
System.out.println(Start.getSQLConnection().getTTL(userID));
Output
----Call one
System call: 1529456964416
DB call: 1529456964416
DB Actual: 1529456964416
----Call two
System call: 1529456665144
DB call: 4294967295
DB Actual: 1529456964416
The database is set up using MariaDB. The row is a signed BIGINT with a length of 20.
Retrieval
Code below is in order of called first to last:
First call:
getLong("adbname", "TTL", "SELECT TTL FROM atablename WHERE ID=?;", userID);
Second call:
public long getLong(String database, String tag, String sql, Object... parameters) throws SQLException {
return (long) getObject(database, sql, tag, parameters);
}
Third call:
public Object getObject(String database, String sql, String tag, Object... parameters) throws SQLException {
Connection conn = getConnection(database);
conn.setAutoCommit(false);
PreparedStatement stmt = conn.prepareStatement(sql);
assert parameters != null && parameters.length > 0;
for (int i = 0; i < parameters.length; i++) {
stmt.setObject(i + 1, parameters[i]);
}
ResultSet rs = stmt.executeQuery();
if (!rs.next())
return null;
Object o = rs.getObject(tag);
conn.commit();
if (rs != null)
rs.close();
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
return o;
}
Setting
public boolean setTTL(long userID, long TTL) throws SQLException {
return executeUpdate("adbname", "UPDATE atablename SET TTL=? WHERE ID=?;", TTL, userID);
}
//The actual work
public boolean executeUpdate(String database, String sql, Object... parameters) throws SQLException {
Connection conn = getConnection(database);
conn.setAutoCommit(false);
PreparedStatement stmt = conn.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
stmt.setObject(i + 1, parameters[i]);
}
boolean result = stmt.executeUpdate() > 0;
conn.commit();
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
return result;
}
I hate to admit this but #Scary Wombat was right. It ended up (not exactly) being one of those "oh I forgot to tell you" scenarios.
The issue was a design flaw in the way that the user ID was being retrieved due to the fact that they weren't guaranteed to be unique (pretty serious flaw on my part). It was pulling data from another user.
Thanks a ton for all the help, while it didn't directly solve the problem you gave me insight into a few things that I was doing wrong with SQL and storage in general. I'll make sure to remember this one, thanks again!
The clue: 4294967295 = 2^32-1
Smells like you have a 32-bit build of some product, possibly even the OS.
Or you are using INT UNSIGNED when you need BIGINT. You say "The row is a signed BIGINT with a length of 20" -- Please provide SHOW CREATE TABLE.
And what is to keep Object o = rs.getObject(tag); from being limited to 32 bits?
Related
I have a problem with my sql query in Java. As you can see below, the query is supposed to return rph_id but it returns only 0, so it only proceeds to the else method.
At first, I did not use prepareStatement but many suggest that I use prepareStatement. But it still won't work. After checking both suggestions, I think my problem is on the query but I don't know what.
public class RphDAO{
static int rph_id;
public static RPHS getRph(RPHS rph) {
try {
currentCon = ConnectionManager.getConnection();
ps=currentCon.prepareStatement("select * from rphs where rph_id=?");
ps.setInt(1, rph_id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
rs.getInt("rph_id");
rph.setRph_id(rph_id);
rph.setValid(true);
} else {
rph.setValid(false);
}
}
...
return rph;
}
}
It should return an existing id, not 0.
Here:
if (rs.next()) {
rs.getInt("rph_id");
rph.setRph_id(rph_id);
rph.setValid(true);
You get a value from the ResultSet rs ... to then throw that result away.
Probably you meant something like:
rph_id = rs.getInt("rph_id");
On the other hand, your request those entries that have rph_id==1, so I don't see what other value than 1 you expect to find here. Most likely, you want to look into other parts of that result set, and somehow pull those into variables/fields of some object.
It seems as if the OP is simply not familiar with using ResultSet, thus I think the real answer is: go and read a good tutorial on this subject. You can't learn how to use a new API by trial and error. Read its documentation, or a good tutorial that shows you how to use it properly!
I think you should use COUNT instead in your sql since it is no point in setting the id if it is already correct and also use the id from the object given as parameter to the method for consistency.
public static RPHS getRph(RPHS rph) {
try {
currentCon = ConnectionManager.getConnection();
ps=currentCon.prepareStatement("select COUNT(*) from rphs where rph_id=?");
ps.setInt(1, rph.getRph_id);
ResultSet rs = ps.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rph.setValid(count == 1);
}
I am having a problem with a ResultSet being closed. What confuses me is that it works for a portion of the data and then closes. At first I thought it might be because of connection timeout but that doesn't seem the case.
This portion of the program pertains to comparing an .xlsx workbook to an already present SQL database and for lack of a better term merges/updates it.
First, in my CompareDatabase class I am calling a search function that searches an SQLite database for a specific string every 6 iterations.
int columnCount = 6;
dataPoint = dataPoint.replaceAll("Detail", "");
String[] temp = dataPoint.trim().split("\\s+");
System.out.println(Arrays.toString(temp));
for (String tempDataPoint : temp) {
if ( columnCount == 6) {
System.out.println(search(tempDataPoint, connection));
}
columnCount = 0;
} else {
columnCount++;
}
}
This search function (also in the CompareDatabase class is then supposed to search for the value and return a String (was originally a Boolean but I wanted to see the output).
private String search (String searchValue, Connection connection) throws SQLException {
PreparedStatement pStatement = null;
pStatement = connection.prepareStatement("SELECT * FROM lotdatabase where (Vehicle) = (?)");
pStatement.setString(1, searchValue);
try (ResultSet resultSet = pStatement.executeQuery()){
return resultSet.getString(1);
}finally {
close(pStatement);
}
}
At the end you can see that the PreparedStatement is closed. The ResultSet should also be closed automatically (I read somewhere) but JDBC could possibly be being unreliable.
The Connection however is still open as it will be searching some 200+ strings and opening and closing that many times did not seem like a good idea.
These functions are called by my main class here:
One is commented out since it will error out because of primary key violation.
public static void main(String[] args) {
SQLDatabase sqlDatabase = new SQLDatabase();
//sqlDatabase.convertToSQL("Database1.xlsx");
sqlDatabase.compare("Database2.xlsx");
}
I have a suspicion that I am going about a bunch of this wrong (on the aspect of managing connections an such) and I would appreciate a reference to where I can learn to do it properly.
Also, being that PreparedStatement can only handle one ResultSet I don't see that being my issue since I close it every iteration in the for loop.
If more code or explanation is required please let me know and I will do my best to assist.
Thank you for taking the time to read this.
So after a bit more Googling and sleeping on it here is what worked for me.
The search function in compareDatabase changed to this:
private Boolean search (String searchValue, Connection connection) {
PreparedStatement ps = null;
try {
ps = connection.prepareStatement("SELECT * FROM lotdatabase where " +
"(Vehicle) = (?)");
ps.setString(1, searchValue);
ResultSet resultSet = ps.executeQuery();
//The following if statement checks if the ResultSet is empty.
if (!resultSet.next()){
resultSet.close();
ps.close();
return false;
}else{
resultSet.close();
ps.close();
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
And in the other function within compareDatabase I call the search function like this:
if (search(tempDataPoint, connection)) {
System.out.println("MATCH FOUND: " + tempDataPoint);
}else {
System.out.println("NOT FOUND: " + tempDataPoint);
}
This allows me to check the ResultSet and also be sure that it is closed.
I'm trying to insert a new record into an MS SQL database, and I'm getting an exception I've never seen before. When I call executeUpdate the following exception is thrown:
com.microsoft.sqlserver.jdbc.SQLServerException: A result set was generated for update.
This is the Java code that produces the error:
// addComment method adds a new comment for a given requestId
public CommentBean addComment(CommentBean comment) {
PreparedStatement stmt = null;
INative nat = null;
Connection conn = null;
try {
nat = dbConn.retrieveNative();
conn = (Connection)nat.getNative("java.sql.Connection");
stmt = conn.prepareStatement(ADD_COMMENT);
stmt.setInt(1, comment.getRequestId());
stmt.setString(2, comment.getComment());
stmt.setString(3, new SimpleDateFormat("MM/dd/yyyy").format(comment.getDateCreated()));
stmt.setString(4, comment.getCreatedBy());
comment.setCommentId(stmt.executeUpdate()); // exception
} catch(Exception ex) {
System.err.println("ProjectRegistration::SQLDAO - addComment");
ex.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
} catch (Exception e) {}
}
return comment;
}// end addComment
Where ADD_COMMENT is defined as a String:
private static final String ADD_COMMENT = "INSERT INTO RequestComments OUTPUT INSERTED.commentId VALUES(?,?,?,?)";
For the sake of being thorough, the table is defined as:
CREATE TABLE RequestComments (
commentId int NOT NULL PRIMARY KEY IDENTITY(1,1),
requestId int FOREIGN KEY REFERENCES Requests(requestId),
comment varchar(400),
dateCreated date,
createdBy varchar(12)
);
I don't think I'm doing anything terribly complicated here, but I can't think of why I'm getting this exception. I have a method in the same class which does the exact same type of insertion (literally the same query with a different table name and number of values), and it has no issues. Does anyone have any ideas on how to resolve this issue?
This particular error can also be caused by an INSERT-trigger, which has a SELECT-statement as a part of the trigger code.
To test whether this is the case, you can try:
using executeQuery(), instead of executeUpdate() - and display the result.
executing the insert in tool like MySQL Workbench, SQL Server Management Studio, or whatever flavour of database design tools are available for your DBMS, to see whether a result is returned.
Related: sql server error "A result set was generated for update"
I'm hoping this may help others looking at the same error message, as it did for me. My solution was to live with a call to executeQuery(), although it only handles an underlying issue, instead of fixing it.
This instruction stmt.executeUpdate() is not returning the commentId, it returns a ResultSet which you could then get the commentId from. Something like this,
ResultSet rs = stmt.executeQuery(); // Not update, you're returning a ResultSet.
if (rs.next()) {
comment.setCommentId(rs.getInt(1));
}
you are using OUTPUT in your insert query i.e you will get a resultset after your query executes and to hold that you need an object of class ResultSet to hold that data
SqlServer : When SET NOCOUNT is ON, the count is not returned. When SET NOCOUNT is OFF, the count is returned.
Connection conn = DriverManager.getConnection(connectDB,user,pwd);
String sql = " set nocount off;INSERT INTO test (name) values (1)";
PreparedStatement prepareStatement = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
System.out.println(prepareStatement.executeUpdate());
ResultSet generatedKeys = prepareStatement.getGeneratedKeys();
if(generatedKeys.next()){
System.out.println(generatedKeys.getString(1));
}
Related:
set-nocount-on-usage
I've had a similar problem where after a while an insert on a autonumber table would give a "A result set was generated for update." at random. I use connection pooling and somehow the driver can get into a state where executeUpdate in combination with Statement.RETURN_GENERATED_KEYS doesn't work anymore. I found out that in this state an executeQuery does the trick, but in the initial state executeQuery does not work. This lead me to the following workaround:
PreparedStatement psInsert = connection.prepareStatement("INSERT INTO XYZ (A,B,C) VALUES(?,?,?)", Statement.RETURN_GENERATED_KEYS);
ResultSet rs = null;
try {
psInsert.setString(1, "A");
psInsert.setString(2, "B");
psInsert.setString(3, "C");
Savepoint savePoint = connection.setSavepoint();
try {
psInsert.executeUpdate();
rs = psInsert.getGeneratedKeys();
} catch (SQLServerException sqe)
{
if (!sqe.getMessage().equals("A result set was generated for update."))
throw sqe;
connection.rollback(savePoint);
rs = psInsert.executeQuery();
}
rs.next();
idField = rs.getInt(1);
} finally {
if(rs != null)
rs.close();
psInsert.close();
}
I would like to get an integer saved in my MySql DB into an Integer in Java. I have a Table, that includes PlayerName and Level. I would like to get The Level (Integer) From a Specific Player. And then Add Integer "Value" to it. Then put it back in the DB. My Code up to now is:
public void addinputPData(String loc, int value, Player player, String playername){
//add input Player Data
try{
logm("Putting Kill Death Int Data into " +player.getName() + "'s Profile!");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/WebCom", "root", "MyPW");
int ovalue = -1;
Statement stmt = (Statement) con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT "+loc+" FROM PlayerData WHERE PlayerName='"+playername+"'");
if(rs.next()){
ovalue= rs.getInt(loc);
}
if(ovalue == -1){
logm("Error Occured");
}
int nvalue = value + ovalue;
String insert = "UPDATE PlayerData SET "+ loc + "='" + nvalue + "' WHERE PlayerName='" + playername + "'";
stmt.executeUpdate(insert);
con.close();
}catch(Exception e){
logm("Could Not Send Data To MYSQL DATABASE SERVER s: "+ e.getMessage());
}
}
I don't know why this won't work, Is there anything obvious that i am missing? Thank you in advance.
So first what you must understand is that when you won't use parametrized statements, there is big danger of SQL Injection. So your code is very dirty written.
So anyway, use PreparedStatement with parametrized SQL statements for much more better performace. Now rewrite your code like this:
final String SELECT_QUERY = "SELECT level FROM PlayerData WHERE PlayerName = ?";
final String UPDATE_QUERY = "UPDATE PlayerData SET level = ? WHERE PlayerName = ?";
public boolean dataMethod(String playerName) {
Connection con = null;
PreparedStatement ps = null;
PreparedStatement ps1 = null;
ResultSet rs = null;
int dataLevel = 0;
try {
// getConnection etc...
ps = con.prepareStatement(SELECT_QUERY);
ps.setString(1, playerName) // first param is order of ? param, starts with 1(not 0)
rs = ps.executeQuery();
while (rs.next()) {
dataLevel = rs.getInt();
}
if (dataLevel > 0) {
ps1 = con.prepareStatement(UPDATE_QUERY);
ps1.setInt(1, dataLevel);
ps1.setString(2, playerName);
ps1.executeUpdate();
}
return true;
}
catch (SQLExcetion ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
finally {
if (con != null) {
con.close();
}
}
}
Step by step, first init your statement, sets parameters if you have then when you use select, you will retrieve data in ResultSet that is table of data generated with query. imlicitly cursor in ResultSet is position before first row so you have to use next() method to go on current row and with the help of getter method you add data from ResultSet to your variable. Then you check if it's correct, if do, init second statement and execute it. And that's all.
But you should consider when you use more that 1 operation, sets autoCommit on false and all operations will do in one Transaction, because implicitly in JDBC is one operation = one transaction. And second, you should consider to use SQL stored procedures for add any data, update data or delete. It's more safer yet and less code. So let database working when it able to do it and also it's faster of course.
At the last, really you should think about this approach and makes your code more safer, faster and cleaner. Not have look on simplicity but on efficiency, compability and security.
More about SQL Injection
And when you decided right to use stored procedure, you can use it like this:
CREATE OR REPLACE PROCEDURE SOME_NAME(VARCHAR v_name PlayerData.name%type)
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
// body
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
So now you have to create String for call procedure.
final String CALL_SOMENAME = "{call SOME_NAME(?)}";
Then intead of PreparedStatement you have to use CallableStatement that is interface used to execute SQL stored procedures.
cs.prepareCall(CALL_SOMENAME); // Creates a cs object for calling db stored procedures
cs.setString(1, playerName);
cs.execute();
I don't know why many people searching the easiest way to do something and don't look at performance and readability of code.
Regards
In the UPDATE statement, you're inserting the value for the "loc" column as a string (there are single quotes around the value). If the database column is an integer, then this could be causing a problem.
Tip: JDBC provides a class called PreparedStatement. This class allow you to build SQL queries safely. It makes sure that all user input is properly escaped in order to avoid security vulnerabilities.
PreparedStatement ps = con.prepareStatement("UPDATE PlayerData SET " + loc + " = ? WHERE PlayerName = ?");
ps.setInt(1, nvalue);
ps.setString(2, playername);
ps.execute();
I have table VIDEO (VideoID int Primary Key, Address Varchar(100)) and I want to search table to see if there is video with given address. But I am not sure if this code works good, and if this could be better done.
Here is my code:
public boolean checkIfVideoExist(String address) throws SQLException {
int count = 0;
Statement stmt = connection.createStatement();
ResultSet rset = stmt
.executeQuery("SELECT Count(VideoID) from VIDEO WHERE Address='"
+ address + "'");
if (rset.next())
count = rset.getInt(1);
if (count == 0)
return false;
else
return true;
}
Be sure you have an index set on column ADDRESS. Then your query should run fast.
It is better to use a prepared statement to pass the address value to the query. And you should close the result set and the statement.
And
if (count == 0)
return false;
else
return true;
looks a bit strange.
public boolean checkIfVideoExist(String address) throws SQLException {
int count = 0;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
stmt = connection.prepareStatement(
"SELECT Count(VideoID) from VIDEO WHERE Address=?");
stmt.setString(1, address);
rset = stmt.executeQuery();
if (rset.next())
count = rset.getInt(1);
return count > 0;
} finally {
if(rset != null) {
try {
rset.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
}
}
The code is fine, except for the way you're embedding strings in your query. If address contains a quote character, the query will become invalid. And this is only a small part of the problem. Doing it like this opens the door to SQL injection attacks, where malicious users could enter an address which completely changes the meaning of the query.
Always use prepared statements to bind parameters:
PreparedStatement stmt = connection.prepareStatement("SELECT Count(VideoID) from VIDEO WHERE Address=?");
stmt.setString(1, address); // proper escaping is done for you by the JDBC driver
ResultSet rset = stmt.executeQuery();
Also, you should use finally blocks to close your result sets and statements.
Your code is vulnerable to SQL Injection, it should use a prepared statement instead of building the SQL query via string concatenation.
Apart from that, it looks OK.
ResultSet rset = stmt.executeQuery("SELECT * from VIDEO WHERE Address='" + address + "'");
return rset.next();
then there is at least one matching record and you are done. No need for aggregate function count() ....