JAVA 8 : RowMapper - java

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.

Related

Nested SQL in #Query tag is not working properly

Suppose I have a table which contains all the accounts of user and type.
I want to make a Jpa Repository method which returns an array of total number of each type of user (USER, ADMIN, MASTER).
Here is how I did it in JpaRepository:
#Query(value="SELECT (SELECT COUNT(*) FROM account WHERE account_role='USER'),"
+ "(SELECT COUNT(*) FROM account WHERE account_role='ADMIN'),"
+ "(SELECT COUNT(*) FROM account WHERE account_role='MASTER')"
+ "FROM account LIMIT 1",
nativeQuery=true)
public List<Integer> getTotalAccountType();
The code executed fine, but the result wasn't what I expected.
Result:
[2]
Expected result: [2,10,30]
Any idea how would I use nested SQL with JPQL? Thank you in advance!
If repository method returns List of Integers it means that query result row contains an Integer value. But you expect to get sequence of Integers in one row.
You can get same result different way:
#Query(value="SELECT COUNT(*) FROM account WHERE account_role=?", nativeQuery=true)
public Integer getTotalAccountType(String role);
and then:
Integer userCount = repository.getTotalAccountType("USER");
Integer adminCount = repository.getTotalAccountType("ADMIN");
Integer masterCount = repository.getTotalAccountType("MASTER");
or if you have mapped entity:
create Pair<K,V> class with constructor Pair(K key, V value) or use it from any external library
repository method based on hql query
#Query(value="select new javafx.util.Pair(a.accountRole, count(a)) from Account a group by a.accountRole")
public List<Pair<String, Integer>> getRoleCountList();
convert repository result to a Map<String, Integer> in service
javafx.util.Pair<String, Integer> result = repository.getRoleCountList();
Map<String, Integer> map = result.stream().collect(Collectors.toMap(r-> r.getKey(), r-> r.getValue()));
Try returning Object[] rather than a List<Integer>. I think returning List<Integer> would indicate multiple rows of an Integer value are being returned, whereas you're getting back one row with multiple Intger columns.
From the resulting Object[] you would pull out the first value (indicating a row). This should be another Object[], which will have your values in the order returned.
You can also remove that last "FROM account LIMIT 1" line, as it has no bearing on the result.
I would recommend casting all of this to an object though. As seen here -
How to return a custom object from a Spring Data JPA GROUP BY query

Need to pass parameter dynamically for Excel generation

I am having a select query, which generate dynamically according to the number of fields that the user selects from multiple select box.
while (rs.next()) {
data.put(Integer.toString(i), new Object[] {rs.getString(1), rs.getString(2)});
i++;
}
I have created query dynamically.
Now i need to pick the values from DB using rs.getString().
Above I have given two fields manually. but in my case number fields may vary according to user selection.
so something like List reference i have to put instead of rs.getString(1), rs.getString(2).
can any one give suggestion on this.
Step1: please form your select query with selective columns based on user selection.
if the user selects col1, col2, please form query like select col1, col2 from table.
Step2: Use the ResultSetMetaData api method getColumnCount() to know the no of columns available in resultset
Step3: Based on Step2, use getXXXMethod(columnIndex) to retrieve the values.
Example:
int count = rsMetaData.getColumnCount();
while (rs.next())
{
String[] colValues = new String()[count];
for(int i=0; i<count;i++)
{
colValues[i] = rs.getString(i);
}
System.out.println("The current result set values are :"+colValues);
}

Using sub queries

I am trying to access a data in a table using a sub query.
The table 1 contains a foreign key to table 2 , which means i can use that key to access the data in table 2.
My problem is after i return the array list from the below shown method , the arraylist is null.
This is what i have done:
LogEntry logBookDates;
List<LogEntry> bookList =new ArrayList();
try{
PreparedStatement getSummaryStmt=con.prepareStatement("SELECT * FROM LOGENTRYTABLE WHERE DIARYCODE =(SELECT Diarycode FROM LOGBOOKTABLE WHERE STUDENTUSERNAME=? OR SUPERVISORUSERNAME=? AND PROJECT_APPROVE_STATUS=?)");
//the above statment is the sub query which i have created, i get the diary code from log book table and then access the log entry table.
getSummaryStmt.setString(1,userName);
getSummaryStmt.setString(2,userName);
getSummaryStmt.setString(3,"Accepted");
ResultSet rs=getSummaryStmt.executeQuery();
while(rs.next())
{
logBookDates=new LogEntry(rs.getString("STUDENTUSERNAME"),rs.getString("SupervisorUsername"),rs.getString("projecttitle"),rs.getString("projectDescription"),rs.getDate("startDate"),rs.getDate("enddate"),rs.getString("project_approve_status"),rs.getString("diarycode"),rs.getString("projectcode"),rs.getInt("Index"),rs.getString("log_Entry"),rs.getDate("logentry_date"),rs.getString("supervisor_comment"),rs.getString("project_progress"));
bookList.add(logBookDates);
}
}catch(Exception e){}
return bookList;
}
I have not used sub queries before and this is the first time am using them.
What seems to be the problem here ?
Thank you for your time.
Edit : Sample data of logbook table
Sample Data of logentry table
Expected output:
I don't have a screen shot of that but what i need is just to iterate through the arraylist which will be returned from the above method.
Here is the problem, the LOGENTRYTABLE table doesn't contain a column with STUDENTUSERNAME, SupervisorUsername, projecttitle, projectDescription, startDate, etc...
rs.getString("STUDENTUSERNAME"), rs.getString("SupervisorUsername"), etc...
probably, you need JOIN query
"SELECT * FROM LOGENTRYTABLE LT
INNER JOIN LOGBOOKTABLE LB ON LT.DIARYCODE=LB.DIARYCODE
WHERE LT.DIARYCODE =
(SELECT DIARYCODE FROM LOGBOOKTABLE
WHERE (STUDENTUSERNAME=? OR SUPERVISORUSERNAME=?)
AND PROJECT_APPROVE_STATUS=?)"

How to query for a List<String> in JdbcTemplate?

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.

How to run an aggregate function like SUM on two columns in JPA and display their results?

I am new to JPA. So my question should be so simple to some.
Below is the Simple Query in SQL which i would like to convert to JPA. I already have an entity class called TimeEnt.
SELECT
SUM(TimeEntryActualHours) as UnBilledHrs,
SUM (TimeEntryAmount) as UnbilledAmount
FROM TimeEnt WHERE MatterID = 200
The JPA Query Language does support aggregates functions in the SELECT clause like AVG, COUNT, MAX, MIN, SUM and does support multiple select_expressions in the SELECT clause, in which case the result is a List of Object array (Object[]). From the JPA specification:
4.8.1 Result Type of the SELECT Clause
...
The result type of the SELECT
clause is defined by the the result
types of the select_expressions
contained in it. When multiple
select_expressions are used in the
SELECT clause, the result of the query
is of type Object[], and the
elements in this result correspond in
order to the order of their
specification in the SELECT clause
and in type to the result types of
each of the select_expressions.
In other words, the kind of query you mentioned in a comment (and since you didn't provide your entity, I'll base my answer on your example) is supported, no problem. Here is a code sample:
String qlString = "SELECT AVG(x.price), SUM(x.stocks) FROM Magazine x WHERE ...";
Query q = em.createQuery(qlString);
Object[] results = (Object[]) q.getSingleResult();
for (Object object : results) {
System.out.println(object);
}
References
JPA 1.0 Specification
4.8.1 Result Type of the SELECT Clause
4.8.4 Aggregate Functions in the SELECT Clause
Lets think we have entity called Product:
final Query sumQuery = entityManager
.createQuery("SELECT SUM(p.price), SUM(p.sale) FROM Product p WHERE p.item=:ITEM AND ....");
sumQuery.setParameter("ITEM","t1");
final Object result= sumQuery.getSingleResult(); // Return an array Object with 2 elements, 1st is sum(price) and 2nd is sum(sale).
//If you have multiple rows;
final Query sumQuery = entityManager
.createQuery("SELECT SUM(p.price), SUM(p.sale) FROM Product p WHERE p.item in (" + itemlist
+ ") AND ....");
// Return a list of arrays, where each array correspond to 1 item (row) in resultset.
final List<IEniqDBEntity> sumEntityList = sumQuery.getResultList();
Take a look at the EJB Query Language specification.
The idiom is very similiar to standard SQL
EntityManager em = ...
Query q = em.createQuery ("SELECT AVG(x.price) FROM Magazine x");
Number result = (Number) q.getSingleResult ();
Regards,

Categories

Resources