groovy script / java code to get distinct users from resultset - java

Hi I run a simple select query in oracle over a table and get a resultset. like select username, responsibility, project from mytable.
The resultset contains user details. there are multiple rows returned for each username with different values for responsibility and project.
Now I want to get a list of lists from this resultset which has one List per username and distinct values are concatenated in a comma seperated string.
So if Sam has multiple entries in the resultset then the output of my operation should give me:
UserList =
["Sam", "responsibility1,responsibility2,responsibility3...", "dept1,dept2,dept3.."],
[Some other User],
[and so on..]
Later I will write this to a csv file.
I cannot do this in the query itself for compatibility reasons, we have to support multiple databases, versions in future.
How do I do this in java or groovy?
Thanks

Java is quite easy.
You need a class to model each user.
You need a Map of username to User.
Each User contains a List of responsibility and a List of departments.
Then you iterate your resultset, find the User from the map on each row and add the responsibility and department to that User
Do you need the code or is that good enough?
HTH
Edit: Here's some Java starting code:
(Not checked for syntax or mistakes ;] )
public class User {
private final List<String> responsibility = new ArrayList<String>();
private final List<String> department = new ArrayList<String>();
...standard getters and setters
}
// Your code to do the read
public void executeRead() {
... obtain the resultset somehow here
Map<String, User> usernameToUser = new HashMap<String, User>():
while (rs.next) {
String username = rs.getString("username");
User user = usernameToUser.get(username);
if (user == null) {
user = new User(); // Create and remember a user the first time you see them
usernameToUser.put(username, user);
}
String responsiblity = rs.getString("responsiblity");
String department = rs.getString("department");
user.addResponsibility(responsibility);
user.addDepartment(department);
}
rs.close();
// Now you have the data structure of users in memory you can output
// it in whichever format you like e.g. HTML, CSV, etc
// Probably best to do this step in a totally separate place that can
// be switched out for a different output format in future.
}

Related

How to store multiple database column values in a single list in Java

I want to compare 2 data base tables using Selenium Java.For that I am trying store values of one table in one list and other tables in another list.Later i want to compare both the lists.but I am unable to store all values of database columns in single List.This is what I tried.
public class Employee{
public void execute_query() throws Exception{
Class.forName(dbdriver);
con=DriverManager.getConnection(URL,DB_UserName,DB_Password);
query ="select * from Employee;
stmt = con.createStatement();
rs = stmt.executeQuery(query);
ArrayList<Employee> customerList = new ArrayList<>();
while(rs.next()) {
Employee customer = new Employee(rs.getString("ID"),rs.getString("LastName"),rs.getString("FirstName");
customerList.add(customer);
}
}
}
But I am getting error near
Employee customer = new Employee(rs.getString("ID"),rs.getString("LastName"),rs.getString("FirstName"));
saying
The constructor Employee(String,String,String)is undefined.
Is there any other way to store all database column values in single list?
You may be missing the constructor for that sequence of parameters in your Employee class. This will need to be created or you will have to make use of a builder pattern/setters to populate the fields of the class.
Also, the ID field is likely to be an Integer or a Long if it comes from the primary key field in the database rather than a String - but this is just a predication as I cannot see the database you are using nor how the primary key has been configured.
Try rs.rs.getInt("ID") instead of rs.getString("ID")List item
anyway, you can store them in a different way;
HashMap<String>,HashMap<String,String>>
//As in [Id,[FName,LName]]

Querying mysql with java with multiple possible "where" statements

I'm trying to find a nice solution to a movie filtering system built in java and with a mysql database. The user is supposed to be able to filter which movies they wish to see based on a number of attributes, such as: director, actor, length, genre, year,...,. In total there are 11 fields which can be used to filter the query.
The problem is some of these fields can (and probably will) be left blank. For instance, maybe the user only wants to filter data based on a certain genre, director and length. Or maybe they only want to filter it based on the prodution studio, and dont care about the other filter options.
I have made a connection to the server, and the problem is in creating the "SQL_String" that I will use in statement.executeQuery(SQL_String).
Lets say I only wanted to filter for one field. Then I know I could write
String field = //user input (for example: actor)
String filter = //user input (for example: 'tom cruise')
String SQL_String = "SELECT * FROM Movies WHERE "+field + "=" +filter
But if i want to allow the user to filter based on several (or zero) fields, then I dont know how to write the code.
Some example queries could be:
"SELECT * FROM Movies WHERE (director = 'steven spielberg' AND genre = 'action' AND length >100)"
"SELECT * FROM Movies WHERE (type = 'tv-series' AND actor = 'bob odenkirk')"
So the user can specify which fields they want to filter (if any) and i need to come up with a java code that can take those into account and construct a query string.
Since you don't know how many fields the user will filter on but you do know that the data you're dealing with has two parts (the field and the filter), the first two things that come to my mind are maps and tuples. Since, unlike Python, Java does not have a built in tuple data type (to my knowledge), here is a small example solution that I thought of for your problem solved using Java's HashMap and Map classes.
In this example, I create a HashMap with the key being a string for the "field" and the value being a string for the "filter". You can set these values based on the user input wherever you have that in your code (in this example, simply hard-coded in the main method). Then you can loop through the key-value pairs in your HashMap (see this helpful post), appending the key and value as well as the additional characters necessary for the query. This is a simple example but shows a possible solution route.
If you want to make sure that this solution works for the cases where you filter value is an integer, then just add in another if-statement in the loop to try parsing for an integer and if one exists to not add the extra \' escape characters.
import java.util.HashMap;
import java.util.Map;
public class MovieQueryTest {
public static void main(String[] args) {
String SQL_Query = "SELECT * FROM Movies WHERE ";
HashMap<String, String> queryFilters = new HashMap<>();
queryFilters.put("director", "Steven Spielberg");
queryFilters.put("type", "tv-series");
queryFilters.put("actor", "Bob Odenkirk");
boolean firstQueryFilter = true;
for (Map.Entry<String, String> entry : queryFilters.entrySet()) {
if (firstQueryFilter) {
SQL_Query += entry.getKey() + "=\'" + entry.getValue() + "\'";
firstQueryFilter = false;
} else {
SQL_Query += " AND " + entry.getKey() + "=\'" + entry.getValue() + "\'";
}
}
System.out.println(SQL_Query);
}
}

Java Map<String,Map<String,Integer> use to populate results

I want to create one report that you select product and storeName and returns all the sales per date (within a range).
The db view that helped me with other reports looks like this:
product - store_name - date
So far my approach is to return all records from the db in a list and then I do the following:
public void salesReport(String product, String store, String date){
List<RecordSalesInfo> salesResults = salesDao.getSales();
Map<String, Integer> mapper = new HashMap();
//calculation
for (RecordSalesInfo record : salesResults) {
StringBuilder key = new StringBuilder();
key.append(record.getProduct()).append(":")
.append(record.getStoreName()).append(":")
.append(record.getWageredDate()).append(":");
if (mapper.containsKey(key.toString())) {
Integer get = mapper.get(key.toString());
get++;
mapper.put(key.toString(), get);
} else {
mapper.put(key.toString(), 1);
}
}
for (String key : mapper.keySet()) {
if(key.toString.equals("Pen:London:13June2016"){
System.out.println("sales:" + mapper.get(key.toString);
}
}
}
the query in the salesDao(saving as "RecordSalesInfo") is:
SELECT
rs.product AS product,
rs.store_name AS store,
rs.date AS date,
rs.product_id AS productId
FROM sales rs
ORDER BY rs.product,rs.store_name,rs.date
The reason I didn't query "Count(blabla) where product='a' and store_name='b' and date='c' " is because the user changes the input using a jSlider very often (input=product,store,date), that means too many queries. So I thought it is better to take all the results from db and then display what the user needs.
A) Is there a better way to do this?
B) In the next phase the user will enter only the product and the store and I have to return the list of the sales by date, looking like this:
Pen - London (manual input)
12June2016 100
15June2016 30
19July2016 67
With what I have done so far, I can't get only the dates that I have sales, and I have to "search" from the hashMap for all dates(specific range). I think as a solution to change the key to "product:storeName" one the existing map and have as a value another map where the String will be the date and the Integer the amount of sales.
Again is there a better way on doing that?
What I would do is that I will not hold/keep a map, rather I will fetch the results as the query changes based on the JSlider etc.
You can do one thing that when a query is sent until its results are available you can disable your Jslider and show a spinner that notifies the user that processing is going on.
Once the result is available, enable the slider and hide the spinner.
To have all the data in the map does not seem a good idea. It will be a disconnected state of data.

How to fetch large datasets into List of Strings with Spring?

I want to fetch large datasets from a MySql database, and return it as a List of comma separated Strings via a Webservice (not via a downloadable file, but directly as text).
Therefore I first need all selected rows in CSV format.
Question: What's the best way achieving this with Spring?
The following works with JdbcTemplate, but I don't know if this is the best approach (maybe optimize using Java 8 streams)?
Also if somehow feels wrong having to iterate the ResultSet and call rs.getString(i), concatenating each element of the ResultSet. Isn't there a more elegant way?
RowMapper<String> rowMapper = new RowMapper<String>() {
#Override
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
sb.append(rs.getString(i)).append(",");
}
return sb.toString();
}
};
String sql = "SELECT * from mypersons where age > 12";
List<String> list = new JdbcTemplate(dataSource).query(sql, params, rowMapper);
//...return the list via Webservice
Sidenote: I have to use native SQL for the select. They are much more complicated in my example above.
I recommend using Spring Data JPA to get a SET of persons. Once you have a set of persons, you can use streams to map the persons to the field you want to collect, and join that field with commas.
Assuming a Set, given a person has an attribute called name, this method will return a comma separated list of names.
public String joinName(Set<Person> persons) {
return persons.stream().map(Person::getName).collect(Collectors.joining(", "));
}
Your solution looks fine. I just have two possible improvements:
the indexed based access to the ResultSet already exists in the ColumnMapRowMapper. You could delegate to it and would get (pseudocode)
class CommaSeparatedStringRowMapper implements RowMapper<String> {
ColumnMapRowMapper delegate = new ColumnMapRowMapper();
#Override
public Map<String, Object> mapRow(ResultSet rs, int rowNum){
delegate.mapRow(rs, rowNum).valueSet().toStream.collect(Collectors)
}
Note: this will be less efficient, due to creating and accessing the intermediate map, but it looks nicer.
Note 2: The underlying map should preserve the order of columns, but you better double check.
Alternatively, you might consider using a RowCallbackHandler writing your results directly into a Webservice response thingy. If done right, part of your response might be on the way to your client while you are still processing the rest of the query result. So this might improve latency and memory consumption.

The best way how to keep select result in array

I have a question about keeping query result in array. For example I execute a query
SELECT * FROM some_table
Then I want to save it to array and create records. The table contains these columns:
id
user_name
last_name
The result array can be:
[[1, "First user name", "First last name"],
[2, "Second user name", "Second last name"]
...
].
Can you recommend me which array or data type should I use?
You do that like this:
Create a bean class for User
public class User {
private int id;
private String firstName;
private String lastName;
// getter and setter
...
}
And then query all the data from table, and create a User object and set the data.
List<User> users = new ArrayList<>();
while(someValue) {
...
int id = ...
String firstName= ...
String lastName = ...
User user = new User();
user.setId(id);
user.setFirstName(firstName);
user.setLastName(lastName);
users .add(user);
}
// after do what you want with the list
I extend your question to "the best way to keep select result" (with or without array).
It depends on:
how many results
how many fields
what do you want to do after ?
do you want to modify, put in your database again ?
So, several propositions:
just arrays: String[] fields1; String[] fields2, ...
array of arrays: String[][];
better collections: Vector, List or Set: do you want them to be sorted ?, how do you pick them after ? Or Map, (if you want to keep index => data)
or Object you create yourself. For this, you even have tools to map object-database.
you should take a look at these features, and what you want to do .
Hope it helps.

Categories