How to externalize sql statement with dynamic parameter substitution? - java

I'd like to provide a REST webservice for database actions. The SQL statements should be externalized in local files (not inside the code).
It would be nice to also define the parameter types inside the external files.
Normally a statement would be written as:
String sql = "SELECT * from user_table WHERE id=?";
PreparedStatement stm = c.prepareStatement(sql);
stm.setInt(1, 345);
//stm.setString(2, "username");
stm.executeUpdate();
Problem: if I'd just import the sql statement from file, I have to know the parameter types beforehand (and also the number of parameters). Thus have to more or less hardcode the statement fetching.
But I'd like to be able to replace the sql statement at runtime (and eg add or remove parameters) without having to touch the java code. So I could change the sql without having to restart the service.
Like (pseudocode):
<definition>
<sql id="getUsers">
<statement>SELECT * from user_table WHERE id=? and name=?</statement>
<param idx="1" type="Integer"/>
<param idx="2" type="String"/>
</sql>
</definition>
And then just feed the sql statement with parameters from a Map.
Question: is there any existing framework that provides such a mechanism for templating the sql in external files. And feed the sql statements with parameters from a HashMap (that might eg be received via POST dynamically from a webservice)?
Sidenote: the sql in the file must be native sql. I cannot make use of eg HSQL or similar.
Sidenote2: I'm not looking for an ORM, I know there is hibernate to map between database and java beans. But I'm not interested in mapping the sql result to beans. My focus is to have the ability to interchange the sql statements dynamically without having to recompile.

Related

What is the SQL equivalent of CallableStatement?

I have some code I'm trying to fix and a function is used within a CallableStatement but it seems that said function is called in a particular instance where a Schema of the Database becomes the entire 'see-able' database for the function.
Let me explain :
My database (in PostgreSQL) has multiple projects (schemas) such as i2b2test_project or i2b2test_project2. Each of these projects has an architecture of multiple tables, such as visit_dimension, patient_dimension, etc..
Now, if I want to reference the visit_dimension table from a i2b2test_project, I need to use i2b2test_project.visit_dimension, as any logical SQL syntax would suggest.
The problem is that, in the code (which I'll include below), a CallableStatement is used to execute a function (plain old load-table-from-temp-table function). This function references the architecture mentioned above as if only one project exists, as the database. In other words, instead of referencing i2b2test_project.visit_dimension, it only references visit_dimension.
I haven't found a way to execute a function in some sort of 'separated instance' of the database and as a result, the function can not be used in a plain SQL statement inside a DB terminal, errors such as visit_dimension table does not exist etc..
So I ask : Does the call statement (which indeed seems to reference the schema) allow for such a 'separated instance' of the database ? And is there a way to do so with a plain SQL statement ?
Code :
CallableStatement callStmt = conn.prepareCall("{call "
+ this.getDbSchemaName()
+ "INSERT_ENCOUNTERVISIT_FROMTEMP(?,?,?)}");
callStmt.setString(1, tempTableName);
callStmt.setInt(2, uploadId);
callStmt.registerOutParameter(3, java.sql.Types.VARCHAR);
callStmt.execute();

How to externalize an insert file SQL query

In my application, I want to externalize SQL query (in .properties files for example). But sometimes I had to insert the entire content of a text file into a CLOB column.
This is the code I use now:
String requete = "the content of the file in xml";
PreparedStatement prepareStatement = con.prepareStatement("INSERT INTO \"TABLE\".\"_XML\" (ID, BLOC_XML) VALUES ('1',?)");
prepareStatement.setCharacterStream(1, new StringReader(requete), requete.length());
I really need to decouple the SQL logic from the application business logic. Any suggestions to tackle this problem.
Thanks.
It's not a good idea to externalize the SQL queries. Imagine that someone would change the .properties file to something like
drop table really_important_stuff;
Furthermore, as a developer I would prefer to have the SQL queries as close to the source code as possible. So that I do not need to look them up in some other resource.
It's simple to gain control of the complexity using DAO pattern for example.

Does Hibernate's createCriteria() sanitize input?

Came across some code today that uses Hibernate to perform a query. The query uses a value submitted from a form. It made me curious as to whether or not this sort of code "sanitizes" its input.
public List<School> search(String query) {
Session session = this.getCurrentSession();
query = "%" + query + "%";
Criteria criteria = session.createCriteria(getPersistentClass());
criteria.createAlias("country", "a");
Criterion nameCriterion = Restrictions.ilike("name", query);
Criterion cityCriterion = Restrictions.ilike("city", query);
Criterion countryCriterion = Restrictions.ilike("a.name", query);
Criterion criterion = Restrictions.or(Restrictions.or(nameCriterion, cityCriterion), countryCriterion);
criteria.add(criterion);
return criteria.list();
}
Is this safe?
Hibernate Criteria Queries are quiet safe in terms of Sql Injection since they pass strings as parameter while performing any fetch. Even, Hql is quiet safe unless you build the query via string literal.
For more details, you should take a look at queries getting fired at the database level by switching on hibernate sql logging.
If you think to SQL injection attacks, then yes, Hibernate Criteria API is safe.
It will generate the underlying query by first compiling it from the specified query fields and only after apply the query parameters (It should use a classical PreparedStatement). This way the JDBC driver will know which part of the query are fields and which part are parameters. Then the driver will take care to sanitize the parameters.
Tough you should take care with the SQL restrictions applied on the Criteria, if you need to place parameters there. For example
String vulnerable = //parameter from user interface
criteria.add(
Restrictions.sqlRestriction("some sql like + vulnerable") //vulnerable
criteria.add(
Restrictions.sqlRestriction("some sql like ?",
vulnerable, Hibernate.STRING)) //safe
In this case the vulnerable parameter could "leak" in to the query fields part and be bypassed by JDBC driver checking as in a normal vulnerable SQL query.
Hibernate is useful to sanitizing inputs but sanitizing inputs is not considered the best practice for preventing SQL injection attacks. As your code develops over time, you will need to remember to change your Hibernate sanitation as your database and client-side application change; this leaves a lot of room for error and any one mistake can compromise your database.
To prevent SQL injection attacks, it is better to use prepared statements. In a prepared statement, your client-side application will make a non-SQL request and let your server generate your SQL statement.
For example, if a user wants all users in the city "Dallas" then your client-side application should make a request similar to username equals "Dallas" and then your server can generate:
SELECT * FROM users WHERE name='Dallas'

Jasper Reports Fill Parameters in SQL Query

I have a web application which generates reports based off an SQL query. These SQL queries have Jasper Report parameters (i.e. $P{Param}).
In my Java code, I'm using PreparedStatement to execute the query and return a result set. I use this result set and change it into a JRResultSetDataSource to pass into JasperFillManager.fillReport(JasperReport, parameters, dataSource).
The reason I'm using a data source and not a connection is so that I can use setQueryTimeout on my PreparedStatement.
My problem is that I need a way to fill in the query's parameters with the parameter map values. Is there a built in way to do this?
Ex.
rawSqlString = "SELECT * FROM TABLE WHERE ROW1 = $P{Param}";
filledSqlString = somefunction(sqlString);
ResultSet rs = sqlStatement.executeQuery(filledSqlString);
I can't use the "rawSqlString" since it has $P{Param}.
Alternatively, is there a type of datasource which simply stores the unfilled SQL query which I can pass to JasperFillManager?
Normally JasperFillManager handles this but I want my query to timeout, so I need to use setQueryTimeout, and somehow convert this into a format Jasper can handle.

Error with simple Parameterized Query - Java/ SQL

Following on from one of my previous questions to do with method design I was advised to implemented my SQL queries as a parameterized query as opposed to a simple string.
I've never used parameterized queries before so I decided to start with something simple, take the following Select statement:
String select = "SELECT * FROM ? ";
PreparedStatement ps = connection.prepareStatement(select);
ps.setString(1, "person");
This gives me the following error: "[SQLITE_ERROR] SQL error or missing database (near "?": syntax error)"
I then tried a modified version which has additional criteria;
String select = "SELECT id FROM person WHERE name = ? ";
PreparedStatement ps = connection.prepareStatement(select);
ps.setString(1, "Yui");
This version works fine, in the my first example am I missing the point of parameterized queries or am I constructing them incorrectly?
Thanks!
Simply put, SQL binds can't bind tables, only where clause values. There are some under-the-hood technical reasons for this related to "compiling" prepared SQL statements. In general, parameterized queries was designed to make SQL more secure by preventing SQL injection and it had a side benefit of making queries more "modular" as well but not to the extent of being able to dynamically set a table name (since it's assumed you already know what the table is going to be).
If you want all rows from PERSON table, here is what you should do:
String select = "SELECT * FROM person";
PreparedStatement ps = connection.prepareStatement(select);
Variable binding does not dynamically bind table names as others mentioned above.
If you have the table name coming in to your method as a variable, you may construct the whole query as below:
String select = "SELECT * FROM " + varTableName;
PreparedStatement ps = connection.prepareStatement(select);
Parameterized queries are for querying field names - not the table name!
Prepared statements are still SQL and need to be constructed with the appropriate where clause; i.e. where x = y. One of their advantages is they are parsed by the RDMS when first seen, rather than every time they are sent, which speeds up subsequent executions of the same query with different bind values.

Categories

Resources