Custom mapper class is not getting the result? - java

I am writing a DAO application and I want to get zone data. I have written my own mapper class. I am unable to get values. It is throwing an exception.
Mapper class
public class zoneMapper implements RowMapper {
#Override
public Object mapRow(ResultSet resultSet, int rowNum) throws SQLException {
ZoneBean zone=new ZoneBean();
zone.setZoneId(resultSet.getInt("ZONE_ID"));
zone.setZoneName(resultSet.getString("ZONE_NAME"));
return zone;
}
dao call
List<ZoneBean> zoneList=new ArrayList<ZoneBean>();
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
zoneList = (List<ZoneBean>) jdbcTemplate.queryForObject(
queriesConstants.GET_ZONES, new zoneMapper());
}catch(Exception e){
System.out.println("getZones "+e.getMessage());
//logger.error("getZones "+e.getMessage());
}
**Exception is**
getZones Incorrect result size: expected 1, actual 17

getZones Incorrect result size: expected 1, actual 17
You retrieve multiple elements when your execute your query.
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) is therefore not suitable for your need because it executes a query given static SQL, mapping a single result row to a Java object via a RowMapper.
You should use rather this method :
public <T> List<T> query(String sql, RowMapper<T> rowMapper)which executes a query given static SQL, mapping each row to a Java object via a RowMapper.

Related

How to call an Oracle PL-SQL procedure which has a custom defined complex Object type as input parameter from Java

In Oracle database, I have defined an Object type which has a nested Object type. Assuming, that I have a PL-SQL procedure which uses the parent Object type as input parameter, I'm looking for a performant way to call the procedure from Java code.
CREATE OR REPLACE TYPE STUDENT AS OBJECT (
name VARCHAR2(35),
address HOME_ADDRESS
);
CREATE OR REPLACE TYPE HOME_ADDRESS AS OBJECT (
street VARCHAR2(35)
);
CREATE OR REPLACE PROCEDURE TEST(
studentObject IN STUDENT
)
IS
BEGIN
... do domething ...
END;
On Java side, I have the relevant POJOs holding data.
I have tried using Spring SimpleJdbcCall along with a StructMapper.
StructMapper<Demo> MAPPER = BeanPropertyStructMapper.newInstance(Student.class);
SqlParameter studentIn = new SqlParameter("studentObject", Types.STRUCT, "MY_SCHEMA.STUDENT");
SimpleJdbcCall sjc = new SimpleJdbcCall(datasource).withSchemaName("MY_SCHEMA")
.withProcedureName("TEST")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("studentObject")
.declareParameters(studentIn);
final MapSqlParameterSource inputParameters = new MapSqlParameterSource().addValue("studentObject",
new SqlStructValue<Student>(studentInstance, MAPPER, "MY_SCHEMA.STUDENT"));
final Map<String, Object> execute = sjc.execute(inputParameters);
The above example works when not using nested Object types. When using a nested Object type, a java.sql.SQLException: Fail to convert to internal representation exception is thrown. Besides that, spring-data-oracle and spring-data-jdbc-ext projects seems to not be maintained anymore and I would like to avoid using them. The above code is not my real code but an example for the question's purposes. My real Object type and the corresponding POJO in Java might be quite complex with many nested objects and arrays.
Eventually, I have come to the solution of using Java wrapper classes which extend my plain POJOs and implement the java.sql.SQLData interface. The implementation of readSQL method instructs on how to populate the object with data read from the database, while the writeSQL writes this object to the given SQL data stream.
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
public class StudentMapper extends Student implements SQLData {
public StudentMapper(Student student) {
setName(student.getName());
setAddress(student.getAddress());
}
#Override
public String getSQLTypeName() throws SQLException {
return "MY_SCHEMA.STUDENT";
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
setAddress((Address) stream.readObject());
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getName());
final AddressMapper addressMapper = new AddressMapper(getAddress());
stream.writeObject(addressMapper);
}
}
final StudentMapper mapper = new StudentMapper(studentInstance);
CallableStatement callableStatement = ...
callableStatement.setObject("studentObject", mapper);
callableStatement.execute();
you can use JPA 2.1 or upper for call stored procedure in Oracle DB.
for example:
https://thoughts-on-java.org/call-stored-procedures-jpa/

How to assign all the '?' with object's data in JDBC template?

I am new in spring boot.
I have created on method in spring boot, please see the below function:
public ArrayList<ShipmentDetailsVO> getShipmentStatus(
ShipmentDetailsVO shpmntpert) {
return jdbcTemplate.query("select * from SELECT_SEARCH_DETAILS(?,?,?,?,?,?,?,?)",new ResultSetExtractor<ArrayList<ShipmentDetailsVO>>(){
#Override
public ArrayList<ShipmentDetailsVO> extractData(ResultSet rs) throws SQLException,
DataAccessException {
shipmentDao = new ArrayList<ShipmentDetailsVO>();
while(rs.next()) {
shipmentDetDaoObj =new ShipmentDetailsVO();
shipmentDetDaoObj.setContractNumber(rs.getString(1));
System.out.println("hello" + rs.getString(1));
shipmentDetDaoObj.setOrderNumber(rs.getString(2));
System.out.println(rs.getString(2));
shipmentDetDaoObj.setShipmentNumber(rs.getString(3));
shipmentDetDaoObj.setShipmentControlNo(rs.getString(4));
shipmentDetDaoObj.setStatusCode(rs.getString(5));
shipmentDetDaoObj.setStatusDateStr(rs.getString(6));
shipmentDetDaoObj.setLastUpdatedtStr(rs.getString(7));
shipmentDetDaoObj.setResendFlag(false);
shipmentDetDaoObj.setSourceSystem(rs.getString(8));
shipmentDetDaoObj.setDestinationSystem(rs.getString(9));
shipmentDetDaoObj.setRfid(rs.getString(10));
shipmentDetDaoObj.setUid(rs.getString(11));
shipmentDetDaoObj.setShipmentSeqId(rs.getString(12));
shipmentDao.add(shipmentDetDaoObj);
}
return shipmentDao;
}
});
}
I don't have any idea how to assign all the '?' with object's(shpmntpert) data. Can any one kindly help on the same.
From JdbcTemplate documentation
You can find one of the query the signature
public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse)
throws DataAccessException
With an explanation of the interesting parameters
sql - SQL query to execute
args - arguments to bind to the query
So you can see that the array is used to bing the values. That means that for a query like
select * from table where name = ? and weight= ?
you need to generate an array with those values in the correct order
new Object[]{name, weight}
An other signature show you that they provide some setter for PreparedStatement named PreparedStatementSetter.
public <T> T query(String sql,
PreparedStatementSetter pss,
ResultSetExtractor<T> rse)
throws DataAccessException
Where the PreparedStatementSetter interface have only one methods to implements
void setValues(PreparedStatement ps)
That will let you set the values like you want fron this setter. You could pass the instance to an implementation of this interface or directly in the code like
final MyInstance m = new MyInstance();
jdbcTemplate.query("select * from table where name = ? and weight= ?",
new PreparedStatementSetter(){
#Override
public void setValues(PreparedStatement ps){
ps.setString(1, m.getName);
ps.setInt(2, m.getWeight);
}
}, myResultSetExtractor);
Note : This is a quick reading of the documentation knowing that I never used this API. But from what I have read, this should work just fine.

JDBI, retrieve data with sql query into customized object(constructor) instead of Map

So when we use JDBI to query from database, it is getting it into a Map<String, Object> type.
I want to get it as my customized object (constructor) instead of Map<String, Object>.
DBI dbi = establishConnection(url, userName, passWord);
Handle handle = dbi.open();
List<Map<String, Object>> rs = handle.select("select * from sometable");
Instead I want to use:
List<customizedObject> rs = handle.select("select * from sometable");
Where customizedObject class is an object that contains all the column properties with it.
Is there any way to do this? I found some relative documentation, but I cannot really understand the implementation.
http://jdbi.org/sql_object_api_queries/
Please also see the previous page in the documentation that shows how to link your Handle or DBI with the mappers.
Essentially, you need a mapper to convert the ResultSet to the desired object and an interface to refer to the mapper.
Let's assume a minimal example. First the mapper needs to be provided:
public class CustomizedObjectMapper implements ResultSetMapper<customizedObject> {
#Override
public customizedObject map(int index, ResultSet r, StatementContext ctx)
throws SQLException {
return new customizedObject(r.getString("uuid"), r.getString("other_column"));
}
}
Then we need an interface to define which query provides the data that is passed to the mapper class. One result row leads to one invocation of CustomizedObjectMapper.map(...):
#RegisterMapper(CustomizeObjectMapper.class)
public interface CustomizeObjectQuery {
#SqlQuery("Select uuid, other_column from schema.relation")
List<customizedObject> get();
}
Finally, the objects can be retrieved: List<customizedObject> test = dbi.open(CustomizeObjectQuery.class).get().
Your can also put the components together on an individual basis like so and omit the interface:
dbi.open().createQuery("Select uuid, other_colum from schema.relation").map(new EventMapper()).list()

Modelling Database Entities using Spring Rowmapper Object

I'm in the process of creating a front end for a Database Driven application and Could do with some advice. I have the following basic entities in my database:
aspect
aspect_value
As you can image I can have many aspect value to each aspect so that when a user records an aspect they can select more than one value per aspect... simple.
I've created an POJO entity to model each aspect an my question is this... using Spring and the jdbcTemplate how would I be able to create the desired composite relationship using org.springframework.jdbc.core.RowMapper i.e. each aspect object containing one or more aspect value ojects? And for that matter I would appreciate if you could please let me know if this would be the best way to do it. I'm keen at some point to delve deeper into ORM but I've been put off so far by the number of issues I've encountered which has slowed down my development and led to the decision to use jdbcTemplate instead.
Thanks
You can use a RowMapper if you are storing aspect_values in your aspect object as objects. Each call to RowMapper returns an object so you'll end up with a collection of aspect_values. If you need to build an aspect object (or objects) with values contained in the aspect_value table then a ResultSetExtractor is the better choice.
Here are my examples as promised. I have to type these in by hand because our development network is on an internal network only so any typos are copy errors and not errors in the code. These are abbreviated versions of inner classes in my DAO:
This maps a single row in the ResultSet to an object:
public List<MessageSummary> getMessages(Object[] params)
{
// mList is filled with objects created in MessageRowMapper,
// so the length of the list equal to the number of rows in the ResultSet
List<MessageSummary> mList = jdbcTemplate.query(sqlStr, new MessageRowMapper(),
params);
return mList;
}
private final class MessageRowMapper implements RowMapper<MessageSummary>
{
#Override
public MessageSummary mapRow(ResultSet rs, int i) throws SQLException
{
MessageSummary ms = new MessageSummary();
ms.setId(rs.getInt("id"));
ms.setMessage(rs.getString("message"));
return ms;
}
}
ResultSetExtractor works on the same idea except you map the entire set yourself instead of just converting a row into an object. This is useful when your object has attributes from multiple rows.
public Map<Integer, List<String>> getResults(Object[] params)
{
Map<Integer, List<String>> result = jdbcTemplate.query(sqlStr, new ResultExtractor(),
params);
return result;
}
private final class ResultExtractor implements ResultSetExtractor<Map<Integer, List<String>>>
{
#Override
public Map<Integer, List<String>> extractData(ResultSet rs)
throws SQLException, DataAccessException
{
Map<Integer, List<String>> resultMap = new HashMap<Integer, List<String>>();
while (rs.next())
{
int id = rs.getInt("id");
List<String> nameList = resultMap.get(id);
if (nameList == null)
{
nameList = new ArrayList<String>();
resultMap.put(id, nameList);
}
nameList.add(rs.getString("name"));
}
return resultMap;
}
}
The RowMapper interface provides a method
Object mapRow(ResultSet rs,
int rowNum)
throws SQLException
You implement this method in a class and provide code to populate your entity object with values held in the row of the ResultSet rs. To obtain the resultset itself from database , you can use JdbcTemplate.select method's overload
List jdbcTemplate.query(String sql, RowMapper mapper )

How to get parameters from PreparedStatement?

I'm writing generic logger for SQLException and I'd like to get parameters that were passed into PreparedStatement, how to do it ? I was able to get the count of them.
ParameterMetaData metaData = query.getParameterMetaData();
parameterCount = metaData.getParameterCount();
Short answer: You can't.
Long answer: All JDBC drivers will keep the parameter values somewhere but there is no standard way to get them.
If you want to print them for debugging or similar purposes, you have several options:
Create a pass-through JDBC driver (use p6spy or log4jdbc as a basis) which keeps copies of the parameters and offers a public API to read them.
Use Java Reflection API (Field.setAccessible(true) is your friend) to read the private data structures of the JDBC drivers. That's my preferred approach. I have a factory which delegates to DB specific implementations that can decode the parameters and that allows me to read the parameters via getObject(int column).
File a bug report and ask that the exceptions are improved. Especially Oracle is really stingy when it comes to tell you what's wrong.
Solution 1: Subclass
Simply create a custom implementation of a PreparedStatement which delegates all calls to the original prepared statement, only adding callbacks in the setObject, etc. methods. Example:
public PreparedStatement prepareStatement(String sql) {
final PreparedStatement delegate = conn.prepareStatement(sql);
return new PreparedStatement() {
// TODO: much more methods to delegate
#Override
public void setString(int parameterIndex, String x) throws SQLException {
// TODO: remember value of X
delegate.setString(parameterIndex, x);
}
};
}
If you want to save parameters and get them later, there are many solutions, but I prefer creating a new class like ParameterAwarePreparedStatement which has the parameters in a map. The structure could be similar to this:
public class ParameterAwarePreparedStatement implements PreparedStatement {
private final PreparedStatement delegate;
private final Map<Integer,Object> parameters;
public ParameterAwarePreparedStatement(PreparedStatement delegate) {
this.delegate = delegate;
this.parameters = new HashMap<>();
}
public Map<Integer,Object> getParameters() {
return Collections.unmodifiableMap(parameters);
}
// TODO: many methods to delegate
#Override
public void setString(int parameterIndex, String x) throws SQLException {
delegate.setString(parameterIndex, x);
parameters.put(parameterIndex, x);
}
}
Solution 2: Dynamic proxy
This second solution is shorter, but seems more hacky.
You can create a dynamic proxy by calling a factory method on java.lang.reflect.Proxy and delegate all calls on the original instance. Example:
public PreparedStatement prepareStatement(String sql) {
final PreparedStatement ps = conn.prepareStatement(sql);
final PreparedStatement psProxy = (PreparedStatement) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{PreparedStatement.class}, new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("setLong")) {
// ... your code here ...
}
// this invokes the default call
return method.invoke(ps, args);
}
});
return psProxy;
}
Then you intercept the setObject, etc. calls by looking at method names and looking to the second method arguments for your values.
This article, from Boulder, ahtoulgh DB 2 "specific", gives a complete example of ParameterMetadata usage.

Categories