Can't get column name when using HibernateTemplate - java

I'm using Spring-orm and HibernateTemplate to execute a native SQL query (DB is Oracle 11 for the reference), like this:
#Override
public List<Object> executeNativeQuery(final String queryStr, final Map<String, String> params) {
List<Object> results = this.template.execute(new HibernateCallback<List<Object>>() {
#Override
public List<Object> doInHibernate(Session session) throws HibernateException, SQLException {
// Get the query
Query query = session.createSQLQuery(queryStr);
// Assign parameters to the query, if any
if (params != null) {
for (Map.Entry<String, String> entry : params.entrySet()) {
query.setString(entry.getKey(), entry.getValue());
}
}
// fire the query
#SuppressWarnings("unchecked")
List<Object> res = query.list();
return res;
}
});
return results;
}
I've managed to successfully execute the query and get the results back. But I couldn't figure out a way to also get the resulting column names, and I'm starting to think that's not possible using this approach.
My problem is that I have to execute a query that comes from user input and I have no clues about parameter names.
Any ideas?

I finally found a way through it, so I post it hoping it'll be useful for others. I was doing it the wrong way, the correct way (at least for what my needs are) is to use doWork.
instad of:
session.createSQLQuery(queryStr);
I had to get the connection like this:
session.doWork(new Work() {
#Override
public void execute(Connection con) throws SQLException {
try {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(queryStr);
ResultSetMetaData md = rs.getMetaData();
int col = md.getColumnCount();
System.out.println("Number of Column : " + col);
System.out.println("Columns Name: ");
for (int i = 1; i <= col; i++) {
String col_name = md.getColumnName(i);
System.out.println(col_name);
}
} catch (SQLException s) {
System.out.println("SQL statement is not executed!");
}
}
});

Try the following to get column names in Hibernate:
public ArrayList<String> getTableDesc(String tableName){
System.out.println("getFieldNames:start"+tableName);
Object[] a;
List<Object[]> fieldNames = new ArrayList<Object[]>();
ArrayList<String> tabFieldNames = new ArrayList<String>();
Session session = getHibernateTemplate().getSessionFactory().openSession();
try{
String queryStr = "desc "+tableName;
fieldNames = (List<Object[]>) session.createSQLQuery(queryStr).list();
for(int i=0;i<fieldNames.size();i++){
a = fieldNames.get(i);
tabFieldNames.add(a[0].toString());
}
}
catch(Exception e){
System.out.println("exception "+e);
}
finally{
session.close();
}
System.out.println("getFieldNames:end"+tabFieldNames.toString());
return tabFieldNames;
}

You can use ResultTransformer class to map the query result to an entity class.
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/querysql.html#d0e17313
EDIT 1:
for( int i= 0; i< ((Object[])res.get(0)).length ; i++){
//do something with data
res.get(0)[i]
}

Related

You have an error in your SQL syntax but query runs in MySQL workbench

Query doesn't work in IntelliJ but it does in SQL workbench. On other queries the updatePreparedStatement method works. I've checked my query for syntax errors and strangely enough it does run in sql workbench. Can somebody help a noob out?
public void updatePreparedStatement(String queryWithParameters, Object ... values) {
ArrayList<Map<String, Object>> result = new ArrayList<>();
try(PreparedStatement ps = this.connection.prepareStatement(queryWithParameters)) {
for(int i=0; i<values.length; i++) {
ps.setObject(i+1, values[i]);
}
int rs = ps.executeUpdate();
}catch (SQLException e){
e.printStackTrace();
}
}
public void assignOrdersToDeliveryRoute(BigInteger routeNumber, int[] orderNumbers){
DbConnection connection = new DbConnection();
System.out.println(Arrays.toString(orderNumbers));
StringBuilder orderNumberString = new StringBuilder();
int index =1;
for (int orderNumber : orderNumbers) {
String order = String.valueOf(orderNumber);
if (index < orderNumbers.length ){
orderNumberString.append(order).append(",");
}
else {
orderNumberString.append(order);
}
index++;
}
System.out.println(orderNumberString);
String query = "SET FOREIGN_KEY_CHECKS=0; UPDATE bestelling SET bezorgroutenummer = ? WHERE bestelnummer IN (" + orderNumberString + ")";
System.out.println(query);
connection.updatePreparedStatement(query, routeNumber);
}
https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html says:
SQL syntax for prepared statements does not support multi-statements (that is, multiple statements within a single string separated by ; characters).
Run your statements one at a time.

batch processing with dynamic insert query using spring "jdbcTemplate.batchUpdate"

Well I have been updating a legacy code since last few days.
Explanation:
There is a table CUSTOMER with columns blah1, blah2, blah3, blah4, blah.....
As per our architecture I need to create an insert statement dynamically which can insert data into any table with any number of column.
Currently we have following code.
public void save(Table table, Connection conn) throws Exception {
PreparedStatement pstmt = null;
try {
List<Row> rows = table.getRows();
String sql = "";
if(!rows.isEmpty() && rows != null)
{
for(Row row: rows) //READ EACH ROW
{
String columnName = ""; String columnValue = "";
List<String> params = new ArrayList<String>();
List<Column> columns = row.getColumns();
if(!columns.isEmpty() && columns != null)
{
for(Column column: columns) //GET EACH COLUMN DATA
{
columnName += ", "+column.getName();
columnValue += ", ?";
String value = column.getValue();
params.add(value); //ADD VALUE TO PARAMS
}
//INSERT QUERY
sql = "INSERT INTO "+table.getTableName()+" ("+columnName+") VALUES ("+columnValue+")";
if(pstmt == null) pstmt = conn.prepareStatement(sql);
//POPULATE PREPARED STATEMENT
for (int i =0; i<params.size(); i++) {
pstmt.setString(i+1, (String)params.get(i));
}
pstmt.addBatch();
}
}
pstmt.executeBatch();//BATCH COMMIT
conn.commit();
}
} catch (Exception e) {
if (conn != null) {
conn.rollback();
}
throw e;
}
}
Now instead of using the typical pstmt.executeBatch(). I want to use spring batch update as follows:
public void save(Table table, Connection conn) throws Exception{
String sql = createSaveQuery(table);//CREATES the INSERT Query
getJdbcTemplate().batchUpdate(sql.toString(), new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int j) throws SQLException {
//PROBLEM AREA: How to map this for each insert statement?
for(int i =0; i < params.size(); i++){
ps.setString(i+1, (String)params.get(i));
}
}
#Override
public int getBatchSize() {
return 0;
}
});
}
But I cannot figure out how to set the params for the each insert Query. As in the we can set the pstmt.setString(i, params.get(i)); for each row. How to achieve the same in 'new BatchPreparedStatementSetter()'.
any suggestions will be appreciated. If you need to further imporve the explanation. Please let me know.
I guess it's against Spring Batch nature to create dynamic SQL queries. That's because of PreparedStatement nature (read more here: What does it mean when I say Prepared statement is pre-compiled?). Long story short - PreparedStatements are compiled on DB side and they are really fast. If you modify SQL query, it can't be reused. It is hard to give advise without knowing of actual DB schema, but in general you should have one POJO for CUSTOMER table (I hope you don't have BLOBS) and you should read-write all fields. It will be faster than "optimized" dynamic query.

ResultSet to HashMap

I am trying to pass the output of a ResultSet to Java HashMap.
Map<Integer, String> sIDpNumberHashMap = new HashMap<Integer, String>();
while (DBresult.next()) {
int sID = DBresult.getInt("slrid");
String pNumber = DBresult.getString("pNumber");
sIDpNumberHashMap.put(sID , pNumber );
System.out.println("Output1"+ sID + "\t" + pNumber + "\n");
}
System.out.println("Output2" + "\n" + sIDpNumberHashMap);
While the Output1 is showing all the records(from the DB). The put command only takes the last value from the ResultSet in.
Output1:
502332262 101E2571G103
502332262 101E2571G103
502332262 116E3139P001
502332262 117E3640G025
502332262 314B7159G003
502332262 117E3640G025
Output2:
{502332262=117E3640G025}
How do I make the put command to iterate over the results from the ResultSet?
All your IDs are identical (502332262), and HashMap doesn't allow duplicate keys. That's the reason you see only one entry in the HashMap (containing the last value you put in the Map).
If you want to allow duplicates, consider a different collection to hold the data. For example, you can use an ArrayList<SomeClass> where SomeClass contains the two properties you read from the DB.
I might be late but I believe someone can get an idea from what I did.
Recently I had an almost similar challenge where I wanted to build dynamic query results (whatever select query just returns its JSON list and question would vary a lot and I can't write method per query so I had to come up with something everyone will be calling).
Below is my sample code:
public <T> List<T> findWithDynamicQuery(String query){
Connection conn = null;
PreparedStatement statement = null;
List<Object> mResults = new ArrayList<>();
try {
conn = //use your connection parameters;
} catch (SQLException e) {
e.printStackTrace(out);
return (List<T>) mResults;
}
try {
statement = conn.prepareStatement(query);
ResultSet result = statement.executeQuery();
ResultSetMetaData metaData = result.getMetaData();
int column = metaData.getColumnCount();
while (result.next()) {
HashMap<Object, Object> rows = new HashMap<>();
for(int i = 1; i <= column; i++){
rows.put(metaData.getColumnLabel(i), result.getObject(i));
}
mResults.add(rows);
}
result.close();
return (List<T>) mResults;
} catch (SQLException e) {
e.printStackTrace(out);
}finally{
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
}
}
}
return (List<T>) mResults;
}

Unable to get correct json values from jersey response

I am fetching values from database and I have a webservice which returns a list of values fetched from database which I am sending it to front-end view layer on request from angular like a webservice call
The problem is I am getting incorrect json values. It getting keys instead of values. When I see it in chrome console the json is like:
{"jobName":"NASA Scientist",
"jobPrimarySkill":null,
"jobRole":"JOB_ROLE",
"jobDesignation":"JOB_EXP", // keyname instead of value (Incorrect values)
"jobDescription":"JOB_DESCRIPTION", // keyname instead of value (Incorrect values)
"jobSalaryRange":"JOB_POSITIONS", // keyname instead of value (Incorrect values)
"jobExp":"JOB_SAL_RANGE", // keyname instead of value (Incorrect values)
"jobPositions":"JOB_POSTEDBY", // keyname instead of value (Incorrect values)
"jobPostedBy":null}
I have my back end code like this:
#Path("/FetchJobSummary")
public class FetchJobSummaryService {
FetchJobSummaryDAO dao = new FetchJobSummaryDAO();
#GET
#Produces(MediaType.APPLICATION_JSON)
public List fetch() {
System.out.println(dao.getJobSummaries());
// prints object [com.RTH.WebServices.JobSummaries#6d90d6c5,
com.RTH.WebServices.JobSummaries#6d90d6c5,
com.RTH.WebServices.JobSummaries#6d90d6c5,....
return dao.getJobSummaries();
}
}
DAO class:
public class FetchJobSummaryDAO {
public List getJobSummaries() {
JobSummaries jobSummaries = new JobSummaries();
List<JobSummaries> jobSummaryList = new ArrayList<JobSummaries>();
try {
Connection con = DBConnection.getConnection();
String query = "select JOB_NAME,JOB_DESCRIPTION,JOB_ROLE,JOB_PRIMARY_SKILL,JOB_DESIGNATION,JOB_EXP,JOB_SAL_RANGE, JOB_POSTEDBY from TBL_JOBPOSTING";
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnsNumber = rsmd.getColumnCount();
while (rs.next()) {
jobSummaries.setJobName(rs.getString("JOB_NAME"));
jobSummaries.setJobDescription("JOB_DESCRIPTION");
jobSummaries.setJobRole("JOB_ROLE");
jobSummaries.setJobPrimarySkill(rs.getString("JOB_PRIMARY_SKILL"));
jobSummaries.setJobDesignation("JOB_DESIGNATION");
jobSummaries.setJobExp("JOB_EXP");
jobSummaries.setJobSalaryRange("JOB_SAL_RANGE");
jobSummaries.setJobPostedBy("JOB_POSTEDBY");
jobSummaryList.add(jobSummaries);
for (int i = 1; i <= columnsNumber; i++) {
if (i > 1) {
System.out.print(", ");
}
String columnValue = rs.getString(i);
System.out.print(rsmd.getColumnName(i) + " " + columnValue); //Prints correct values
}
}
} catch (SQLException ex) {
Logger.getLogger(FetchJobSummaryDAO.class.getName()).log(Level.SEVERE, null, ex);
}
return jobSummaryList;
}
I am sure its the problem somewhere in java than in frontend but not to figure out what is wrong
you have
jobSummaries.setJobRole("JOB_ROLE");
instead of
jobSummaries.setJobRole(rs.getString("JOB_ROLE"));
so you should have
jobSummaries.setJobName(rs.getString("JOB_NAME"));
jobSummaries.setJobDescription(rs.getString("JOB_DESCRIPTION"));
jobSummaries.setJobRole(rs.getString("JOB_ROLE"));
jobSummaries.setJobPrimarySkill(rs.getString("JOB_PRIMARY_SKILL"));
jobSummaries.setJobDesignation(rs.getString("JOB_DESIGNATION"));
jobSummaries.setJobExp(rs.getString("JOB_EXP"));
jobSummaries.setJobSalaryRange(rs.getString("JOB_SAL_RANGE"));
jobSummaries.setJobPostedBy(rs.getString("JOB_POSTEDBY"));
jobSummaryList.add(jobSummaries);
As you are taking "JOB_PRIMARY_SKILL" from result set using rs.getString, try
rs.getString("JOB_DESIGNATION"), maybe you are missing out that.
Your error is from here :`
while (rs.next()) {
jobSummaries.setJobName(rs.getString("JOB_NAME"));
jobSummaries.setJobDescription("JOB_DESCRIPTION");
jobSummaries.setJobRole("JOB_ROLE");
jobSummaries.setJobPrimarySkill(rs.getString("JOB_PRIMARY_SKILL"));
jobSummaries.setJobDesignation("JOB_DESIGNATION");
jobSummaries.setJobExp("JOB_EXP");
jobSummaries.setJobSalaryRange("JOB_SAL_RANGE");
jobSummaries.setJobPostedBy("JOB_POSTEDBY");
jobSummaryList.add(jobSummaries);
it should be :
` while (rs.next()) {
jobSummaries.setJobName(rs.getString("JOB_NAME"));
jobSummaries.setJobDescription(rs.getString("JOB_DESCRIPTION"));
jobSummaries.setJobRole(rs.getString("JOB_ROLE"));
jobSummaries.setJobPrimarySkill(rs.getString("JOB_PRIMARY_SKILL"));
jobSummaries.setJobDesignation(rs.getString("JOB_DESIGNATION"));
jobSummaries.setJobExp(rs.getString("JOB_EXP"));
jobSummaries.setJobSalaryRange(rs.getString("JOB_SAL_RANGE"));
jobSummaries.setJobPostedBy(rs.getString("JOB_POSTEDBY"));
jobSummaryList.add(jobSummaries);
`

How to write sql innerquery in hibernate?

presently my method uses basic jdbc concept like this,
public static ArrayList<VehicleDetailsBean>
getAllVehicleDetails(String groupId,String clientId)
throws ClassNotFoundException, SQLException {
ArrayList<VehicleDetailsBean> vehicleDetailsList =
new ArrayList<VehicleDetailsBean>();
try {
Statement statement = connection.createStatement();
String sql ="SELECT a.vehicleno,a.lat,a.lng,a.status, "+
"a.rdate,a.rtime from latlng a,vehicle_details b where"+
"a.vehicleno=b.vehicleno and b.clientid='"+
clientId+"' and b.groupid in(select groupid from group_details"+
" where groupname='"+groupId+"' and clientid='"+clientId+"')";
ResultSet rs = statement.executeQuery(sql);
while(rs.next()) {
VehicleDetailsBean vehicleDetailsBean=new VehicleDetailsBean();
vehicleDetailsBean.setVehicleno(rs.getString("vehicleno"));
vehicleDetailsBean.setLat(rs.getString("lat"));
vehicleDetailsBean.setLng(rs.getString("lng"));
vehicleDetailsBean.setStatus(rs.getString("status"));
vehicleDetailsBean.setRdate(rs.getInt("rdate"));
vehicleDetailsBean.setRtime(rs.getString("rtime"));
vehicleDetailsList.add(vehicleDetailsBean);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
it returns an ArrayList. Now I want to change it to hibernate SO I will change above code as,
query = session.createQuery(hqlquery);//I am not getting how to write hqlquery
List<Object[]> groupList = query.list();
for(Object[] arr : groupList){
System.out.println(Arrays.toString(arr));
}
by doing this it returns List object but my method has to return ArrayList. So can any one help me to write query and return the result as ArrayList.
You can do it the ugly way:
query = session.createQuery(hqlquery); // Take a walk in Hibernate Docs for HQL Queries
List<?> groupList = query.list();
ArrayList<VehicleDetailsBean> result = new ArrayList<VehicleDetailsBean>(groupList.size());
for (Object o : groupList) {
result.add((VehicleDetailsBean) o);
}
return result;
If you want to return it as ArrayList, you can just addAll it with your ArrayList.
List<VehicleDetailsBean> groupList = (List<VehicleDetailsBean>) query.list();
vehicleDetailsList.addAll(groupList);
return vehicleDetailsList;
You can see similar question here and a way to cast here
EDIT
Or try to add entity
List<LatitudeBean> bean = (List<LatitudeBean>) session.createSQLQuery(query)
.addEntity(LatitudeBean.class).list();
//add to ArrayList here

Categories