When we work with JPA and JPQL we can use some date/time expressions which make the query DMBS independent. For instance let's say if I want to set the ending time of a session in my database I could simply use CURRENT_TIMESTAMP expression as follows:
String jpql = "UPDATE SessionJpa s SET s.end = CURRENT_TIMESTAMP WHERE s.id = :id";
entityManager.getTransaction().begin();
Query query = entityManager.createQuery(jpql);
query.setParameter("id", someIdValue);
query.executeUpdate();
entityManager.getTransaction().commit();
This way the same JPQL should work with Oracle, MySQL, PostreSQL, etc as DBMS.
Now my question: Is there a way to achieve the same when using JDBC instead of JPA?
This is what I have so far:
String sql = "UPDATE Sessions SET end = SYSDATE WHERE id = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, someIdValue);
int updatedRows = statement.executeUpdate();
// ...
} catch(SQLException ex) {
Logger.getLogger(SessionsBean.class.getName()).log(Level.SEVERE, null, ex);
}
But of course SYSDATE is not a generic expression and it will work only with Oracle as DBMS most likely.
Apart from the fact that most databases have the SQL standard CURRENT_TIMESTAMP, JDBC drivers might support the JDBC escape functions and translate those to the database specific variant. These escapes are called using {fn <function>}, and are listed in Appendix C of the JDBC 4.2 specification.
Specifically (from C.3):
CURRENT_DATE[()] Synonym for CURDATE()
CURRENT_TIME[()] Synonym for CURTIME()
CURRENT_TIMESTAMP[()] Synonym for NOW()
CURDATE() The current date as a date value
CURTIME() The current local time as a time value
NOW() A timestamp value representing the current date and time
So the JDBC escape equivalent would be:
String sql = "UPDATE Sessions SET end = {fn CURRENT_TIMESTAMP} WHERE id = ?";
(or {fn NOW()})
Note that although JDBC drivers are required to support the escape syntax, they are not actually required to support all functions. Check the result of DatabaseMetaData.getTimeDateFunctions() for your driver.
You can find a related discussion here - Is Oracle's CURRENT_TIMESTAMP function really a function?.
Summary is that - CURRENT_TIMESTAMP is defined by the SQL standard and any compliant database system should recognize it.
You can get the same by defining another parameter for the date, such as:
String sql = "UPDATE Sessions SET end = ? WHERE id = ?";
...
statement.setTimestamp(1, new java.sql.Timestamp(new java.util.Date().getTime()));
statement.setLong(2, sesion.getId());
I hope this works
Related
I have a java servlet application and I'm using a prepared query to update a record in a SQL Server Database table.
Lets say I want to execute UPDATE MyTable SET name = 'test' WHERE id = '10'. (Yes, id is a varchar)
I used the following code to make this happen:
PreparedStatement pstmt = con.prepareStatement("UPDATE MyTable SET name = ? WHERE id = ?");
pstmt.setString(1, getName() );
pstmt.setString(2, getID() );
pstmt.executeUpdate();
I found out that while I was running a JMeter script to simulate 2 users, this statement causes a deadlock in my database.
I wanted to check what my values were in the SQL Profiler so I used the following code, so I could check the values.
String query = String.format("UPDATE MyTable SET name = '%s' WHERE id = '%s' ", getName(), getID() );
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.executeUpdate();
Suddenly my deadlock was gone! It's a shame the last approach is vulnerable to SQL injection.
Is there somebody who can tell me what is going on and/or how to fix it?
Ok I finally found the problem and solution to my problem.
It seemed that the combination of the jTDS JDBC driver with MSSQL was the 'problem'.
This article explained my situation exactly. And with the help of this FAQ I was able to set the datasource to the right configuration.
From what I understand:
If you have statement that uses a String-like index (Like in my situation), the table performs an index SCAN instead of an index SEEK. This causes the whole table to be locked and vulnerable to deadlocks.
I hope this will help other people too.
In my application i have a java Timestamp dateCreated, which is inserted to a mysql Timestamp colum. Inserting is no problem, i use a prepared statement and statement.setTimestamp(dateCreated).
Now i need to select a row with the dateCreated as unique identifier. my method gets another java Timestamp object.how does the SQL query work in this case? i havent figured out how to compare the java timestamp to the mysql one.
SELECT * FROM table WHERE timestamp_column = ???
Thanks!
In Java you will use a similar setTimestamp method as you did with the insert.
Timestamp t = ???;
String sql = "SELECT * FROM table WHERE timestamp_column = ?";
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setTimestamp(t);
etc.
I'm attempting to create a JDBC query with the following statement
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName +
"where datediff(d,DATECOLUMN2,getdate()) <= 1";
st = conn1.createStatement();
rs = st.executeQuery(query); //receiving error here
I am receiving the following error message
java.sql.SQLException: "d" is not a recognized table hints option. If it is intended as a parameter to a table-valued function or to the CHANGETABLE function, ensure that your database compatibility mode is set to 90.
I'm sure the query isn't recognizing the datediff function for some reason I am not sure why since i was previously using HQL in the same application and retrieving the values.
In an attempt to use an alternative function I used
{fn TIMESTAMPADD( SQL_TSI_DAY, 1, CURRENT_TIMESTAMP)}
but it also failed I later on found that this is only used for Derby Database's
Can someone assist me in using the proper sql function to compare a date with the current date using JDBC
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName "+
"where datediff(day,DATECOLUMN2,getdate()) <= 1";
You have a comma before from. Based on the error messages you are running this against SQL server.
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName "
+" where datediff(d,DATECOLUMN2,getdate()) <= 1";
The comma after the "d" should be a dot:
where datediff(d.DATECOLUMN2,getdate())
--------------- ^ dot here
The posted snippet doesn't have a closing double quote between tableName and +, but I figure that is just a typo. However, in your real code, where precisely is the double quote? Is it directly after tablename, like this
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName" +
or after the space that follows tablename, like this
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName "+
It is very likely the former, because in that case the resulting query would look exactly the way as to cause the error you are getting. Take a look at this:
SELECT COLUMN1,DATECOLUMN2 FROM tableNamewhere datediff(d,DATECOLUMN2,getdate()) <= 1
You can see that where merges with the table name and datediff becomes an alias. What follows is interpreted as table hints. (You can specify table hints without WITH in older versions of SQL Server/older compatibility levels.) Consequently, SQL Server stumbles over d, as that is indeed an incorrect table hint.
I have an application that should support oracle and mysql databases.
I will get different configuration for different database.
But i want all HSQL used in the code to be intact.
But i am not able to do so bcoz of below :
I have created a query like below:
String SQL_QUERY = "select count(log) from dbtable where created_date='"+ givenDate
Query query = session.createQuery(SQL_QUERY);
query.uniqueResult();
This works very well in mysql
but not in oracle
because oracle db expects formatting the value of created_date column with
to_date(givenDate,"yyyy-MM-dd")
so i have to change the above query as :
String SQL_QUERY = "select count(log) from dbtable where created_date=to_date('"+ givenDate+","yyyy-MM-dd")
Can I avoid this multiple query declarations in any way for mysql and oracle ??
Create a java.sql.Date out of that String and pass it to the query; don't embed the conversion in the database.
Better yet, bind the String to a Date long before it gets to the persistence tier. Java's an object-oriented language; think in terms of objects.
Here is the stored proc in MySQL:
CREATE PROCEDURE topModels(modelCount INT)
BEGIN
select brand_name as brandName,
model_number as modelNumber,
model_description as modelDescription,
parts
from
(
select model_number, model_description, parts
,#rownum := if(#brand=t.brand_name, #rownum, 0) + 1 as rownum
,#brand := t.brand_name as brand_name
from
(
select brand_name, model_number, model_description, parts,
from model f
group by brand_name, model_number, model_description
order by brand_name, sum(sold_count) desc
) t
) tbl
where rownum<=5
END
When I open MySQL workbench and do:
CALL topModels(5)
it return around 3000 rows, which is correct result.
When I run it from Java:
java.sql.CallableStatement cs = connection.
prepareCall("CALL topModels(5)");
java.sql.ResultSet rs = cs.executeQuery();
It returns about 400,000 rows as if it ignores "where rownum<=5" condition.
What do I do wrong?
I see that your procedure has modelCount but you never use it.
IIRC MySQL does not have rownum. Use LIMIT instead:
String sql = "SELECT * FROM tbl ... LIMIT " + modelCount.
Unfortunately, you can't use procedure param in LIMIT (at least not until 5.1).
You may need to use the JDBC call syntax, something like:
conn.prepareCall("{call topModels(5)}");
I'm completely new to Java JDBC (coming from a .NET camp). However looking at the Oracle documentation. I think you need to pass in parameters separately thus:
java.sql.CallableStatement cs = connection.prepareCall("CALL topModels(?)");
cs.setInt(1, 5); // <-- I'm assuming you need set the parameter like this
java.sql.ResultSet rs = cs.executeQuery();
Look this framework (https://code.google.com/p/lindbergframework/). Easy handling of stored procedures and functions, including treatment of cursors automatically.
https://code.google.com/p/lindbergframework/