I want to create a list with my database field values.
There are 2 columns, name and surname.
I want to create a list that stores all names in name column in a field and then add to my DTO.
Is this possible?
Steps you can follow: -
First you need to have a List<String> that will store all your names. Declare it like this: -
List<String> nameList = new ArrayList<String>();
Now, you have all the records fetched and stored in ResultSet. So I assume that you can iterate over ResultSet and get each values from it. You need to use ResultSet#getString to fetch the name.
Now, each time you fetch one record, get the name field and add it to your list.
while(resultSet.next()) {
nameList.add(resultSet.getString("name"));
}
Now, since you haven't given enough information about your DTO, so that part you need to find out, how to add this ArrayList to your DTO.
The above list only contains name and not surname as you wanted only name. But if you want both, you need to create a custom DTO (FullName), that
contains name and surname as fields. And instantiate it from
every ResultSet and add it to the List<FullName>
JDBC unfortunately doesn't offer any ways to conveniently do this in a one-liner. But there are other APIs, such as jOOQ (disclaimer: I work for the company behind jOOQ):
List<DTO> list =
DSL.using(connection)
.fetch("SELECT first_name, last_name FROM table")
.into(DTO.class);
Or Spring JDBC
List<DTO> list =
new JdbcTemplate(new SingleConnectionDataSource(connection, true))
.query("SELECT first_name, last_name FROM table", (rs, rowNum) ->
new DTO(rs.getString(1), rs.getString(2));
Or Apache DbUtils:
List<DTO> list =
new QueryRunner()
.query(connection,
"SELECT first_name, last_name FROM table",
new ArrayListHandler())
.stream()
.map(array -> new DTO((String) array[0], (String) array[1]))
.collect(Collectors.toList());
I've used Java 8 for the Spring JDBC / Apache DbUtils examples, but it can be done with older versions of Java as well.
It is. What have you tried ?
To access the database, you need to use JDBC and execute a query, giving you a ResultSet.
I would create an class called FullName with 2 String fields name and surname. Just populate these in a loop using
rs.getString("NAME"); // column name
rs.getString("SURNAME");
e.g.
List<FullName> fullnames = new ArrayList<FullName>();
while (rs.next()) {
fullnames.add(new FullName(rs));
}
Note that I'm populating the object via the ResultSet object directly. You may choose instead to implement a constructor taking the 2 name fields.
Note also that I'm creating a Fullname object. So the firstname/surname are kept separate and you have the freedom to add initials etc. later. You may prefer to rename this Person and that will give you the freedom to add additional attributes going forwards.
What worked for me is:
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
ArrayList<String> resultList= new ArrayList<>(columnCount);
while (rs.next()) {
int i = 1;
while(i <= columnCount) {
resultList.add(rs.getString(i++));
}
}
return resultList;
This worked for me->
private List<Object[]> convertResultsetToObject(ResultSet rs) throws SQLException {
List<Object[]> results = new ArrayList<Object[]>();
int count = 0;
if(rs != null) {
ResultSetMetaData rsm = rs.getMetaData();
count = rsm.getColumnCount();
}
while(rs != null && rs.next()) {
Object [] obj = new Object[count];
int temp = 1;
while(temp <= count) {
obj[temp - 1] = rs.getObject(temp);
temp++;
}
results.add(obj);
}
return results;
}
Related
I have the following section of code to implement a drop-down menu. Based on the two parameters selected from the menu, I run the corresponding query by replacing the selected column with the mapped column name.
String sql = "SELECT ?, ? FROM <table_name>";
ArrayList<Some_POJO> res = this.jdbcTemplate.query(sql, new ResultSetExtractor<Some_POJO>()
{//logic goes here.},
user_inputs_as_object_array);
Now, in the logic section, I'm using the following command to map the returned values:
while(rs.next()) {
Some_POJO = new SOME_POJO();
Some_POJO.setParam1(rs.getString("SOME_COLUMN_NAME")); //ERROR
}
Now, the marked line fails when I refer to column by name. But what is surprising is, the following workaround works:
while(rs.next()) {
Some_POJO = new SOME_POJO();
int i = 1;
Some_POJO.setParam1(rs.getString(i)); //Works(?!)
}
I tried to modify the JDBC template call to return the ResultSetMetaData object instead of an ArrayList of Some_POJO:
String sql = "SELECT ?, ? FROM <table_name>";
ResultSetMetaData res = this.jdbcTemplate.query(sql, new ResultSetExtractor<ResultSetMetaData>()
{//extractData now returns rs.getMetaData()},
user_inputs_as_object_array);
try
{
System.out.println("Column cout: " + res.getColumnCount());
for(int i = 1; i <= res.getColumnCount(); i++)
System.out.println("Label: " + res.getColumnLabel(i) + "\nName: " + res.getColumnName(i));
}
catch(SQLException sqle)
Only to get:
Column cout: 2
Label: :1
Name:
Label: :2
Name:
Could someone please shed some light into what is happening and (except for suggestions suggesting a complete redesign) what would be the most optimal way to achieve the expected functionality? I would prefer not to use column indexes since order changes would break the functionality.
Just for refactoring code, get values as:
ArrayList result = new ArrayList(0);
result.addAll(this.jdbcTemplate.query(sql, (RowMapper) (rs, rowNum) -> {
POJO out = new POJO();
out.setProperty1(rs.getString("prop1"));
out.setProperty2(rs.getString("prop2"));
...
return out;
}));
For me it works fine, passing field name in method, don't think that there are some troubles with spring jdbc. Maybe is something wrong in your sql query.
I want to call a procedure from java.
Example:
CREATE OR REPLACE PROCEDURE helloworld
AS
cursor data_test is
with required_data as( select *
from EMPLOYEES) select * from required_data rd join DEPARTMENTS d on d.department_id=rd.DEPARTMENT_ID ;
emp_rec data_test%ROWTYPE;
more_detail varchar2(50);
BEGIN
open data_test;
LOOP
FETCH data_test INTO emp_rec;
exit when data_test%NOTFOUND;
more_detail =BLA.GET_MORE_DATA(data_test.empid);// Lets say the fuction return varchar2 I've total of 4 similar fields to be added in total
END LOOP;
END;
/
Now how do I return all the data in the cursor(data_test ) along with more_detail in each row?? i.e I want my ResultSet in java to have all the values from the select statement as well as the value of more_detail.Also will my helloworld have out parameter as SYS_REFCURSOR or something else?? and when calling from java all I've to do is create a callable statement and register out parameter as Cursor to use this?
Well, when I look at it today, How could I ask this question. This so wrong.
I didn't have much understanding of PLSQL then and asked the question without reading much. Anyway, there are many ways to solve it as I mentioned in the comment one can send back an associative array but that is a pain on the java side if you don't know the number of expected result. or one can create a nested array with an user with defined object something like this:
CREATE OR REPLACE TYPE keyvalue AS object (col Number(10), col2 VARCHAR2(30));
CREATE OR replace TYPE map IS TABLE OF keyvalue ;
hear's how I did it. You cannot add a column to cursor but you can create you own custom defined data type.
CREATE OR REPLACE TYPE dbObject AS OBJECT
(
empId NUMBER (6),// here add as many fields you want to return or want your object to have
emailId VARCHAR2 (25),
hiredate DATE
);
CREATE OR REPLACE TYPE datalist IS TABLE OF DBOBJECT;
PROCEDURE get_emp_data (list OUT datalist)
AS
CURSOR emp_cursor
IS
SELECT employee_id AS empId,
EMPLOYEES.EMAIL AS emailId,
EMPLOYEES.HIRE_DATE AS hiring
FROM EMPLOYEES;
c_datatype emp_cursor%rowtype;
BEGIN
OPEN emp_cursor();
list := datalist();
LOOP
fetch emp_cursor into c_datatype;
EXIT WHEN emp_cursor%NOTFOUND;
list.extend;
list(emp_cursor%ROWCOUNT):=DBOBJECT(c_datatype.empId,c_datatype.emailId,c_datatype.hiring);
END LOOP;
END get_emp_data;
Now you want to call this from java: here is the code:
String dataTypeName = "DBOBJECT";
String dataTypeListName = "datalist";
StructDescriptor structDescriptor = StructDescriptor.createDescriptor(dataTypeName.toUpperCase(), connection);
ResultSetMetaData metaData = structDescriptor.getMetaData();
CallableStatement cs = connection.prepareCall("{call TEST_PACKAGE.get_emp_data(?)}");
cs.registerOutParameter(1, OracleTypes.ARRAY, dataTypeListName.toUpperCase());
cs.execute();
Object[] data = (Object[]) ((Array)cs.getObject(1)).getArray();
for(Object tmp : data) {
Struct row = (Struct) tmp;
int index = 1;
for(Object attribute : row.getAttributes()) {
System.out.println(metaData.getColumnName(index) + " : " + attribute);
++index ;
}
}
cs.close();
As I process each country, how do I add only the languages with matching country id for that country?
private List<String> readCountryLanguages(Statement sqlStatement, List<Country>countries) throws SQLException {
List<String> languages = new ArrayList<>();
for (Country country : countries ) {
ResultSet resultSet = sqlStatement.executeQuery("SELECT language FROM COUNTRY_LANGUAGE");
while (resultSet.next()) {
String language = new String(resultSet.getString("Languages"));
Country obj = new Country(0, language, 0, 0);
obj.getId();
}
}
return languages;
}
Not sure about your table structure of COUNTRY_LANGUAGE but you can update the sql query to something like.
SELECT language FROM COUNTRY_LANGUAGE WHERE country='" + country.getName() + "'
Here I am assuming you have a column "country" in your COUNTRY_LANGUAGE table and country name can be retrieve from Country object by calling Country.getName(). But you can use the exact method from your actual implementation.
return the Country id in a variable when you iterate; you should have something like country.getId();
ResultSet resultSet = sqlStatement.executeQuery("SELECT language FROM COUNTRY_LANGUAGE where id='countryIdVariable'");
change your sql query to be parametized/prepared statement so you can prevent sql injection and also add variable easier instead of having to write variable within single quote in your sql string.
AS menthion by Asura, First of all change your sql query that returns language where contryId is 'something'
then in while loop add each language to languages list object by using add() method like this :
while (resultSet.next()) {
String language = resultSet.getString("Languages");
// add to list
languages.add(language);
}
}
Below you can find the code that I'm using to retrieve a table from a generic SQL statement (the SQL code is inputed by the user in another part of the code).
Since I'll be storing more than one table in the future, and the retrieved table will have some format functions applied to it, I'm storing the values in a:
Map<String, Map<String,List<Object>>> tables = new HashMap<String, Map<String,List<Object>>>();
The first Map is a Table, the second is a Column and finally the list hold each line of data.
The column names/order in:
Map<String, TreeMap<Integer, String>> order = new HashMap<String, TreeMap<Integer, String>>();
The Map is tha table, the TreeMap are the column number - column name
The code to create de generic table is:
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
for(int i = 1; i <= rsmd.getColumnCount(); i++){
String name = rsmd.getColumnLabel(i); //Label vs name. Label is also what's defined by the user in: "as VENDA"
answer.put(name, null);
singleOrder.put(i, name);
}
while(rs.next()){
Iterator answerIt = answer.entrySet().iterator();
while (answerIt.hasNext()) { //Get Columns in index order to compare with body HashMap (unordered)
Map.Entry columnNameValue = (Map.Entry)answerIt.next(); //key = columnName, value = List
String columnName = (String) columnNameValue.getKey();
List<Object> tmp = answer.get(columnName);
if (tmp == null) {
tmp = new ArrayList<Object>();
answer.put(columnName, tmp);
}
Object item = rs.getObject(columnName);
if(item instanceof Integer){
item = ((Integer) item).doubleValue();
} else if (item instanceof Long){
item = ((Long) item).doubleValue();
} else if (item instanceof BigDecimal){
item = ((BigDecimal) item).doubleValue();
}
tmp.add(item);
}
}
Efficiency will be key for this part of my code. How can I improve the table creation?
(I think this is a important question for Java coders, so a clear response can serve a pattern for future programmers that have the same difficulty that I'm having now)
Update 1:
Why I'm asking: I couldn't find any example of how to handle a flexible query in Java. I've created my own solution, but I fear it's some kind of monster compared to how to handle such a case properly in Java.
As example:
Lets say I'll name the result of a first query as "table1".
The query will be: Select STORE, SALES, GOAL, EXTRA from ... ;
The order variable will be like:
Map<String, TreeMap<Integer, String>> order = new HashMap<String, TreeMap<Integer, String>>();
table1,
1, "STORE"
2, "SALES"
3, "GOAL"
4, "EXTRA"
The tables variable:
Map<String, Map<String,List<Object>>> tables = new HashMap<String, Map<String,List<Object>>>();
"table1",
"STORE",
"unit1"
"unit2"
"unit3"
"unit4"
"SALES",
1312
126
1361
6823
"GOAL"
1300
160
1200
6000
"EXTRA"
"info1"
"info2"
"info3"
"info4"
I am using
List<USERS> user =
getHibernateTemplate().find("select uid, username,email from USERS");
to get three columns values from the users TABLE. But I can access no individual column value using the "user" object because the "user" type is an object type and I can't cast it to the USERS.
Is there any ways to use the "user" object and access individual columns value?
Why are you just querying selected columns - just get the whole row(s). Let me know if that helps.
If you are fetching only few columns, the hibernate template will return a list of object arrays.
Your example should look like this,
List<Object[]> userDetails =
getHibernateTemplate().find("select uid, username,email from USERS");
And you should know the first element is a integer and second, third are string and do cast on your own. This is very error prone ofcourse.
Thanks Nilesh and Sean for your suggestions. I always deal with the objects instead of individual columns. But this specific app works with other tables from another app which is not written in Java (That is why I am using USERS table not "User", because it is already created by another app) and is not using hibernate. I created a USERS class that implements UserDetails and has much less columns than the original app USERS table. When I get the whole object I get a formatting error that is why I tried using selected columns instead of the object.Anyhow I wrote this code and was able to get the individual columns:
List user=
getHibernateTemplate().find("select uid, username,email from USERS where uid<>0 AND obj_type=1");
List<USERS> l = new ArrayList<USERS>();
for (int i = 0; i < user.size(); i++) {
USERS du = new USERS();
Object[] obj = (Object[]) user.get(i);
Integer uid = (Integer) obj[0];
du.setUid(uid);
String username = (String) obj[1];
du.setUsername(username);
String email = (String) obj[2];
du.setEmail(email);
l.add(du);
}
My last question: isn't it more expensive to get the whole columns(the object) than getting the individuals ones?
Keep in mind it that...
getHibernateTemplate.find() method returns List of based on passed object.
Then after this you have to take List of Users then you have to separate all resulting object and after specified a object you can access attribute of it.
Its very easy..
If you have any query then tell me
I will try my best.
#Override
public Object findByNo(Object id) throws Exception {
List list = getHibernateTemplate().find(
"select book from Book book " +
"where book.id=?",id);
if (list.size() != 0) {
return list.get(0);
} else {
return new Book();
}
}
I'm guessing your db table is called USERS and the entity class is called User. If that is the case, then you should do something like this:
List<User> users = getHibernateTemplate().find("from User");
for(User user: users){
// you probably don't need the id, so I'll skip that
String email = user.getEmail();
String userName = user.getUserName();
// now do something with username and email address
}
If you use an ORM framework, deal with objects, not with Database Columns!
And about naming:
Java naming conventions suggest that a class name is in TitleCase (or more precisely UpperCamelCase), not UPPERCASE. Also, a class that represents a single user should be called User, not Users.
you can try something like this:
for (TemplateAttributes templateAttributes1 : templateAttributes) {
templateAttributes1.setTemplates(templates);
templateAttributes1.setCreateDate(new Date());
templateAttributes1.setCreateUser(ApplicationConstants.userName);
templateAttributes1.setLastModified(new Timestamp(new Date().getTime()));
templateAttributesNew.add(templateAttributes1);
}
templates.setTemplateAttributes(templateAttributesNew);
templates.setUpdateDate(new Date());
templates.setUpdateUser(ApplicationConstants.userName);
templates.setLastModified(new Timestamp(new Date().getTime()));
getHibernateTemplate().bulkUpdate("delete from TemplateAttributes where templateAttributePK.templates.id="+templates.getId());
getHibernateTemplate().update(templates);
Try this
List<USERS> user =(List<USERS>)(List<?>)
getHibernateTemplate().find("select uid, username,email from USERS");