Replacing RowMapper object with java 8 lambda expression - spring

I am using Spring JdbcTemplate class in my project.I have the following code:
List<PersonDTO> personList = jdbcTemplate.query(query, new RowMapper<PersonDTO>() {
#Override
public PersonDTO mapRow(ResultSet rs, int rowNumber) throws SQLException {
PersonDTO personDTO = new PersonDTO ();
personDTO.setPerId(rs.getString("COL_ONE"));
personDTO.setIdTypeCd(rs.getString("COL_TWO"));
return personDTO;
}
});
Now I want to replace the anonymous class RowMapper with java8 lambda expression something like this:
Runnable r1 = () -> {
System.out.println("My Runnable");
};
Is it possible?

AFAIK RowMapper is a functional interface, so this would work. lambda expression can't declare checked exceptions, so you will need to wrap that...
jdbcTemplate.query(query, (ResultSet rs, int rowNumber) -> {
PersonDTO personDTO = new PersonDTO ();
personDTO.setPerId(rs.getString("COL_ONE"));
personDTO.setIdTypeCd(rs.getString("COL_TWO"));
return personDTO;
});
The comments are spot on: since that functional interface already declares to throw the exception, there is no need to catch it.

try this
List<PersonDTO> personList = jdbcTemplate.query(query, (rs,rowNumber) -> {
try
{PersonDTO personDTO = new PersonDTO ();
personDTO.setPerId(rs.getString("COL_ONE"));
personDTO.setIdTypeCd(rs.getString("COL_TWO"));
return personDTO;
}catch(SQLException e){
throw new RuntimeException("your error message",e) // or other unchecked exception here
}
});

Related

How to mock JdbcTemplace with RowMapper

I have a EmployeeDaoImpl class wish to mock JdbcTemplate and Rowmapper and set the dummy data to ResultSet. Given small snipper code below. Could you help me out on this.
public List<EmployeeTask> getDetails(String sql,String countryCode, int departmentNumber, Status status,
int sectionNumber,int aisle, String zone ){
if(status!=null) {
return jdbcTemplate.query(sql, new Object[] { countryCode,departmentNumber, Status.OK,sectionNumber,sectionId,zone},
(ResultSet rs, int rowNum) -> {
EmployeeTask employeeTask = new EmployeeTask();
employeeTask.setDepatmentNumber(rs.getInt("dept_number"));
employeeTask.setRemartks(rs.getString("remarks"));
return employeeTask;
});
} else {
return jdbcTemplate.query(sql, new Object[] { countryCode,departmentNumber, Status.OK,sectionNumber,sectionId,zone},
(ResultSet rs, int rowNum) -> {
EmployeeTask employeeTask = new EmployeeTask();
employeeTask.setDepatmentNumber(rs.getInt("dept_number"));
employeeTask.setRemartks(rs.getString("remarks"));
return employeeTask;
});
}
}
Unfortunately, neither your question nor the code snippet explains details about the context in which this Dao is implemented and used.
In general, I would recommend you to read the following article:
https://www.baeldung.com/spring-jdbctemplate-testing
It shows some ways how to test code based on a JdbcTemplate.

Usage of custom freemarker template

I was wondering if anyone can help me with Apache FreeMarker? I'm trying to use a custom model but I can't figure it out.
Imagine I want to dump the result of a query (java ResultSet in a FreeMarker template). What is the best approach?
I have found on Google the class: ResultSetTemplateModel
import java.sql.ResultSet;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateSequenceModel;
public class ResultSetTemplateModel implements TemplateSequenceModel {
private ResultSet rs = null;
public ResultSetTemplateModel(ResultSet rs) {
this.rs = rs;
}
public TemplateModel get(int i) throws TemplateModelException {
try {
rs.next();
} catch(Exception e) {
throw new TemplateModelException(e.toString());
}
TemplateModel model = new Row(rs);
return model;
}
public int size() throws TemplateModelException {
int size=0;
try {
rs.last();
size = rs.getRow();
rs.beforeFirst();
} catch (Exception e ) {
throw new TemplateModelException( e.toString());
}
return size;
}
class Row implements TemplateHashModel {
private ResultSet rs = null;
public Row(ResultSet rs) {
this.rs = rs;
}
public TemplateModel get(String s) throws TemplateModelException {
TemplateModel model = null;
try {
model = new SimpleScalar( rs.getString(s) );
} catch (Exception e) { e.printStackTrace(); }
return model;
}
public boolean isEmpty() throws TemplateModelException {
boolean isEmpty = false;
if ( rs == null ) { isEmpty = true; }
return isEmpty;
}
}
}
And I have a very simple class (I even made it easier than previous):
public static void main(String[] args) {
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_27);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setClassForTemplateLoading(MyCLASS.class, "/");
StringWriter out = new StringWriter();
Map<String, Object> parameters = new TreeMap<>();
ResultSet rs = getResultSet("Select foo, bar FROM my_table");
parameters.put("hello", "World");
parameters.put("result", rs);
Template temp = cfg.getTemplate("template.txt");
temp.process(parameters, out);
System.out.println("out = " + out);
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
My template
Hello ${hello}
<#-- how do I specify ResultSet columns here ?? -->
How can I use the custom template?? Any advice?? I know how to load the template file. But I don't know how to specify that it is a custom model in the template.
THank you guys for the support :)
There are two ways of using ResultSetTemplateModel for wrapping ResultSet-s:
Either extend DefaultObjectWrapper by overriding handleUnknownType, where you return new ResultSetTemplateModel((ResultSet) obj) if obj is a ResultSet, otherwise call super. Then use Configuration.setObjectWrapper to actually use it.
Or, add new ResultSetTemplate(rs) to parameters instead of rs; if something is already a TempalteModel, it will not be wrapped again. Note that if you get a ResultSet from somewhere else in the template, this approach will not work as it avoids your manual wrapping, so extending the DefaultObjectWrapper is what you want generally.
Note that the ResultSetTemplateModel implementation shown is quite limited. The ObjectWrapper should be passed to the constructor as well, and stored in a final field. Then, instead of new SimpleScalar( rs.getString(s) ) it should do objectWrapper.wrap(rs.getObject(s)).

Get Statement from JDBCTemplate

I have this code,
SimpleJdbcCall sql = new SimpleJdbcCall(dataSource).withProcedureName(procName);
sql.execute(parameters);
And I believe that under the hood this uses a JDBC Statement. How can I get to that object from here? (I need to call the .getWarnings() method on the statement).
In other words how can I get SQLWarnings AND named parameters?
It took a lot of digging, but here is how you can get SQLWarnings (or Print statements) AND named parameters. I extended JdbcTemplate and overrode the handleWarnings() method, and then passed that into my SimpleJdbcCall.
public class JdbcTemplateLoggable extends JdbcTemplate{
List<String> warnings;
public JdbcTemplateLoggable(DataSource dataSource){
super(dataSource);
warnings = new ArrayList<String>();
}
protected void handleWarnings(Statement stmt){
try {
SQLWarning warning = stmt.getWarnings();
while(warning != null){
warnings.add(warning.getMessage());
warning = warning.getNextWarning();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public List<String> getWarnings(){
return warnings;
}
}
Then in my main program
JdbcTemplateLoggable template = new JdbcTemplateLoggable(dataSource);
SimpleJdbcCall sql = new SimpleJdbcCall(template).withProcedureName(procName);
sql.execute(parameters);
for(String s : template.getWarnings()){
log.info(s);
}
You should perhaps use JdbcTemplate directly, or subclass it for usage with your SimpleJdbcCall (instead of a DataSource). JdbcTemplate has a method execute(CallableStatementCreator, CallableStatementCallback), where a callback can be passed which gets the used Statement object.
You could override that method and wrap the passed callback with an own which stores the statement for later use.
public class CustomJdbcTemplate extends JdbcTemplate {
private CallableStatement lastStatement;
public CustomJdbcTemplate(DataSource dataSource) {
super(dataSource);
}
public CallableStatement getLastStatement() {
return lastStatement;
}
#Override
public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action) throws DataAccessException {
StoringCallableStatementCallback<T> callback = new StoringCallableStatementCallback<T>(action);
try {
return super.execute(csc, callback);
}
finally {
this.lastStatement = callback.statement;
}
}
private static class StoringCallableStatementCallback<T> implements CallableStatementCallback<T> {
private CallableStatementCallback<T> delegate;
private CallableStatement statement;
private StoringCallableStatementCallback(CallableStatementCallback<T> delegate) {
this.delegate = delegate;
}
#Override
public T doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
this.statement = cs;
return delegate.doInCallableStatement(cs);
}
}
}
Note that the statement will most probably be closed when you retrieve it later, so getWarnings() may cause errors, depending on used JDBC driver. So maybe you should store the warnings instead of the statement itself.

Choose Class in Birt is empty eventhough I have added jar in Datasource

Even though while creating dataset choose class window is empty. I am using Luna Service Release 2 (4.4.2).
From: http://yaragalla.blogspot.com/2013/10/using-pojo-datasource-in-birt-43.html
In the dataset class the three methods, “public void open(Object obj, Map map)”, “public Object next()” and “public void close()” must be implemented.
Make sure you have implemented these.
Here is a sample that I tested with:
public class UserDataSet {
public Iterator<User> itr;
public List<User> getUsers() throws ParseException {
List<User> users = new ArrayList<>();
// Add to Users
....
return users;
}
public void open(Object obj, Map<String, Object> map) {
try {
itr = getUsers().iterator();
} catch (ParseException e) {
e.printStackTrace();
}
}
public Object next() {
if (itr.hasNext())
return itr.next();
return null;
}
public void close() {
}
}

Incorrect number of arguments for PROCEDURE; expected 1, got 0. Cant determine the error from code

//set input parameters
Map<String,Object> inParams = new HashMap<String,Object>();
inParams.put("Sig",resourceHistoryBean.getId());
List<ResourceHistoryBean> resourceHistoryList= new ArrayList<ResourceHistoryBean>();
// define stored procedure
try{
SimpleJdbcCall readResult = new SimpleJdbcCall(getDataSource())
.useInParameterNames("Sig")
.declareParameters(new SqlParameter("Sig", Types.VARCHAR))
.withProcedureName("SP_ResourceAllocationDtls")
.withSchemaName("hrms")
.returningResultSet("ResourceHistory", new ParameterizedRowMapper<ResourceHistoryBean>() {
public ResourceHistoryBean mapRow(ResultSet rs, int rowNum)
throws SQLException {
ResourceHistoryBean bean = new ResourceHistoryBean();
resourceHistoryBean.setProjectName(rs.getString(RH_PROJECT_NAME));
return bean;
}
});
readResult.compile();
// execute stored procedure
Map<String, Object> out = readResult.execute(inParams);
resourceHistoryList = (List<ResourceHistoryBean>) out.get("ResourceHistory");
Looks like I was able to find an alternative solution to above problem (Parameter passing to stored procedure and use a mapping class as well ):
public List<ResourceHistoryBean> getResourceHistory(final ResourceHistoryBean resourceHistoryBean)throws Exception{
try {
// call stored procedure and pass parameter to it
List resourceHistoryList = getJdbcTemplate().query(
"call hrms.SP_ResourceAllocationDtls(?)",
new Object[] {resourceHistoryBean.getId()}, new HistoryMapper());
return resourceHistoryList;
} catch (Exception e) {
throw e;
} finally {
closeTemplate();
}
}
// mapper class
class HistoryMapper implements RowMapper, IDatabaseConstants {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
ResourceHistoryBean resourceHistoryBean = new ResourceHistoryBean();
resourceHistoryBean.setProjectName(rs.getString(RH_PROJECT_NAME));
return resourceHistoryBean;
}
}

Resources