From poking around in PreparedStatement, it appears that parameterizing SQL statements only allows the developer to specify positional arguments using ? and PreparedStatement.setX(index, value):
PreparedStatement statement = connection.prepareStatement("select * from something where id = ?;");
statement.setString(1, '5');
Is there a way to supply named parameters to prepared statements like this:
ParameterizedStatement statement = connection.parameterizeStatement(
"select * from something where id = $id;");
statement.setString("id", "5");
Does something exist for this in Java?
Not in the base JDK, but this sounds like the MyBatis SQL Builder Class.
For example,
// Anonymous inner class
public String deletePersonSql() {
return new SQL() {{
DELETE_FROM("USER");
WHERE("ID = ${id}");
}}.toString();
}
// Builder / Fluent style
public String insertPersonSql() {
String sql = new SQL()
.INSERT_INTO("USER");
.VALUES("ID, FIRST_NAME", "${id}, ${firstName}")
.VALUES("LAST_NAME", "${lastName}")
.toString();
return sql;
}
JPA queries can use named parameters, example:
EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x WHERE x.title = :titleParam and x.price > :priceParam");
q.setParameter("titleParam", "JDJ");
q.setParameter("priceParam", 5.0);
List<Magazine> results = (List<Magazine>) q.getResultList();
Related
I want to execute a simple native query, but it does not work:
#Autowired
private EntityManager em;
Query q = em.createNativeQuery("SELECT count(*) FROM mytable where username = :username");
em.setProperty("username", "test");
(int) q.getSingleResult();
Why am I getting this exception?
org.hibernate.QueryException: Not all named parameters have been set: [username]
Named parameters are not supported by JPA in native queries, only for JPQL. You must use positional parameters.
Named parameters follow the rules for identifiers defined in Section 4.4.1. The use of named parameters applies to the Java Persistence query language, and is not defined for native queries. Only positional parameter binding may be portably used for native queries.
So, use this
Query q = em.createNativeQuery("SELECT count(*) FROM mytable where username = ?1");
q.setParameter(1, "test");
While JPA specification doesn't support named parameters in native queries, some JPA implementations (like Hibernate) may support it
Native SQL queries support positional as well as named parameters
However, this couples your application to specific JPA implementation, and thus makes it unportable.
After many tries I found that you should use createNativeQuery And you can send parameters using # replacement
In my example
String UPDATE_lOGIN_TABLE_QUERY = "UPDATE OMFX.USER_LOGIN SET LOGOUT_TIME = SYSDATE WHERE LOGIN_ID = #loginId AND USER_ID = #userId";
Query query = em.createNativeQuery(logQuery);
query.setParameter("userId", logDataDto.getUserId());
query.setParameter("loginId", logDataDto.getLoginId());
query.executeUpdate();
You are calling setProperty instead of setParameter. Change your code to
Query q = em.createNativeQuery("SELECT count(*) FROM mytable where username = :username");
em.setParameter("username", "test");
(int) q.getSingleResult();
and it should work.
I use EclipseLink. This JPA allows the following way for the native queries:
Query q = em.createNativeQuery("SELECT * FROM mytable where username = ?username");
q.setParameter("username", "test");
q.getResultList();
Use set Parameter from query.
Query q = (Query) em.createNativeQuery("SELECT count(*) FROM mytable where username = ?1");
q.setParameter(1, "test");
This was a bug fixed in version 4.3.11
https://hibernate.atlassian.net/browse/HHH-2851
EDIT:
Best way to execute a native query is still to use NamedParameterJdbcTemplate
It allows you need to retrieve a result that is not a managed entity ; you can use a RowMapper and even a Map of named parameters!
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
final List<Long> resultList = namedParameterJdbcTemplate.query(query,
mapOfNamedParamters,
new RowMapper<Long>() {
#Override
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getLong(1);
}
});
I get following error: "com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'Card.findPrefix'"
for:
#NamedNativeQueries({
#NamedNativeQuery(name = "Card.findPrefix",
query = "SELECT DISTINCT(FLOOR(c
.number/10000)) FROM Card c")
})
public List<Integer> findPrefix(){
Query q = em.createNativeQuery("Card.findPrefix");
try{
return q.getResultList();
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
I can not understand where is my mistake, because when I type this query directly to Mysql it works perfectly.
Use should use createNamedQuery, instead of createNativeQuery.
From EntityManager JavaDoc
createNativeQuery(String sqlString)
Create an instance of Query for executing a native SQL statement, e.g., for update or delete.
createNamedQuery(String name)
Create an instance of Query for executing a named query (in the Java Persistence query language or in native SQL).
So in your code you are executing query name 'Find....' as SQL query.
The right one:
Query q = em.createNamedQuery("Card.findPrefix");
Final code is:
public List<Integer> findPrefix(){
Query q = em.createNamedQuery("Card.findPrefix");
List<Integer> res = new ArrayList<Integer>();
for(Object row : (List<Object>) q.getResultList()){
res.add(((BigInteger)row).intValue());
}
return res;
}
#NamedNativeQueries({
#NamedNativeQuery(name = "Card.findPrefix",
query = "SELECT DISTINCT(FLOOR(c.number/10000)) FROM Card c")
})
To avoid SQL injection attacks in my project, I'm attempting access database with Parameterized Query way. Right now I know how to handle equal case like below (With Spring JdbcTemplate):
String sql = "SELECT * FROM T_USER WHERE USERNAME = ? AND PASSWORD = ?"
jdbcTemplate.query(sql,
new UserRowMapper(),
new Object[]{"%admin%", "%password%"});
Above code runs no problem, but I had no idea how to handle the 'IN' case, following is my case, and it works failed:
String sql =
"SELECT * FROM T_USER WHERE USERNAME = ? AND PASSWORD = ? AND CLASS_ID IN (?)"
jdbcTemplate.query(sql,
new UserRowMapper(),
new Object[]{"%admin%", "%password%", "1,2,3"});
Anybody give me guidance? Thanks a lot.
I think you can create a List and pass it as 3rd parameter. Also You need to use LIKE in place of = in first two column filters.
List<Integer> classIds = new ArrayList<Integer>();
classIds.add(1);
classIds.add(2);
classIds.add(3);
String sql = "SELECT * FROM T_USER WHERE "+
"USERNAME LIKE ? AND PASSWORD LIKE ? AND CLASS_ID IN (?)";
jdbcTemplate.query(sql, new Object[]{"%admin%", "%password%", classIds},
new UserRowMapper());
Please note: Here is the syntax:
public List query(String sql, Object[] args, RowMapper rowMapper)
throws DataAccessException
EDIT: Please try namedParameterJdbcTemplate as bwlow:
String sql = "SELECT * FROM T_USER WHERE "+
"USERNAME LIKE :uname AND PASSWORD LIKE :passwd AND CLASS_ID IN (:ids)";
Map<String, Object> namedParameters = new HashMap<String, Object>();
namedParameters.put("uname", "%admin%);
namedParameters.put("passwd", "%password%");
namedParameters.put("ids", classIds);
List result = namedParameterJdbcTemplate.query(sql, namedParameters,
new UserRowMapper());
Three options:
Generate different JDBC queries for each length of the IN LIST, and parameterize each INDIVIDUAL item, e.g. this answer
For small tables, you can cheat and use a LIKE statement, e.g. this answer
Use a SPLIT function (anti-LISTAGG) to turn the delimited list into individual rows of one column each, and JOIN against it. Example SPLIT function
You'll parameterize the argument to the function as a single string
I am trying to implement PreparedStatement, which won't work with sql DB.
Suppose I have the following sql query:
String selectSqlQuery = "SELECT * FROM customer WHERE f1 = ? AND f2 =? AND f3 > ?";
and the following code:
//----
prest = con.prepareStatement(selectSqlQuery );
prest.setString(1, "val1");
prest.setString(2, "val2");
prest.setInt(3, 108);
ResultSet rs = prest.executeQuery();
//---
My question is how to implement setString and setInt methods for injecting params?
For now I save parameters' indexes and values into HashMap, but after it I can't make injection into sql query string.
implementation of sql's java interfaces are part of vendor specific jdbc driver. You probably just need to get the proper jdbc jar file for you database. writing implementations of such stuff is usually just needed if you intend to write your own database driver...
Since you're writing your own driver, you can play with your class a little. Let's change the approach. If you have a query like this one:
"SELECT * FROM table WHERE id = ? AND name = ?"
Replace the ? to turn it into
"SELECT * FROM table WHERE id = {0} AND name = {1}"
About your set methods, those will have to save your new parameters in an Object array, again matching against the index.
Object parameterArray = new Object[1];
public boolean setString(int paramIndex, String param) {
if(paramIndex < 0 || paramIndex > parameterArray.length)
throw new IllegalArgumentException("Can't set parameter " + paramIndex + ", The query only has " + parameterArray.length + " parameters.");
parameterArray[paramIndex - 1] = param;
}
Before executing the query, take advantage of your formatted string and set the parameters:
MessageFormat messageFormat = new MessageFormat(query);
String newQuery = messageFormat.format(parameterArray);
The format method will replace the {number} substrings for the corresponding element in the index represented by the number between brackets.
I was wondering if there is a more elegant way to do IN() queries with Spring's JDBCTemplate. Currently I do something like that:
StringBuilder jobTypeInClauseBuilder = new StringBuilder();
for(int i = 0; i < jobTypes.length; i++) {
Type jobType = jobTypes[i];
if(i != 0) {
jobTypeInClauseBuilder.append(',');
}
jobTypeInClauseBuilder.append(jobType.convert());
}
Which is quite painful since if I have nine lines just for building the clause for the IN() query. I would like to have something like the parameter substitution of prepared statements
You want a parameter source:
Set<Integer> ids = ...;
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("ids", ids);
List<Foo> foo = getJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",
parameters, getRowMapper());
This only works if getJdbcTemplate() returns an instance of type NamedParameterJdbcTemplate
I do the "in clause" query with spring jdbc like this:
String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid IN (:goodsid)";
List ids = Arrays.asList(new Integer[]{12496,12497,12498,12499});
Map<String, List> paramMap = Collections.singletonMap("goodsid", ids);
NamedParameterJdbcTemplate template =
new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
List<Long> list = template.queryForList(sql, paramMap, Long.class);
If you get an exception for : Invalid column type
Please use getNamedParameterJdbcTemplate() instead of getJdbcTemplate()
List<Foo> foo = getNamedParameterJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",parameters,
getRowMapper());
Note that the second two arguments are swapped around.
Refer to here
write query with named parameter, use simple ListPreparedStatementSetter with all parameters in sequence. Just add below snippet to convert the query in traditional form based to available parameters,
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(namedSql);
List<Integer> parameters = new ArrayList<Integer>();
for (A a : paramBeans)
parameters.add(a.getId());
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("placeholder1", parameters);
// create SQL with ?'s
String sql = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource);
return sql;
Many things changed since 2009, but I can only find answers saying you need to use NamedParametersJDBCTemplate.
For me it works if I just do a
db.query(sql, new MyRowMapper(), StringUtils.join(listeParamsForInClause, ","));
using SimpleJDBCTemplate or JDBCTemplate