How to extract a hashmap object from jdbc template query - java

I'm trying to extract 2 lists/arrays of Integers from a JDBCTemplate query.
I assume retrieving a Map would be the most practical.
The query is
Map<Integer, Integer> availabletime = jdbctemp.query("
Select a.hour,
s.duration from appointment as a inner join services as s on a.service_fid=s.id
where date=? and guru_fid=?
",date,guru_fid,//mapperlogic.class);
I need a.hour and s.duration as key value pairs of a hashmap. I'm a bit confused regarding the row mapper logic here. Ive mapped only to objects as of now like
public class RoleRowMapper implements RowMapper<Role> {
#Override
public Role mapRow(ResultSet row, int rowNum) throws SQLException {
Role role=new Role();
role.setId(row.getLong("id"));
role.setName(row.getString("name"));
return role;
}
}
` Can someone help me with extracting query results to Maps or multiple lists??

.query() will always return list. Hence, added .get(0)
public Map<Integer,Integer> getAvailableTime(Date date, Integer guru_fid) {
return jdbctemp.query("Select a.hour, s.duration from appointment as a inner join services as s on a.service_fid=s.id where date=? and guru_fid=? ",new Object[] { date, guru_fid }, (ResultSet rs) -> {
HashMap<Integer,Integer> results = new HashMap<>();
while (rs.next()) {
results.put(rs.getInt("a.hour"), rs.getInt("s.duration"));
}
return results;
}).get(0);
}

Related

jdbcTemplate.query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) does not return any resultset

I am in the process of converting creation of connection to use JdbcTemplate class which handles the creation and release of resources.
One of the implementation shown below is not returning me a resultset though the original one for the same query returns me records. I converted the below code
ps = connection.prepareStatement(strQuery, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setLong(1, getId());
objRs = ps.executeQuery();
to the one shown below. The reason I used a StreamingStatementCreator was because I needed to set
ResultSet.TYPE_SCROLL_INSENSITIVE and ResultSet.CONCUR_READ_ONLY.
objRs = (ResultSet) jdbcTemplate.query(new StreamingStatementCreator(strQuery),
new PreparedStatementSetter() {
public void setValues(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setLong(1, getId());
}
}, new CustomResultSetExtractor());
public class CustomResultSetExtractor implements ResultSetExtractor<ResultSet>{
public ResultSet extractData(ResultSet resultSet) throws SQLException, DataAccessException {
return resultSet;
}
}
I am not familiar with these methods. I tried to look for examples and I feel the code is correct by maybe I missing something. The first case returns me value in objRs and the second returns me nothing.
Method query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) Does not returns ResultSet. It calls second parameter with ResultSet as argument and returns object that was returned by it.
So you shoud process ResultSet in CustomResultSetExtractor and return onther object as a result of processing.
Based on the clue by Alexander, made use of the ColumnMapRowMapper to iterate the result set. Since I wanted a generic solution that I could use for all my queries, instead of processing the resultset in my CustomResultSetExtractor or create separate classes.
I am adding the code here so that anyone else who comes across this same situation may find it useful.
public class CustomResultSetExtractor implements ResultSetExtractor<Object>{
private RowMapper<Map<String, Object>> mapper;
public CustomResultSetExtractor(ColumnMapRowMapper mapper) {
this.mapper = mapper;
}
public List<Map<String, Object>> extractData(ResultSet rs) throws SQLException, DataAccessException {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
int row = 0;
while(rs.next()){
Map<String, Object> o = (Map<String, Object>)mapper.mapRow(rs, row++);
list.add(o);
}
return list;
}
}

How JdbcTemplate return List using implements RowMapper in Mapper?

Good evening guys, I'm trying to implement a jdbctemplate model with controller / Service / Dao / DaoImpl / and Mapper ...
But by the models I see the mapper needs to implement RowMapper or ParameterizedRowMapper and both have a maprow method that does not return a List. As I needed a List I implemented a method in the mapper to get me the list I needed. but I do not know what to call it.
In the customerList method I have to pass the CustomerMapper inside the query like this:
jdbcTemplate.query (sql, new CustomerMapper (), id);
And the customer mapper must implement either RowMapper or ParameterizedRowMapper so that the
jdbcTemplate.query accept it and along with RowMapper or ParameterizedRowMapper the maprow method must come.
When I call the Mapper method via the listCustomer when entering the CustomerMapper class it automatically enters the first mapRow method and does not enter the method I wanted listCustomer that would return the List that I need.
Any idea how to help me do this?
I need to return a list of customers. just this! But following this form of implementation...
Thank you!
My class:
#Controller
public class CustomerController {
#Autowired
private CustomerService customerService;
#ResponseBody
#RequestMapping(value = "/customer/{id}", method = RequestMethod.GET)
public Map<String, Object> searchCustomer(#PathVariable(value="id") Long id,
final HttpServletRequest request) throws IOException, SQLException {
Map<String, Object> map = new HashMap<String, Object>();
List<Customer> customerList = customerService.searchCustomer(id);
map.put("customer", customerList);
return map;
}
}
SERVICE
#Service
public class CustomerService {
#Autowired
private CustomerDAO dao;
public List<Customer> searchCustomer(Long id) throws SQLException {
return dao.listCustomer(id);
}
}
DAO
public interface CustomerDAO {
List<Customer> listCustomer(Long id);
}
DAOIMPL
public class CustomerDAOImpl implements CustomerDAO {
#Autowired
private SimpleJdbcTemplate jdbcTemplate;
String sql = "SELECT * FROM purchases C WHERE C.ID = ?";
public List<Customer> listCustomer(Long id) {
return jdbcTemplate.query(sql, new CustomerMapper(), id);
}
}
//MAPPER
public class CustomerMapper implements RowMapper<Customer>{
public Customer mapRow(ResultSet rs, int arg1) throws SQLException {
... "N" RULES INSIDE BUT DONT RETURN ArrayList OR LIST.... ANYAWAY..
//THIS METHOD IS INTERFACE INHERITANCE AND HAS TO BE IMPLEMENTED
//BUT I NEED A METHOD THAT RETURNS A LIST OF CUSTOMERS FOR THE CONTROLLER
//AND THEREFORE IT RETURNS JUST 1 CUSTOMER
}
//SO I CREATED THIS OTHER METHOD THAT RETURNS A LIST OF CUSTOMERS AND
//IMPLEMENTED IN THE INTERFACE ... BUT I DO NOT KNOW HOW THE MAPPER CALLS IT ...
//NOT MAPROW
public List<Customer> listCustomer(ResultSet rs, int arg1) throws SQLException {
List<Customer> customerList = new ArrayList<Customer>();
Customer customer = new Customer();
while (rs.next()) {
if (rs.getString("ID") != null)
customer.setEtpId(rs.getString("ID"));
......
......
customerList.add(customer);
}
return customerList;
}
}
sorry, I did not put a whole query of an example, but I'm sorry if it was poorly explained. Well, what I need is to pass the customer number to make a select in the table of purchases for example, and bring me all purchases from that customer.
for example.
Select * from table purchases p where p.customerid = 4;
This would be the query, so let's imagine that it returns 5 records. I want the mapper to return me a list of buying objects (not customer) with the 5 purchases that that customer made.
Understood now?
I will set the example for better understanding.
Thanks for the answers!
Seems like the problem lies in the understanding of RowMapper.
Since, Jdbc has no idea of how you want to map your row to an object, it asks you for the mapping. So the RowMapper's sole responsibility is to provide this mapping. Nothing else. Change your mapRow like below.
public Customer mapRow(ResultSet rs, int arg1) throws SQLException {
Customer customer = null;
if (rs.getString("ID") != null){
customer = new Customer()
customer.setEtpId(rs.getString("ID"));
......
......
}
return customer;
}
JdbcTemplate will apply this mapper to map a row with an object. If you have multiple rows, it will convert all the rows to a list of Customers using this rowMapper. JdbcTemplate will do this transparently so that you don't need to worry about it.
Edit:
After your edit, you are explaining that you need a list of purchase information from purchases table for a given customer. For that case, you actually need a PurchaseRowMapper. That will map a record from your purchases table with a Class named (maybe) PurchaseInformation. That will return you a list of PurchaseInformation objects.
I have find the solution... this example with another entity but it works!
String sqlSelectQuery = "SELECT name, email, address, telephone FROM contact";
52
List listContacts = jdbcTemplateObj.query(sqlSelectQuery, new RowMapper() {
53
public Contact mapRow(ResultSet result, int rowNum) throws SQLException {
54
Contact contactObj = new Contact();
55
contactObj.setName(result.getString("name"));
56
contactObj.setEmail(result.getString("email"));
57
contactObj.setAddress(result.getString("address"));
58
contactObj.setPhone(result.getString("telephone"));
59
return contactObj;
60
}
61
});
62
63
// Displaying The SQL Records
64
for (Contact contactDetail : listContacts) {
65
System.out.println(contactDetail.toString());
66
}
67
Rowmapper Implementaion :
public class CustomerMapper implements RowMapper<Customer>{
List<Customer> customerList = new ArrayList<Customer>();
public Customer mapRow(ResultSet rs, int arg1) throws SQLException {
Customer customer = new Customer();
while (rs.next()) {
if (rs.getString("ID") != null)
customerList.add(rs.getString("ID"));
}
customer.setEtpId(customerList);
return customer;
}
}
Above code will return list of customer's.
Please refer to https://stackoverflow.com/a/27593565/2695504 or RowMapper vs ResultSet for rowMapper explanation.

Get return value in Lambda function

I have a function call implemented using lambda which insert a row in postgres database using jooq library.
Below is the code:
dslContext.transaction(
c -> {
this.postgresService.insertData(c, table, map);
});
where c of type org.jooq.Configuration.
The code works properly & inserts a record in table & returns the inserted record. How can I access the
returned primary key out of the lambda function.
This is the function to insertData :
public Record insertData(
Configuration configuration, Table<? extends Record> table, Map<TableField<? extends Record, ?>, Object> map
)
{
return DSL.using(configuration)
.insertInto(table)
.set(map)
.returning()
.fetchOne();
}
Just use transactionResult:
String primaryKey = dslContext.transactionResult(
(Configuration c) -> {
return this.postgresService.insertData(c, table, map);
});
You could create a wrapper class for storing the retrieved value:
class PrimaryKeyWrapper{
Record primaryKey;
public void setPrimaryKey(Record primaryKey) {
this.primaryKey = primaryKey;
}
public Record getPrimaryKey() {
return primaryKey;
}
}
And use and instance of that class for store this value from inside the lambda function:
PrimaryKeyWrapper primaryKeyWrapper = new PrimaryKeyWrapper();
dslContext.transaction(
c -> {
Record primaryKey = this.postgresService.insertData(c, table, map);
primaryKeyWrapper.setPrimaryKey(primaryKey);
});
Finally you can obtain the value from outside:
primaryKeyWrapper.getPrimaryKey();

Getting column names from a JPA Native Query

I have an administrative console in my web application that allows an admin to perform a custom SQL SELECT query on our database.
Underneath, the application is using Hibernate, but these queries are not HQL, they're pure SQL, so I'm using a Native Query like this:
protected EntityManager em;
public List<Object[]> execute(String query) {
Query q = em.createNativeQuery(query);
List<Object[]> result = q.getResultList();
return result;
}
This works correctly, but it only returns the rows of data, with no extra information. What I would like is to also get the column names, so when I print the results back to the user I can also print a header to show what the various columns are.
Is there any way to do this?
Query query = entityManager.createNamedQuery(namedQuery);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> result = nativeQuery.getResultList();
And now you have Map<String,Object> . You can see your Column Names
2020
With hibernate 5.2.11.Final is actually pretty easy.
In my example you can see how I get the column names for every row. And how I get values by column name.
Query q = em.createNativeQuery("SELECT columnA, columnB FROM table");
List<Tuple> result = q.getResultList();
for (Tuple row: result){
// Get Column Names
List<TupleElement<Object>> elements = row.getElements();
for (TupleElement<Object> element : elements ) {
System.out.println(element.getAlias());
}
// Get Objects by Column Name
Object columnA;
Object columnB;
try {
columnA = row.get("columnA");
columnB= row.get("columnB");
} catch (IllegalArgumentException e) {
System.out.println("A column was not found");
}
}
This code worked for me
DTO Class :
public class ItemResponse<T> {
private T item;
public ItemResponse() {
}
public ItemResponse(T item) {
super();
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
Service Class is in the below
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Service;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
#Service
public class ServiceClass{
#PersistenceContext
public EntityManager entityManager;
public ItemResponse exceuteQueryResponse(String queryString) {
ItemResponse itemResponse=new ItemResponse();
Query jpaQuery = entityManager.createNativeQuery(queryString);
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)jpaQuery).getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
itemResponse.setItem(res);
return itemResponse;
}
}
Ryiad's answer DTO adds some confusion, you should have kept it away.
You should have explained that it works only with hibernate.
If like me you needs to keep the order of columns, you can specify your own transformer. i copied the code from hibernate and changed the HashMap to LinkedHashMap:
import java.util.LinkedHashMap;
import java.util.Map;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.hibernate.transform.ResultTransformer;
/**
* {#link ResultTransformer} implementation which builds a map for each "row", made up of each aliased value where the
* alias is the map key. Inspired by {#link org.hibernate.transform.AliasToEntityMapResultTransformer}, but kepping the
* ordering of elements.
* <p/>
* Since this transformer is stateless, all instances would be considered equal. So for optimization purposes we limit
* it to a single, singleton {#link #INSTANCE instance}.
*/
public class AliasToEntityMapResultTransformer extends AliasedTupleSubsetResultTransformer {
public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
/**
* Disallow instantiation of AliasToEntityMapResultTransformer.
*/
private AliasToEntityMapResultTransformer() {
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Map result = new LinkedHashMap<>(tuple.length);
for (int i = 0; i < tuple.length; i++) {
String alias = aliases[i];
if (alias != null) {
result.put(alias, tuple[i]);
}
}
return result;
}
#Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
/**
* Serialization hook for ensuring singleton uniqueing.
*
* #return The singleton instance : {#link #INSTANCE}
*/
private Object readResolve() {
return INSTANCE;
}
}
With this transformer you can used Ryiad's solution with Hibernate:
Query jpaQuery = entityManager.createNativeQuery(queryString);
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)jpaQuery).getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
After a long time without any answer, And based on my own further research, It seems that it can not be possible, Unfortunately.
If the JPA provider does not support the retrieval of query metadata, another solution could be the use of a SQL parser like JSQLParser, ZQL or General SQL Parser (comercial), which extracts the fields from the SELECT statement.
cast query to hibernate query, then use hibernate method
//normal use, javax.persistence.Query interface
Query dbQuery = entityManager.createNativeQuery(sql);
//cast to hibernate query
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)dbQuery)
.getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
List<TxTestModel> txTestModels = new ArrayList<>();
res.forEach(e->{
TxTestModel txTestModel = new ObjectMapper().convertValue(e, TxTestModel.class);
// txTestModels.add(new TxTestModel().setIdd((Integer) e.get("idd")).setMmm((String) e.get("mmm")).setDdd((Date) e.get("ddd")));
txTestModels.add(txTestModel);
});
System.out.println(txTestModels.size());
To enforce em.createNativeQuery(..).getResultList() to return List<Tuple> specify it with Tuple.class when creating native queries :
Query q = em.createNativeQuery("SELECT columnA, columnB FROM table", Tuple.class );
List<Tuple> result = q.getResultList();
for (Tuple row: result){
// Get Column Names
List<TupleElement<Object>> elements = row.getElements();
for (TupleElement<Object> element : elements ) {
System.out.println(element.getAlias());
}
// Get Objects by Column Name
Object columnA;
Object columnB;
try {
columnA = row.get("columnA");
columnB= row.get("columnB");
} catch (IllegalArgumentException e) {
System.out.println("A column was not found");
}
}
This worked for me:
final Query emQuery = em.createNativeQuery(query, Tuple.class);
final List<Tuple> queryRows = emQuery.getResultList();
final List<Map<String, Object>> formattedRows = new ArrayList<>();
queryRows.forEach(row -> {
final Map<String, Object> formattedRow = new HashMap<>();
row.getElements().forEach(column -> {
final String columnName = column.getAlias();
final Object columnValue = row.get(column);
formattedRow.put(columnName, columnValue);
});
formattedRows.add(formattedRow);
});
return formattedRows;
This is the working solution
Below example return the objectlist from the query.
Looping the same and from the first object cast it to hasmap, and hashmap.keyset will give you all the coulmn names in a set.
List dataList = session.createSQLQuery("SLECT * FROM EMPLOYEETABLE").setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
for (Object obj : dataList) {
HashMap<String, Object> hashMap = (HashMap<String, Object>) obj;
Set<String> keySet = hashMap.keySet();
break;
}
I also faced a similar problem working with JPA. There is no direct way in JPA to access the resultset metadata. The solution can be extracting column names from the query itself or use JDBC to get the metadata.

JPA EntityManager createQuery() error

This is failing:
public List<TypeActionCommerciale> requestTypeActionCommercialeSansNip() throws PersistenceException {
Query query = createQuery("from TypeActionCommercialeImpl where type != :type1");
query.setParameter("type1", TypeActionCommercialeEnum.NIP);
return (List<TypeActionCommerciale>) query.list();
}
exception:
Hibernate: select typeaction0_.id as id1_102_, typeaction0_.libelle as
libelle3_102_, typeaction0_.code as code4_102_, typeaction0_.type as
type5_102_ from apex.typeActionCommerciale typeaction0_ where
typeaction0_.type<>?
ERROR
org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions(SqlExceptionHelper.java:129)
No value specified for parameter 1 org.hibernate.exception.DataException: could not extract ResultSet at
i use setProperties but i have the same error:
public List<TypeActionCommerciale> requestTypeActionCommercialeSansNip() throws PersistenceException {
Query query = createQuery("from TypeActionCommercialeImpl where type <> :type1");
final Map<String, Object> properties = new HashMap<>();
properties.put("type1", TypeActionCommercialeEnum.NIP);
query.setProperties(properties);
return (List<TypeActionCommerciale>) query.list();
}
The problem is here query.setParameter("type1", TypeActionCommercialeEnum.NIP);
The enum type is not defined in hibernate so you must store the name of enum and use it for the query (the easy way) then use:
query.setString("type1", TypeActionCommercialeEnum.NIP.name());
To use enum directly (the hard way) you must implement your CustomUserType . You can find here how https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch06.html#types-custom
The main advantages of use CustomUserType are:
you can store into DB an integer (that is more smaller) instead of a string that represents the enum.
Delegate the parsing to hibernate during storing and retrieving of object.
You can use the enum directly into the query (like you are trying to do)
Try to use <> instead of != like this:
"from TypeActionCommercialeImpl where type <> :type1"
i resolve my pb i have a class public class EnumUserType<E extends Enum<E>> implements UserType and i implement this method:
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
String name = rs.getString(names[0]);
Object result = null;
if (!rs.wasNull()) {
result = Enum.valueOf(clazz, name);
}
return result;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (null == value) {
st.setNull(index, Types.VARCHAR);
} else {
st.setString(index, ((Enum<E>) value).name());
}
}

Categories