My application dynamically creates tables, and I don't know how to read a table using Spring jdbc without hard coding it into the string query. I was thinking about something like this:
jdbcTemplate.query("SELECT * FROM ?", new Object[] { tableName }, new TableMapper());
But spring doesn't like the question mark :-(
Thanks for any help!
Try String query = String.format("SELECT * FROM %s", tableName);
A word of explanation: Spring is not the one to blame here, the exception is thrown all the way from your DB driver. PreparedStatement doesn't allow parametrising the table name, you can apply the ? to query parameters only. As noted in the other answer, the only way to go around this is to insert it into the query string.
Related
I'm trying to make a N1QL based query on Spring Data Couchbase. The documentation says
#n1ql.fields will be replaced by the list of fields (eg. for a SELECT clause) necessary to reconstruct the entity.
My repository implementation is this one:
#Query("#{#n1ql.fields} WHERE #{#n1ql.filter}")
List<User> findAllByFields(String fields);
And I'm calling this query as follows:
this.userRepository.findAllByFields("SELECT firstName FROM default");
I'm getting this error:
Caused by: org.springframework.data.couchbase.core.CouchbaseQueryExecutionException: Unable to execute query due to the following n1ql errors:
{"msg":"syntax error - at AS","code":3000}
After a little bit of researching, I also tryed:
#Query("SELECT #{#n1ql.fields} FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
With this query, I don't get an error, I get all the documents stored but only the ID the other fields are set to null, when my query tries to get the firstName field.
this.userRepository.findAllByFields("firstName");
Anyone knows how to do such a query?
Thank you in advance.
You're misunderstanding the concept, I encourage you to give the documentation more time and see more examples. I'm not sure what exactly you're trying to achieve but I'll throw some examples.
Find all users (with all of their stored data)
#Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
List<User> findAllUsers();
This will basically generate SELECT meta().id,_cas,* FROM bucket WHERE type='com.example.User'
Notice findAllUsers() does not take any parameters because there are no param placeholders defined in the #Query above.
Find all users where firstName like
#Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND firstName like $1")
List<User> findByFirstNameLike(String keyword);
This will generate something like the above query but with an extra where condition firstName like
Notice this method takes a keyword because there is a param placeholder defined $1.
Notice in the documentation it says
#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND test = $1
is equivalent to
SELECT #{#n1ql.fields} FROM #{#n1ql.bucket} WHERE
#{#n1ql.filter} AND test = $1
Now if you don't want to fetch all the data for user(s), you'll need to specify the fields being selected, read following links for more info
How to fetch a field from document using n1ql with spring-data-couchbase
https://docs.spring.io/spring-data/couchbase/docs/2.2.4.RELEASE/reference/html/#_dto_projections
I think you should try below query, that should resolve the issue to get fields based parameter you have sent as arguments.
Please refer blow query.
#Query("SELECT $1 FROM #{#n1q1.bucket} WHERE #{#n1ql.filter}")
List findByFirstName(String fieldName);
Here, bucket name resolve to the User entity and and n1ql.filter would be a default filter.
With mysql, there is a column used json data type. i want to execute query with the column as condition. With Sql, i can write it like bellow.
select * from user where json_extract(address,'$.city')= 'beijing'
But using spring-data-jpa, i don't know how to implement it. JPQL have no provided some api implement json_extract. And the JpaSpecificationExecutor of jpa have not provide same function too. If someone know how to do it, please answer this question.
Try this:
#Query(value = "select * from user where json_extract(address,'$.city') = '?city'", nativeQuery = true)
List<User> findUsersByCity(String city);
I didn't check it.
Good luck
I have some code that translates a user's search word into a MySQL query:
String sql = String.format("SELECT * FROM my_table WHERE my_column = '%s'", value);
HibernateUtil.getCurrentSession().createSQLQuery(sql);
To test if I was protected—and because I think it would be fun to learn in this way—I wanted to try to SQL inject my own application. So I searched for:
x'; DROP TABLE test;--
which results in the following query:
SELECT * FROM my_table WHERE my_column = 'x'; DROP TABLE test;--
But Hibernate throws a SQLGrammarException. When I run this code via phpMyAdmin, it correctly drops the test table.
How is Hibernate validating my SQL? Perhaps more importantly—is this protecting me against SQL injection or should I be using setParameter. If it's not protecting me, can I have an example of some SQL that will perform the injection. I think it would be fun to actually verify.
You are protected against execution of more than one statement because createSQLQuery allows exactly one statement. It is not Hibernate which protects you here, but your JDBC driver respectively your database - because it does not know how to handle the separator ; in the context of a single statement.
But you are still not safe against SQL injection. There are plenty of other possibilities to inject SQL in that case. Just one example:
Imagine you are searching for some user specific items in your query:
String sql = String.format(
"SELECT * FROM my_table WHERE userId = %s AND my_column = '%s'",
currentUserId, value);
The user can now enter:
x' OR '1' = '1
Which will lead to the SQL:
SELECT * FROM my_table WHERE userId = 1234 AND my_column = 'x' OR '1' = '1'
And because AND has higher precedence, he will see all items - even those of other users.
Even your provided example can be dangerous, for example
x' OR (SELECT userName FROM USER) = 'gwg
will let me know if your database contains a user that is called gwg (assuming that I know your database layout, which I could find out with similar queries).
According to Hibermate documentation, the method createSQLQuery "Create a new instance of Query for the given SQL string", so we can assume that Hibernate do the SQL checking for a single query on every call of this method.
Important: createSQLQuery is deprecated on Hibernate, please check out the link given above to see newers ways to execute SQL queries.
Now, speaking about how you could protect yourself from SQL injection, the best way to do it is using parameters on your query.
This question has the exactly example you are in need for, please check it out.
Hope this help you in your studies, good luck!
I have a scenario where I need to parse flat files and process those records into mysql database inserts (schema already exists).
I'm using the FlatFileItemReader to parse the files and a JdbcCursorItemWriter to insert in the database.
I'm also using an ItemProcessor to convert any column values or skip records that I don't want.
My problem is, some of those inserts need to have a foreign key to some other table that already has data into it.
So I was thinking to do a select to retrieve the ID and update the pojo, inside the ItemProcessor logic.
Is this the best way to do it? I can consider alternatives as I'm just beginning to write all this.
Thanks!
The ItemProcessor in a Spring Batch step is commonly used for enrichment of data and querying a db for something like that is common.
For the record, another option would be to use a sub select in your insert statement to get the foreign key value as the record is being inserted. This may be a bit more performant give it removes the additional db hit.
for the batch process - if you require any where you can call use below method anywhere in batch using your batch listeners
well the below piece of code which I wrote , worked for me --
In you Main class - load your application context in a static variable - APP_CONTEXT
If you are not using XML based approach - then get the dataSource by auto-wiring it and then you can use below code -
Connection conn = null;
PreparedStatement pstmt= null;
try {
DataSource dataSource = (DataSource) Main.APP_CONTEXT
.getBean("dataSource");
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(" your SQL query to insert ");
pstmtMstr.executeQuery();
} catch (Exception e) {
}finally{
if(pstmt!=null){
pstmt.close();
}if(conn!=null){
conn.close();
}
}
I'm using Hibernate but doing a simple SQLQuery, so I think this boils down to a basic JDBC question. My production app runs on MySQL but my test cases use an in memory HSQLDB. I find that a SELECT COUNT operation returns BigInteger from MySQL but Long from HSQLDB.
MySQL 5.5.22
HSQLDB 2.2.5
The code I've come up with is:
SQLQuery tq = session.createSQLQuery(
"SELECT COUNT(*) AS count FROM calendar_month WHERE date = :date");
tq.setDate("date", eachDate);
Object countobj = tq.list().get(0);
int count = (countobj instanceof BigInteger) ?
((BigInteger)countobj).intValue() : ((Long)countobj).intValue();
This problem of the return type negates answers to other SO questions such as getting count(*) using createSQLQuery in hibernate? where the advice is to use setResultTransformer to map the return value into a bean. The bean must have a type of either BigInteger or Long, and fails if the type is not correct.
I'm reluctant to use a cast operator on the 'COUNT(*) AS count' portion of my SQL for fear of database interoperability. I realise I'm already using createSQLQuery so I'm already stepping outside the bounds of Hibernates attempts at database neutrality, but having had trouble before with the differences between MySQL and HSQLDB in terms of database constraints
Any advice?
I don't known a clear solution for this problem, but I will suggest you to use H2 database for your tests.
H2 database has a feature that you can connect using a compatibility mode to several different databases.
For example to use MySQL mode you connect to the database using this jdbc:h2:~/test;MODE=MySQL URL.
You can downcast to Number and then call the intValue() method. E.g.
SQLQuery tq = session.createSQLQuery("SELECT COUNT(*) AS count FROM calendar_month WHERE date = :date");
tq.setDate("date", eachDate);
Object countobj = tq.list().get(0);
int count = ((Number) countobj).intValue();
Two ideas:
You can get result value as String and then parse it to Long or BigInteger
Do not use COUNT(*) AS count FROM ..., better use something like COUNT(*) AS cnt ... but in your example code you do not use name of result column but it index, so you can use simply COUNT(*) FROM ...