Element not found while Searching object in HashSet or HashMap

Java provides extensive list of Collection classes to group similar type of objects. Covering complete Java Collection framework details is huge task and is out of scope for this post. Here in this post, I just wanted to cover one scenario related to collections which use hash codes for storing objects.

Scenario:
Collections allow to store user defined class objects. Once the objects are added into collection, what will happen if the object properties gets updated? Will the object in collection get updated with changed values? Will the object be still searchable in collection?

Lets see below example to get the answers of above questions –

Employee.java: Model class which we want to add into Set.


public class Employee {

private long empId;
private String name;
private int salary;


public Employee(long empId, String name, int salary) {

super();
this.empId = empId;
this.name = name;
this.salary = salary;

}
public long getEmpId() {

return empId;

}
public void setEmpId(long empId) {

this.empId = empId;

}public String getName() {

return name;

}
public void setName(String name) {

this.name = name;

}
public int getSalary() {

return salary;

}
public void setSalary(int salary) {

this.salary = salary;

}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (empId ^ (empId >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + salary;
return result;
}


@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (empId != other.empId)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (salary != other.salary)
return false;
return true;
}


@Override
public String toString() {
return "Employee [empId=" + empId + ", name=" + name + ", salary=" + salary + "]";
}


}

ImmutableObjInSet.java: A main class used for testing purpose. Here we will create object of Employee class and add into Set. After adding object we will try to update property of one object and see how it will impact the stored object in Set.

public class ImmutableObjInSet {

  public static void main(String[] args) {
Set set = new HashSet();

    //Create Employee objects
Employee emp1 = new Employee(10, "Mr. ABC", 10000);
Employee emp2 = new Employee(20, "Ms. PQR", 11000);
Employee emp3 = new Employee(30, "Mr. XYZ", 15000);

    //Add Employee objects in Set
set.add(emp1);
set.add(emp2);
set.add(emp3);

    //find object in set
System.out.println("[Before updates]Is Employee found? "+set.contains(emp1));
emp1.setSalary(12000);

    //Printing employee object to check if the updates are reflected in set
for(Employee emp : set) {
System.out.println(emp);
}

System.out.println("[After updates]Is Employee found? "+set.contains(emp1));
}
}

Output: Above program will give below output.

[Before updates]Is Employee found? true

Employee [empId=10, name=Mr. ABC, salary=12000]
Employee [empId=20, name=Ms. PQR, salary=11000]
Employee [empId=30, name=Mr. XYZ, salary=15000]

[After updates]Is Employee found? false

In above output,  salary of emp1 object stored in HashSet is correctly updated to 12000. When we tried to search the modified object in Set using contains() method, the object is not found and method returned false.

How hash based collections work in this case: 

It is recommended to use all object properties to calculate the hash code of object in order to generate different hash code for different state of the objects. So any change is properties value of object result is different hash code.

When the object is stored as Key in HashMap or as value in HashSet, internally HashSet stores it based on the its hash code. Storage location gets decided based on objects hash code. These collections use object’s hash code again to correctly locate the stored elements while searching for particular object.

In this example, when the employee object is added in HashSet, internally HashSet retrieves the hash code of employee object and allocates the space based on the hash code. After the object is added in collection if the object state gets changed, it will generate different hash code. Updated values gets reflected in the object stored the collection as the object reference is same. But when the updated object is searched using contains method, HashSet tries to search the object as per the new hash code and unable to locate the stored object as object was allocated a storage based on old hash code.

Same scenario is applicable for the keys in HashMap.

Thanks,
Lalit

Different Ways of Calling Stored Procedure using Spring

There are many ways we can call database stored procedure from Spring framework. Here are some which I know/used:

Lets us consider test stored procedure as:

DELIMITER $$
DROP PROCEDURE IF EXISTS test.PROC_TEST $$
CREATE PROCEDURE PROC_TEST(IN firstName VARCHAR(30), IN lastName VARCHAR(30), OUT message VARCHAR(100))
BEGIN
SET message= concat('Welcome: ',concat(firstName, lastName));
END $$
DELIMITER ;

We will assume that required JdbcTemplate is populated in our DAO class.

  • Using CallableStatement:

This is similar to plain JDBC where we create CallableStatement from Connection object and use it for calling stored procedure.
The Code:

final String procedureCall = "{call PROC_TEST(?, ?, ?)}";
Connection connection = null;
try {

//Get Connection instance from dataSource
connection = jdbcTemplate.getDataSource().getConnection();
CallableStatement callableSt = connection.prepareCall(procedureCall);
callableSt.setString(1, "Lalit");
callableSt.setString(2, " Chaudhari");
callableSt.registerOutParameter(3, Types.VARCHAR);


//Call Stored Procedure
callableSt.executeUpdate();
System.out.println(callableSt.getString(3));

}catch (SQLException e) {

e.printStackTrace();

} finally {

if(connection != null)
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

In above code, as we are directly using the connection object, connection should be closed before exiting method.

  • Using CallableStatementCreator:

CallableStatementCreator interface has only one method:
public CallableStatement createCallableStatement(Connection connection)
This method takes Connection object as parameter. Using this connection object we can create and return the CallableStatement. We don’t need to care about closing connection object in this method.

The Code:

SqlParameter fNameParam = new SqlParameter(Types.VARCHAR);
SqlParameter lNameParam = new SqlParameter(Types.VARCHAR);
SqlOutParameter outParameter = new SqlOutParameter("msg", Types.VARCHAR);

List paramList = new ArrayList();
paramList.add(fNameParam );
paramList.add(lNameParam );
paramList.add(outParameter);


final String procedureCall = "{call PROC_TEST(?, ?, ?)}";
Map<String, Object> resultMap = jdbcTemplate.call(new CallableStatementCreator() {

@Override
public CallableStatement createCallableStatement(Connection connection)
throws SQLException {

CallableStatement callableStatement = connection.prepareCall(procedureCall);
callableStatement.setString(1, "Lalit");
callableStatement.setString(2, " Chaudhari");
callableStatement.registerOutParameter(3, Types.VARCHAR);
return callableStatement;

}
}, paramList);
System.out.println(resultMap.get("msg"));

Here we have passed CallableStatementCreator instance and list of SqlParameter to jdbcTemplate.call method. Using SqlParameter we can declare name and type of the parameter which stored procedure accepts.
Note while getting result(message) beck from resultMap we have used “msg” as key, which is specified as name while creating SqlOutParameter.

  • Using SimpleJdbcCall:

We don’t need to create CallableStatement when using SimpleJdbcCall class. Just specify name of the procedure and map the required parameters.
The Code:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)

.withProcedureName("PROC_TEST");

Map<String, Object> inParamMap = new HashMap<String, Object>();
inParamMap.put("firstName", "Smita");
inParamMap.put("lastName", "Chaudhari");
SqlParameterSource in = new MapSqlParameterSource(inParamMap);


Map<String, Object> simpleJdbcCallResult = simpleJdbcCall.execute(in);
System.out.println(simpleJdbcCallResult);

  • Using org.springframework.jdbc.object.StoredProcedure class:

StoredProcedure is an abstract class provided by Spring. By subclassing StoredProcedure we need to pass name of the stored procedure and give a call to enclosing execute method with required parameters.

The Code:
First Create subclass of StoredProcedure: MyStoredProcedure

class MyStoredProcedure extends StoredProcedure {

public MyStoredProcedure(JdbcTemplate jdbcTemplate, String name) {

super(jdbcTemplate, name);
setFunction(false);

}

}

Use MyStoredProcedure to call database stored procedure:


//Pass jdbcTemlate and name of the stored Procedure.
MyStoredProcedure myStoredProcedure = new MyStoredProcedure(jdbcTemplate, "PROC_TEST");


//Sql parameter mapping
SqlParameter fNameParam = new SqlParameter("fName", Types.VARCHAR);
SqlParameter lNameParam = new SqlParameter("lName", Types.VARCHAR);
SqlOutParameter msgParam = new SqlOutParameter("msg", Types.VARCHAR);
SqlParameter[] paramArray = {fNameParam, lNameParam, msgParam};


myStoredProcedure.setParameters(paramArray);
myStoredProcedure.compile();


//Call stored procedure
Map storedProcResult = myStoredProcedure.execute("Jidnyasa", " Chaudhari");
System.out.println(storedProcResult);

Summary
These are some of the ways by which we can call StoredProcedure using Spring. There might be some approaches which I have missed to list here. Out of these approaches I feel last two: Using SimpleJdbcCall and StoredProcedure are more powerful and easy. Suggestions are most welcome.

Thanks,
Lalit

Call window.open using POST method

To open new window from jsp page generally we use window.open method. This method opens the URL specified as first parameter to this method in new window using HTTP GET method. GET method limits that data to be passed in request URL. So what if you want to pass large data to new window?

I found sample code below which demonstrates how to call window.open with POST method.

function openWindow(){
var form = document.createElement('FORM');
form.method='POST';
form.action = 'PageToOpen.html';
form.target = 'newWindow'; // Specify the name of the window(second parameter to window.open method.)

var input = document.createElement("INPUT");
input.id="q";
input.type="hidden";
input.value="SOme large content";
form.appendChild(input);

document.body.appendChild(form);

window.open("","newWindow","location=yes,width=400,height=400");
form.submit();
}

Thanks,
Lalit