I'm using Spring's JdbcTemplate and running a query like this:
SELECT COLNAME FROM TABLEA GROUP BY COLNAME
There are no named parameters being passed, however, column name, COLNAME, will be passed by the user.
Questions
Is there a way to have placeholders, like ? for column names? For example SELECT ? FROM TABLEA GROUP BY ?
If I want to simply run the above query and get a List<String> what is the best way?
Currently I'm doing:
List<Map<String, Object>> data = getJdbcTemplate().queryForList(query);
for (Map m : data) {
System.out.println(m.get("COLNAME"));
}
To populate a List of String, you need not use custom row mapper. Implement it using queryForList.
List<String>data=jdbcTemplate.queryForList(query,String.class)
Use following code
List data = getJdbcTemplate().queryForList(query,String.class)
Is there a way to have placeholders, like ? for column names? For example SELECT ? FROM TABLEA GROUP BY ?
Use dynamic query as below:
String queryString = "SELECT "+ colName+ " FROM TABLEA GROUP BY "+ colName;
If I want to simply run the above query and get a List what is the best way?
List<String> data = getJdbcTemplate().query(query, new RowMapper<String>(){
public String mapRow(ResultSet rs, int rowNum)
throws SQLException {
return rs.getString(1);
}
});
EDIT: To Stop SQL Injection, check for non word characters in the colName as :
Pattern pattern = Pattern.compile("\\W");
if(pattern.matcher(str).find()){
//throw exception as invalid column name
}
You can't use placeholders for column names, table names, data type names, or basically anything that isn't data.
Related
I have a complex SQL query which may have different number of parameters in where section. So, query sql should be constructed manually. As a result, I get array of objects which contains 45 fields, each of them I will have to case, convert, etc. I can't use result set mapping because it requires a stable SQL which I should specify in annotation. So the question is is there a way to return pojo or at least map with columns names rather than access all objects by index?
String sql = "select col1 as column1, ...., columnN as columnN from table where col1=2 ";
if(param1!=null){
sql+=" AND param1="+param1;
}
....
Query q = manager.createNativeQuery(sql);
//getting list on object arrays of 45 fields, would like to have POJO or at least map
List list = q.getResultList();
If the table from where you retrieve your data is mapped in a POJO, you can specify manually the POJO by doing the following:
Query query = em.createNativeQuery("SELECT * FROM table", MyPojoMappingTable.class);
#SuppressWarnings("unchecked")
List<MyPojoMappingTable> items = (List<MyPojoMappingTable>) query.getResultList();
On the contrary, if the table is not mapped in any POJO and you're in Spring, you could use jdbcTemplate to get at least a map:
#Autowired
NamedParameterJdbcTemplate jdbcTemplate;
String query;
Map<String, Object> queryParameters;
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
query,
queryParameters
);
I have a query:
Query q = em.createNativeQuery("select DISTINCT id, rating, random() as ordering FROM table\n" +
" WHERE id not in (1,2) ORDER BY ordering LIMIT 10");
List data = q.getResultList();
Every element of this list is array like object:
I want to retrieve that "8" and "16" and compose a comma separated string (to use it in my query in "not in" section in future):
for (Object x : data) {
System.out.println(Arrays.asList(x).get(0));
}
But it produces strings:
[Ljava.lang.Object;#ee93cd3
[Ljava.lang.Object;#62f3c3e1
I don't know how to get that IDs ("8" and "16")
1.I think this is what you are looking for...
Convert JPA query.getResultList() to MY Objects.
or
List<Object[]> rows = q.getResultList(); for (Object[] row : rows) { System.out.println(row[0]); }
in this line
List<Object[]> data = q.getResultList();
data is list of Object of form
[ [1,233, 0.000333], [1,233, 0.000333] ]
for (Object[] x : data) {
// x is [1,233, 0.000333]
System.out.println(x[0]);
}
If I understood it correctly, you are looking for comma separated string of ID's.
If so, then follow these steps might help you to solve the issue.
Create a constructor in table which has only one parameter ID. (If you want you can add more parameters as well but make sure the value which you want it must be in constructor as well as in query.)
Write sql query and execute it.
It returns result and gather it in List which contains the object of the table.
Get the string
dataList.stream().map(obj -> obj.getId()).collect(Collectors.joining(", "))
This will give you the comma separated string.
I have a use case to select ids from table where name should not be in a list that is generated dynamically and can be empty sometimes
#SqlQuery("SELECT id FROM APP_TABLE a WHERE a.app_id = :appId AND a.name NOT IN (<nameList>)")
public List<Long> getIds(#Bind("appId") String appId, #BindIn(value = "nameList") List<String> nameList);
An exception is thrown whenever the nameList is empty. please suggest to modify the query to achieve that use case.
Just add blank in the list if it's empty,
if(nameList.isEmpty())
nameList.add("");
I use RowMapper to map the database tables to java objects
my request looks like the one below
SELECT SUM(quantity) FROM table1 t1
WHERE t1.x = 723834 AND t1.y = 0
UNION ALL
SELECT SUM(quantity2) FROM table t2
WHERE t2.x = 723834 AND t2.y > 0
UNION ALL
SELECT nombre FROM table3
WHERE rownum=1
When I look at the result of the query on the GUI is like that :
SUM(quantity)
20
4
30
and the implementation of my RowMapper is like :
public Transit mapRow(ResultSet rs, int rowNum) throws SQLException {
Double ql=rs.getDouble(1);
rs.next();
Double qe=rs.getDouble(1);
rs.next();
return Transit.builder().qteLivree(ql).qteExpediee(qe)
.nombreJoursOuvrables(rs.getInt(1))
.build();
}
Also I would like to know is there any other method to be able to retrieve without using rs.next() by what I do not receive the good values
Use a ResultSetExtractor.
A RowMapper, as its name indicates, is supposed to map one row of the ResultSet, and Spring is in charge of alling it for each returned row. Since you want to transform several rows of the result set in a single Transit object, you should use a ResultSetExtractor.
I have some very complicated SQL (does some aggregation, some counts based on max value etc) so I want to use SQLQuery rather than Query. I created a very simple Pojo:
public class SqlCount {
private String name;
private Double count;
// getters, setters, constructors etc
Then when I run my SQLQuery, I want hibernate to populate a List for me, so I do this:
Query hQuery = sqlQuery.setResultTransformer(Transformers.aliasToBean(SqlCount.class));
Now I had a problem where depending on what the values are for 'count', Hibernate will variably retrieve it as a Long, Double, BigDecimal or BigInteger. So I use the addScalar function:
sqlQuery.addScalar("count", StandardBasicTypes.DOUBLE);
Now my problem. It seems that if you don't use the addScalar function, Hibernate will populate all of your fields with all of your columns in your SQL result (ie it will try to populate both 'name' and 'count'). However if you use the addScalar function, it only maps the columns that you listed, and all other columns seem to be discarded and the fields are left as null. In this instance, it wouldn't be too bad to just list both "name" and "count", but I have some other scenarios where I need a dozen or so fields - do I really have to list them all?? Is there some way in hibernate to say "map all fields automatically, like you used to, but by the way map this field as a Double"?
Is there some way in hibernate to say "map all fields automatically.
No, check the document here, find 16.1.1. Scalar queries section
The most basic SQL query is to get a list of scalars (values).
sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
These will return a List of Object arrays (Object[]) with scalar values for each column in the CATS table. Hibernate will use ResultSetMetadata to deduce the actual order and types of the returned scalar values.
To avoid the overhead of using ResultSetMetadata, or simply to be more explicit in what is returned, one can use addScalar():
sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
i use this solution, I hope it will work with you.
with this solution you can populate what you select from the SQL, and return it as Map, and cast the values directly.
since hibernate 5.2 the method setResultTransformer() is deprecated but its work fine to me and works perfect.
if you hate to write extra code addScalar() for each column from the SQL, you can implement ResultTransformer interface and do the casting as you wish.
ex:
lets say we have this Query:
/*ORACLE SQL*/
SELECT
SEQ AS "code",
CARD_SERIAL AS "cardSerial",
INV_DATE AS "date",
PK.GET_SUM_INV(SEQ) AS "sumSfterDisc"
FROM INVOICE
ORDER BY "code";
note: i use double cote for case-sensitive column alias, check This
after create hibernate session you can create the Query like this:
/*Java*/
List<Map<String, Object>> list = session.createNativeQuery("SELECT\n" +
" SEQ AS \"code\",\n" +
" CARD_SERIAL AS \"cardSerial\",\n" +
" INV_DATE AS \"date\",\n" +
" PK.GET_SUM_INV(SEQ) AS \"sumSfterDisc\"\n" +
"FROM INVOICE\n" +
"ORDER BY \"code\"")
.setResultTransformer(new Trans())
.list();
now the point with Trans Class:
/*Java*/
public class Trans implements ResultTransformer {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
#Override
public Object transformTuple(Object[] objects, String[] strings) {
Map<String, Object> map = new LinkedHashMap<>();
for (int i = 0; i < strings.length; i++) {
if (objects[i] == null) {
continue;
}
if (objects[i] instanceof BigDecimal) {
map.put(strings[i], ((BigDecimal) objects[i]).longValue());
} else if (objects[i] instanceof Timestamp) {
map.put(strings[i], dateFormat.format(((Timestamp) objects[i])));
} else {
map.put(strings[i], objects[i]);
}
}
return map;
}
#Override
public List transformList(List list) {
return list;
}
}
here you should override the two method transformTuple and transformList, in transformTuple you have two parameters the Object[] objects its the columns values of the row and String[] strings the names of the columns the hibernate Guaranteed the same order of of the columns as you order it in the query.
now the fun begin, for each row returned from the query the method transformTuple will be invoke, so you can build the row as Map or create new object with fields.