I know the advantages of using PreparedStatement, which are
query is rewritten and compiled by the database server
protection against SQL injection
But I want to know when we use it instead of Statement?
Query is rewritten and compiled by the database server
If you don't use a prepared
statement, the database server will
have to parse, and compute an
execution plan for the statement
each time you run it. If you find
that you'll run the same statement
multiple times (with different
parameters) then its worth preparing
the statement once and reusing that
prepared statement. If you are
querying the database adhoc then
there is probably little benefit to
this.
Protected against SQL injection
This is an advantage you almost
always want hence a good reason to
use a PreparedStatement everytime.
Its a consequence of having to
parameterize the query but it does
make running it a lot safer. The
only time I can think of that this
would not be useful is if you were
allowing adhoc database queries; You
might simply use the Statement
object if you were prototyping the
application and its quicker for you,
or if the query contains no
parameters.
Ask Tom's opinion:
The use of a Statement in JDBC should be 100% localized to being used for DDL (ALTER,
CREATE, GRANT, etc) as these are the only statement types that cannot accept BIND
VARIABLES.
PreparedStatements or CallableStatements should be used for EVERY OTHER type of statement
(DML, Queries). As these are the statement types that accept bind variables.
This is a fact, a rule, a law -- use prepared statements EVERYWHERE. Use STATEMENTS
almost no where.
He's specifically talking about Oracle but the same principle applies to any database that caches execution plans.
Database apps that scale and prevent SQL injection attacks at the same time? What's the downside?
I would turn this round: in a publicly distributed app, you should generally always use prepared statements unless you have a really compelling reason not to, and you should always supply parameters "properly" to the prepared statement, and not by splicing them into the query string.
Why? Well, basically because of the reasons you gave (or at least, the second one)...
PreparedStatements should be used very carefully in WHERE clauses.
Suppose that a table is defined as:
create table t (int o, k varchar(100), v varchar(100))
(e.g. "o: object-ID (foreign key), k: attribute-key, v: attribute-value").
Furthermore there is a (non-unique) index on v.
create index ixt on t ( v )
Suppose that this table contains 200 million rows inserted like:
for (i = 0; i < 100*1000*1000; i++) {
insert into t (o,k,v) values (i,'k1','v1');
insert into t (o,k,v) values (i,'k2', Convert(i, varchar));
}
("Thus, every object o has attributes k1=v1 and k2=o")
Then you should not build queries like:
select o,p,v from t as tx, t as ty where tx.o=ty.o and tx.k=? and tx.v=? and ty.k=? and ty.v=?
("find objects that have two given attributes")
My experience with ORACLE and MSSQL is, that those queries might need many minutes to return. This is true even if no row matches the where clause. It depends on wether the SQL-Server decides to lookup tx.v or ty.v first.
One shoud put the values for the columns k and v directy into the statement. I think this is because the SQL-Servers take the values into account when computing the execution plan.
A query look like this returns always after milliseconds:
select o,p,v from t as tx, t as ty where tx.o=ty.o and tx.k='k1' and tx.v='v1' and ty.k='k2' and ty.v='1234'
("The SQL-Server will always search first for v='1234' and then for v='v1' ")
Regards
Wolfgang
Statement: Each time the sql query is running,this sql statement is sent to the DBMS where it is compiled. So, it increases the server loads and decreases the performance.
connection con=null;
String sql="select * from employee where id=5";
Statement st=conn.createStatement();
PreparedStatement: Unlike Statement PreparedStatement is given a sql query as a parameter when it is created.
connection con=null;
String sql="select * from employee where id=?";
PreparedStatement ps=conn.prepareStatement(sql);
This sql statement is sent to Database where it is compiled.
So,in preparedStatement compiled happens only once but in statement compiled happens each time Statement is called.
You can always use PreparedStatement instead of Statment( select, insert , update, delete ). Better performance and protected against SQL injection.
But, don't use it with a dynamic request like a request with WHERE variable IN [ hundreds possibilities ] :
It's counter-productive, you lost performance and memory because you cache every time a new request, and PreparedStatement are not just for SQL injection, it's about performance. In this case, Statement will not be slower.
Your pool have a limit of PreparedStatment ( -1 defaut but you must limit it ), and you will reach this limit ! and if you have no limit or very large limit you have some risk of memory leak, and in extreme case OutofMemory errors. So if it's for your small personnal project used by 3 users it's not dramatic, but you don't want that if you're in a big company and that you're app is used by thousand people and million request.
Some reading.
IBM : Periodical OutOfMemory errors with prepared statement caching
It's simply a Java DESIGN MISTAKE tie "prepared statement" with "parameterized query / bind variables".
Databases does have API to accept "bind variables" in SQL code that just run once time.
It's a big resource wasting force use "prepared statement" everywhere, just to protect from SQL injection. Why not Java just let developers use databases in correct way?
It could be as follows:
Statement Interface - Multiples commands could be run. Not accept bind variables. One execution of SQL command. No SQL injection protection.
PreparedStatement Interface - One command could be run. Accept bind variables.
Multiple executions of SQL command. SQL injection protection.
(MISSING IN JAVA!) RunOnceStatement - One command could be run. Accept bind variables. One execution of SQL command. SQL injection protection.
For exemple, in Postgres performance could be better, by driver mapping to:
Statement Interface - PQExec()
PreparedStatement Interface - PQPrepare() / PQExecPrepare() / ...
(MISSING IN JAVA!) RunOnceStatement - PQExecParams()
Using prepared statement in SQL code that runs just once is a BIG performance problem: more processing in database, waste database memory, by maintaining plans that will not called later. Cache plans get so crowed that actual SQL commands that are executed multiple times could be deleted from cache.
Besides preventing SQL injection, formatting portability (which you can't get from Statement), performance is the obvious reason. However, PreparedStatement doesn't come without any penalty. For example, it is generally slower than Statement if running only once, as there is some overhead. So the general idea is PreparedStatement should be used when you are performing the same query many many times. However, how much overhead is very database server implementation-specific, so exactly when to choose PreparedStatement over Statement, from performance consideration, should really be based on your actual experience/experiments of a specific database server.
I was facing the same issue ,Then I break the query into multiple common table expression (cte) and now it's working fine for me.
SELECT DISTINCT
1 AS RecordSource, --CMD
EA.EmployerKey,
EA.AccountID,
ISNULL(EA.SourceGroupNumber,'NA') AS SourceGroupNumber,
EA.SourceSubGroupNumber,
EA.PurchaserOrgNumber,
ISNULL(EA.GroupName,'NA') AS GroupName,
EA.MemberSourceCode,
CASE WHEN ESA.EmailAddress = '' OR ESA.EmailAddress IS NULL THEN 'UNKNOWN#UNKNOWN.COM'
ELSE ESA.EmailAddress END AS EmailAddress,
CASE WHEN ESA.FirstName='' OR ESA.FirstName IS NULL THEN 'NA' ELSE ESA.FirstName END AS FirstName,
CASE WHEN ESA.MiddleName='' OR ESA.MiddleName IS NULL THEN 'NA' ELSE ESA.MiddleName END AS MiddleInitial,
CASE WHEN ESA.LastName='' OR ESA.LastName IS NULL THEN 'NA' ELSE ESA.LastName END AS LastName,
CASE WHEN ESA.ContactName='' OR ESA.ContactName IS NULL THEN 'NA' ELSE ESA.ContactName END AS ContactName,
ISNULL(ESA.PhoneNumber,'NA') As PhoneNumber,
'NA' AS MobilePhoneNumber,
'GROUP ADMINISTRATOR' AS ContactType,
ESA.StateCode
INTO DCB_A1.Temp_abc
FROM
(SELECT FE.EmployerKey, DE.AccountID, DE.SourceGroupNumber, DE.SourceSubGroupNumber,DE.PurchaserOrgNumber,
DE.GroupName, DE.MemberSourceCode
FROM DCB_A1.FctESF FE
INNER JOIN DCB_A1.DimESF DE
ON FE.EmployerKey = DE.EmployerKey
LEFT JOIN DCB_A1.DimMKBU DM
ON FE.MBUKey = DM.MarketingBusinessUnitKey
where DE.IsActiveVersion = 1 AND DE.PurchaserOrgTerminationReasonCode <> '171'
--and (MBUStateCoverage <> '' and MBUStateCoverage <> 'UNK')
GROUP BY FE.EmployerKey, DE.AccountID, DE.SourceGroupNumber,DE.SourceSubGroupNumber,DE.PurchaserOrgNumber,
DE.GroupName, DE.MemberSourceCode
)EA
INNER JOIN
(SELECT DE.AccountID,DE.SourceGroupNumber,
DE.MemberSourceCode,DE.GroupName,
ISNULL(DC.ContactValue,'') AS EmailAddress,
ISNULL(DN.ContactText2,'') AS FirstName,
ISNULL(DN.ContactText3,'') AS MiddleName,
ISNULL(DN.ContactText4,'') AS LastName,
ISNULL(DN.ContactValue,'') AS ContactName,
ISNULL(DP.ContactValue,'') AS PhoneNumber,
SUBSTRING(MBUStateCoverage, 1, 2) AS StateCode
FROM DCB_A1.FctESF FE
INNER JOIN DCB_A1.DimESF DE
ON FE.EmployerKey = DE.EmployerKey
LEFT JOIN DCA_A1.DimECSF DC
ON DE.MemberSourceCode = DC.MemberSourceCode
AND DE.PurchaserOrgNumber = DC.PurchaserOrgNumber
AND DE.PurchaserOrgTypeCode = DC.PurchaserOrgTypeCode
AND DC.ContactType = 3
--and DC.ContactValue <> '' -- Do we need this?
LEFT JOIN DCA_A1.DimECSF DN
ON DE.MemberSourceCode = DN.MemberSourceCode
AND DE.PurchaserOrgNumber = DN.PurchaserOrgNumber
AND DE.PurchaserOrgTypeCode = DN.PurchaserOrgTypeCode
AND DN.ContactType = 4
AND DN.IsActiveVersion = 1
LEFT JOIN DCA_A1.DimECSF DP
ON DE.MemberSourceCode = DP.MemberSourceCode
AND DE.PurchaserOrgNumber = DP.PurchaserOrgNumber
AND DE.PurchaserOrgTypeCode = DP.PurchaserOrgTypeCode
AND DP.ContactType = 1
LEFT JOIN DCB_A1.DimMKBU DM
ON FE.MBUKey = DM.MarketingBusinessUnitKey
WHERE DE.IsActiveVersion = 2
--and DE.AccountID =2
--AND (DE.CustomerStatusCode = 'A'
-- OR DE.IsEmployerActive = 0)
----and CustomerStatusCode = 'A'
--AND (MBUStateCoverage <> '' and MBUStateCoverage <> 'UNK')
GROUP BY DE.AccountID,DE.SourceGroupNumber,
DE.MemberSourceCode,DE.GroupName,
ISNULL(DC.ContactValue,''), ISNULL(DN.ContactValue,''),
ISNULL(DN.ContactText2,''),ISNULL(DN.ContactText3,''),
ISNULL(DN.ContactText4,''),ISNULL(DP.ContactValue,'') ,SUBSTRING(MBUStateCoverage, 1, 2)
)ESA
ON EA.AccountID = ESA.AccountID ;
[Amazon][JDBC](11220) Parameters cannot be used with normal Statement objects, use PreparedStatements instead.`
****Below check what I done with this code and it iss now working fine.****
with ESA AS
(SELECT DE.AccountID,DE.SourceGroupNumber,
DE.MemberSourceCode,DE.GroupName,
ISNULL(DC.ContactValue,'') AS EmailAddress,
ISNULL(DN.ContactText2,'') AS FirstName,
ISNULL(DN.ContactText3,'') AS MiddleName,
ISNULL(DN.ContactText4,'') AS LastName,
ISNULL(DN.ContactValue,'') AS ContactName,
ISNULL(DP.ContactValue,'') AS PhoneNumber,
SUBSTRING(MBUStateCoverage, 1, 2) AS StateCode
FROM DCB_A1.FctESF FE
INNER JOIN DCB_A1.DimESF DE
ON FE.EmployerKey = DE.EmployerKey
LEFT JOIN DCA_A1.DimECSF DC
ON DE.MemberSourceCode = DC.MemberSourceCode
AND DE.PurchaserOrgNumber = DC.PurchaserOrgNumber
AND DE.PurchaserOrgTypeCode = DC.PurchaserOrgTypeCode
AND DC.ContactType = 3
--and DC.ContactValue <> '' -- Do we need this?
LEFT JOIN DCA_A1.DimECSF DN
ON DE.MemberSourceCode = DN.MemberSourceCode
AND DE.PurchaserOrgNumber = DN.PurchaserOrgNumber
AND DE.PurchaserOrgTypeCode = DN.PurchaserOrgTypeCode
AND DN.ContactType = 4
AND DN.IsActiveVersion = 1
LEFT JOIN DCA_A1.DimECSF DP
ON DE.MemberSourceCode = DP.MemberSourceCode
AND DE.PurchaserOrgNumber = DP.PurchaserOrgNumber
AND DE.PurchaserOrgTypeCode = DP.PurchaserOrgTypeCode
AND DP.ContactType = 1
LEFT JOIN DCB_A1.DimMKBU DM
ON FE.MBUKey = DM.MarketingBusinessUnitKey
WHERE DE.IsActiveVersion = 2
--and DE.AccountID =2
--AND (DE.CustomerStatusCode = 'A'
-- OR DE.IsEmployerActive = 0)
----and CustomerStatusCode = 'A'
--AND (MBUStateCoverage <> '' and MBUStateCoverage <> 'UNK')
GROUP BY DE.AccountID,DE.SourceGroupNumber,
DE.MemberSourceCode,DE.GroupName,
ISNULL(DC.ContactValue,''), ISNULL(DN.ContactValue,''),
ISNULL(DN.ContactText2,''),ISNULL(DN.ContactText3,''),
ISNULL(DN.ContactText4,''),ISNULL(DP.ContactValue,'') ,SUBSTRING(MBUStateCoverage, 1, 2)
), EMK AS (
SELECT DISTINCT
1 AS RecordSource, --CMD
EA.EmployerKey,
EA.AccountID,
ISNULL(EA.SourceGroupNumber,'NA') AS SourceGroupNumber,
EA.SourceSubGroupNumber,
EA.PurchaserOrgNumber,
ISNULL(EA.GroupName,'NA') AS GroupName,
EA.MemberSourceCode
FROM
(SELECT FE.EmployerKey, DE.AccountID, DE.SourceGroupNumber, DE.SourceSubGroupNumber,DE.PurchaserOrgNumber,
DE.GroupName, DE.MemberSourceCode
FROM DCB_A1.FctESF FE
INNER JOIN DCB_A1.DimESF DE
ON FE.EmployerKey = DE.EmployerKey
LEFT JOIN DCB_A1.DimMKBU DM
ON FE.MBUKey = DM.MarketingBusinessUnitKey
where DE.IsActiveVersion = 1 AND DE.PurchaserOrgTerminationReasonCode <> '171'
--and (MBUStateCoverage <> '' and MBUStateCoverage <> 'UNK')
GROUP BY FE.EmployerKey, DE.AccountID, DE.SourceGroupNumber,DE.SourceSubGroupNumber,DE.PurchaserOrgNumber,
DE.GroupName, DE.MemberSourceCode)EA
)
Select Distinct
RecordSource
,EMK.EmployerKey
,EMK.AccountID
,EMK.SourceGroupNumber
,EMK.SourceSubGroupNumber
,EMK.PurchaserOrgNumber
,EMK.GroupName
,EMK.MemberSourceCode
,CASE WHEN ESA.EmailAddress = '' OR ESA.EmailAddress IS NULL THEN 'UNKNOWN#UNKNOWN.COM'
ELSE ESA.EmailAddress END AS EmailAddress
,CASE WHEN ESA.FirstName='' OR ESA.FirstName IS NULL THEN 'NA' ELSE ESA.FirstName END AS FirstName
,CASE WHEN ESA.MiddleName='' OR ESA.MiddleName IS NULL THEN 'NA' ELSE ESA.MiddleName END AS MiddleInitial
,CASE WHEN ESA.LastName='' OR ESA.LastName IS NULL THEN 'NA' ELSE ESA.LastName END AS LastName
,CASE WHEN ESA.ContactName='' OR ESA.ContactName IS NULL THEN 'NA' ELSE ESA.ContactName END AS ContactName
,ISNULL(ESA.PhoneNumber,'NA') As PhoneNumber
,'NA' AS MobilePhoneNumber
,'GROUP ADMINISTRATOR' AS ContactType
,ESA.StateCode
INTO DCB_A1.Temp_abc
FROM EMK
INNER JOIN ESA
ON EMK.AccountID = ESA.AccountID ;
Related
I have a requirement. The technology is quite old doesn't support spring at all . It is pure java application with jdbc connection.
Requirement is :
Suppose
select * from employee where empid = <<empid>> and designation = 'Doctor'
I am trying to replace <> with actual int value in java . How I can do it ?
String query = "select * from employee where empid = <<empid>> and designation = 'Doctor'";
if(query.contains("<<empid>>"))
/// Here I want to replace <<empid>> with actual int value in java
Any leads will be helpful
The code you didn't paste, that actually executes the SQL is either [A] a massive security leak that needs serious rewrites, or [B] is using PreparedStatement.
Here's the problem: SQL injection. Creating the SQL string by mixing a template or a bunch of string constants together with a bunch of user input is a security leak. For example, if you try to make SELECT * FROM users WHERE email = 'foo#bar.com' by e.g. String sql = "SELECT * FROM users WHERE email = '" + email + "'";, the problem is, what if the user puts in the web form, in the 'email' field: whatever#foo.com'; DROP TABLE users CASCADE; EXEC 'FORMAT C: /y /force'; --? Then the SQL becomes:
SELECT * FROM users WHERE email = 'whatever#foo.com'; DROP TABLE users CASCADE; EXEC 'FORMAT C: /y /force'; --';
That is legal SQL and you really, really, really don't want your DB engine to execute it.
Each DB engine has its own ideas on what's actually legal, and may do crazy things such as treating curly quotes as real quotes, etc. So, there is no feasible blacklist or whitelist technology you can think of that will properly cover all the bases: You need to ask your DB engine to do this for you, you can't fix this hole yourself.
Java supports this, via java.sql.PreparedStatement. You instead always pass a fully constant SQL string to the engine, and then fill in the blanks, so to speak:
PreparedStatement ps = con.prepareStatement("SELECT * FROM users WHERE email = ?");
ps.setString(1, "foo#whatever.com");
ps.query();
That's how you do it (and add try-with-resources just like you should already be doing here; statements and resultsets are resources you must always close). Even if you call .setString(1, "foo#whatever.com'; DROP TABLE users CASCADE; --"), then it'll simply look for a row in the database that has that mouthful in the email field. It will not delete the entire users table. Security hole eliminated (and this is the only feasible way to eliminate it).
So, check out that code. Is it using preparedstatement? In that case, well, one way or another that code needs to be calling:
ps.setInt(1, 999);
Where ps is the PreparedStatement object created with connection.prepareStatement(...) where ... is either an SQL constant or at least your input string where the <<empid>> was replaced with a question mark and never with any string input from an untrusted source. The 1 in ps.setInt(1, 999) is the position of the question mark (1 = the first question becomes 999), and the 999 is your actual number. It may look like:
if (input instanceof String) {
ps.setString(idx++, (String) input);
} else if (input instanceof Integer) {
ps.setInt(idx++, ((Integer) input).intValue());
} ...
etcetera. If you don't see that, find the setInt invoke and figure out how to get there. If you don't see any setInt, then what you want is not possible without making some updates to this code.
If you don't even see PreparedStatement anywhere in the code, oh dear! Take that server offline right now, research if a security leak has occurred, if this server stored european data you have 72 hours to notify all users if it has or you can't reasonably figure out e.g. by inspecting logs that it hasn't, or you're in breach of the GDPR. Then rewrite that part using PreparedStatement to solve the problem.
I'm confusing with implementation of CRUD methods for DAODatabase (for Oracle 11 xe).
The problem is that the "U"-method (update) in case of storing in generally to a Map collection inserts a new element or renews it (key-value data like ID:AbstractBusinessObject) in a Map collection. And you don't care about it, when you write something like myHashMap.add(element). This method (update) is widely used in project's business logic.
Obviously, in case of using Oracle I must care about both inserting and renewing of existing elements. But I'm stucked to choose the way how to implement it:
There is no intrinsic function for so-called UPSERT in Oracle (at least in xe11g r2 version). However, I can emulate necessary function by SQL-query like this:
INSERT INTO mytable (id1, t1)
SELECT 11, 'x1' FROM DUAL
WHERE NOT EXISTS (SELECT id1 FROM mytble WHERE id1 = 11);
UPDATE mytable SET t1 = 'x1' WHERE id1 = 11;
(src:http://stackoverflow.com/a/21310345/2938167)
By using this kind of query (first - insert, second - update) I presume that the data mostly will be inserted not updated (at least it will be rather rare). (May it be not optimal for concurrency?).
Ok, it is possible. But at this point I'm confusing to decide:
-- should I write an SQL function (with approriate arguments of course) for this and call it via Java
-- or should I simply handle a serie of queries for preparedStatements and do them via .executeUpdate/.executeQuery? Should I handle the whole UPSERT SQL code for one preparedStatment or split it into several SQL-queries and prepared statements inside one method's body? (I'm using Tomcat's pool of connections and I pass a connection instance via static method getConnection() to each method implementation in DAODatabase) ?
Is there another possibility to solve the UPSERT quest?
The equivalent to your UPSERT statement would seem to be to use MERGE:
MERGE INTO mytable d
USING ( SELECT 11 AS id, 'x1' AS t1 FROM DUAL ) s
ON ( d.id = s.id )
WHEN NOT MATCHED THEN
INSERT ( d.id, d.t1 ) VALUES ( s.id, s.t1 )
WHEN MATCHED THEN
UPDATE SET d.t1 = s.t1;
You could also use (or wrap in a procedure):
DECLARE
p_id MYTABLE.ID%TYPE := 11;
p_t1 MYTABLE.T1%TYPE := 'x1';
BEGIN
UPDATE mytable
SET t1 = p_t1
WHERE id = p_id;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO mytable ( id, t1 ) VALUES ( p_id, p_t1 );
END IF;
END;
/
However, when you are handling a CRUD request - if you are doing a Create action then it should be represented by an INSERT (and if something already exists then you ought to throw the equivalent of the HTTP status code 400 Bad Request or 409 Conflict, as appropriate) and if you are doing an Update action it should be represented by an UPDATE (and if nothing is there to update then return the equivalent error to 404 Not Found.
So, while MERGE fits your description I don't think it is representative of a RESTful action as you ought to be separating the actions to their appropriate end-points rather than combining then into a joint action.
So I would like to do a select from an SQL database, using Java, subject to a conditional statement (less than or equal to something) subject to some loop in Java. In other words, something like the following:
for (int i=0; i< 195; i++) {
// Get the results of the SQL query
resultSet = statement.executeQuery(
"SELECT max( millisFromMid ) FROM stockInfo1 WHERE ( millisFromMid <= 34200000 + (120000)*i ) GROUP BY stockID"
);
Now Java is returning an exception here, because it doesn't want me to use "i" in this condition; however, without being able to use it here, I'm not sure how I can change this condition. The problem is this: I'm looking to retrieve data from one database, with the intent of doing some manipulation and putting the results into a new database. These manipulations depend on taking the most recent data available, which is why I'm wanting to increase the number bounding millisFromMid. Does that make sense?
Does anyone have a suggestion on how someone might do something like this? This seems like a fundamental skill to have when using Java and SQL together, so I'd very much like to know it.
The SQL statement is parsed and run in a different environment than your Java code - different language, different scope, and unless you use SQLite they run in different processes or even different machines. Because of that, you can't just refer to the Java variable i from your SQL code - you have to either inject it or use special API.
The first option - injection - is to simply put the value of i inside the string:
"SELECT max( millisFromMid ) FROM stockInfo1 WHERE ( millisFromMid <= 34200000 + (120000)*"+i+" ) GROUP BY stockID"
Personally, I prefer to do it using String.format - but that's just me.
String.format("SELECT max( millisFromMid ) FROM stockInfo1 WHERE ( millisFromMid <= 34200000 + (120000)*%d ) GROUP BY stockID",i)
The second option - via API - is more complex but also faster(especially if you combine it with transactions) - using SQL parameters. You need to create a prepared statement:
PreparedStatement preparedStatement = connection.prepareStatement("SELECT max( millisFromMid ) FROM stockInfo1 WHERE ( millisFromMid <= 34200000 + (120000)*? ) GROUP BY stockID")
Notice the ? that is replacing the i - this is your parameter. You create prepareStatement once - before the loop, and inside the loop you set the parameter every time and execute it:
for (int i=0; i< 195; i++) {
preparedStatement.setInt(1,i); //Set the paramater to the current value of i.
resultSet = preparedStatement.executeQuery(); //Execute the statement - each time with different value of the parameter.
}
The problem os that the database is not told about the value of i. Instead make Java put the value into the string and then submit it to the database.
string sql = "select * from foo where bar=" + i;
You need to remember at all times that the database only knows what you explicitly tell it, and that you are essentially writing code at runtime that will execute on another host.
You can generate an sql string and call it via jdbc, using bind variable is more correct from performance, security. It also saves you from escaping any special characters.
Since you are learning about using sql with jdbc, please read about bind variables. An example is here
I have the following PreparedStatement:
PreparedStatement statement = conn.prepareStatement("Select * from foo
where foo.age ? ? AND foo.children ? ?")
Now to explain what I am looking to do, because I am lazy and don't like writing multiple queries. I want the statement to look like the following when finished:
Select * from foo where foo.age >= 42 AND foo.children <= 3
OR
Select * from foo where foo.age = 42 AND foo.children = 3
If it isn't clear I want to be able to substitute multiple tokens in a row, where the first token happens to be a qualifier (equals,greater,less,etc) and the token following it happens to be a literal (3,17,"Steve",etc). My question is is this possible and if so how can this be accomplished?
You can't do this, because ? doesn't represent a token, but rather a value. Obviously some tokens (namely literals) represent values, but even for these, the ? directly represents the value itself, not the literal-that-also-represents-the-value. (This is an intentional element of the design, because the very purpose of parameterized queries is to prevent parameters from "leaking out" and being interpreted as something other than single values.)
Edited to add: Where I work, we have a custom framework that wraps around JDBC and handles transactions and so on, so we don't usually have to deal with PreparedStatement directly. That framework has a method that looks something like this:
public <T> Iterator<T> executeQuery(ConverterFromResultSetToT<T> converter,
String query, Map<String, Object> params)
{
// . . . modify query, replacing any instances of $!{paramKey} with the
// corresponding value from params -- this allows arbitrary SQL code
// to be injected, in the rare cases that that's necessary
// . . . modify query, replacing any instances of ${paramKey} with '?' and
// adding the corresponding value from params to an array -- we use
// this much more often
// . . . create PreparedStatement with the resulting query
// . . . set parameters of PreparedStatement from aforemented array
// . . . run PreparedStatement; wrap result in an Iterator<T>; and return
}
But I'd only recommend that sort of thing if you expect to be doing a lot of this. We put a lot of effort into that framework, and it's incredibly useful, but it's also a lot of code.
It's worth noting that, despite what the documentation might imply, the cost of creating a PreparedStatement is not very high. Unless you're really running the same query a large number of times, it's not a big deal if you re-create the PreparedStatement each time. So you don't really need built-in support for drop-in operators, as long as you're willing to write your own code for that.
This cannot be done. The parameters in a PreparedSatement can only be values, not operators, table names and such like.
Now, for the specific queries above you can do the following:
select * from foo where age > ? and age < ?
Then with 42, 400 you get age>=42 years and with 42, 42 you get age = 42
You can have a workaround as :
String query = "Select * from foo where foo.age # # AND foo.children # #";
//write code here to manipulate your query string using
query = query.replaceFirst("#", "=");
query = query.replaceFirst("#", "42");
query = query.replaceFirst("#", "=");
query = query.replaceFirst("#", "3");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query );
If you decide to use replaceFirst as above, please be aware that you are assigning the value from left to right.
String firstOperator = ">="
String secondOperator = "<="
PreparedStatement statement = conn.prepareStatement("Select * from foo
where foo.age "+firstOperator+" ? AND foo.children "+secondOperator+" ?");
statement.setInt(1,42);
statement.setInt(2,3);
Anyway I don't think it's a very elegant thing to do. "Not writing multiple queries" doesn't seem to be a sensible design goal.
I'm having some trouble executing a nested sql query. It works fine when i run it on TOAD (for Oracle DB) but when I try and execute the same query from my java program it gives me an error : ORA-00923: FROM keyword not found where expected:
Here is the query:
Select *
from(select tb1.Number, tb1.Desc, tb1.Local, tb2.Division
from tb1
inner join tb2
on tb1.Number = tb2.Number
and Local in ('ENG_USA', 'ENG_CHINA', 'FR_FRENCH'))
where Number in ('7512','4706')
Like I mentioned, it executes fine on Toad and is able to retrieve data but the same query throws the ORA-00923 exception.
rset = stmt.executeQuery(Select *
from(select tb1.Number, tb1.Desc, tb1.Local, tb2.Division
from tb1
inner join tb2
on tb1.Number = tb2.Number
and Local in ("+loc+"))
where Number in ("+s+")
Reply With Quote
I had a similar issue with mysql. My problem was that i splitted the query in multiple rows without leaving a space between 2 rows.
So your query should be:
rset = stmt.executeQuery("Select * from(select tb1.Number, tb1.Desc, tb1.Local, tb2.Division from tb1 inner join tb2 on tb1.Number = tb2.Number and Local in ("+loc+")) where Number in ("+s+")"
PS: I would like to write this as a comment but for some reason i can't in this question. Sorry!
First off, I don't see the beginning or ending double-quotes in your Java code. Since your Java code must compile in order to get far enough to receive an Oracle error, I'm assuming that there is a double quote prior to the SELECT, a double-quote after the final close paren, and then another close parent to match to the open paren on the executeQuery. So I'm assuming that your actual code is something like
rset = stmt.executeQuery(
"Select *
from(select tb1.Number, tb1.Desc, tb1.Local, tb2.Division
from tb1
inner join tb2
on tb1.Number = tb2.Number
and Local in ("+loc+"))
where Number in ("+s+")");
Is there a reason that you're building the SQL statement dynamically rather than using bind variables? Using bind variables is more secure since you don't need to worry about SQL injection attacks. It is more efficient since you're not forcing the database to hard parse the query every time. And it saves you from many sorts of errors around quoting and escaping. In your case, assuming that the local variable loc does not have the leading and trailing single quotes, you'd need to include those in your query
rset = stmt.executeQuery(
"Select *
from(select tb1.Number, tb1.Desc, tb1.Local, tb2.Division
from tb1
inner join tb2
on tb1.Number = tb2.Number
and Local in ('"+loc+"'))
where Number in ("+s+")");
Had you used bind variables and just bound loc to the first bind variable, on the other hand, you wouldn't need to worry about adding the single quotes (or escaping any single quotes in loc.