Passing in a Class dynamically - java

Hopefully quite a simple question. How do I pass in a Class into an abstract method?
My abstract method is as follows:
public abstract class IDataList {
public LinkedList<IThing> getRows(IThing thing, String sql, List<Object> vals) throws Exception {
LinkedList<IThing> list = new LinkedList<>();
List<LinkedHashMap<String, Object>> rows = db.executeSelect(sql, vals);
for (HashMap<String, Object> row : rows) {
list.add(new thing(row));
}
rowCount = (long) getDb().executeScalar("SELECT FOUND_ROWS()");
return list;
}
}
Which is inherited by a concrete class:
public class DataList extends IDataList {
}
The IThing is currently an empty abstract class which is extended by an Thing, for example:
public class Thing extends IThing {
private long uid;
private String name;
public Thing(HashMap<String, Object> row) {
this.uid = (long) row.get("uid");
this.name = (String)row.get("name");
}
}
I want to be able to pass in Thing into a concrete class of IDataList, for example:
IDataList dataList = new DataList();
dataList.getRows(Thing, "select something", new ArrayList<>())

If you want to use Thing Class as a Row->Object mapper, it will be better to use org.springframework.jdbc.core.RowMapper
public class CustomerRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setCustId(rs.getInt("CUST_ID"));
customer.setName(rs.getString("NAME"));
customer.setAge(rs.getInt("AGE"));
return customer;
}
}
public Customer findByCustomerId(int custId) {
String sql = "SELECT * FROM CUSTOMER WHERE CUST_ID = ?";
Customer customer = (Customer)getJdbcTemplate().queryForObject(sql, new Object[] { custId }, new CustomerRowMapper());
return customer;
}

Related

Jackson serializes list of objects with empty objects

I'm trying to serialize a very large object graph that uses #JsonView (I don't know if that's relevant or not). What I'm finding is that for a list of objects within the graph, Jackson is serializing a list of empty objects, such as [{},{},{}]. None of the attributes are in the string. All scalar attributes serialize just fine. I'm only having trouble with the lists.
I've verified several times that the attributes are being set in the objects. Part of my POJO looks like so:
public class ProfessionalData implements Serializable {
#JsonProperty("collegeEducation")
#JsonView(Views.Myview.class)
#Valid
private List<CollegeEducation> collegeEducation = new ArrayList<CollegeEducation>();
#JsonProperty("managementCommittee")
#JsonView(Views.Myview.class)
#Valid
private List<ManagementCommittee> managementCommittee = new ArrayList<ManagementCommittee>();
//getters and setters
}
This is the ObjectMapper code:
#Override
public String convertToDatabaseColumn(#NotNull MyPojoItem item) {
try {
//Disable Default_view_Inclusion, so fields without a view annotation wont be included
objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
objectMapper.setDateFormat(df); //Transform the Object to String
String result = objectMapper
.writerWithView(Views.MyView.class)
.writeValueAsString(item);
return result;
} catch (Exception ex) {
LOG.error("Failed to convertToDatabaseColumn: " + ex.getMessage());
return null;
}
}
Can someone tell me what this usually means?
You could do something like this for both lists. A custom serializer to write your objects there from a List. For example, for the CollegeEducation list:
public class CollegeEducationSerializer extends JsonSerializer<List<CollegeEducation>>
{
#Override
public void serialize(List<CollegeEducation> list, JsonGenerator json,
SerializerProvider provider) throws IOException
{
if (list == null || list.isEmpty())
return;
json.writeFieldName("CollegeEducationArray");
json.writeStartArray();
for (CollegeEducation ce : list)
{
json.writeStartObject();
json.writeObjectField("CollegeEducation", ce);
json.writeEndObject();
}
json.writeEndArray();
}
}
And specify it on the annotations:
#JsonSerialize(using = CollegeEducationSerializer.class)
private List<CollegeEducation> collegeEducation = new ArrayList<CollegeEducation>();
In order to read it, the same logic applies (a custom deserializer for both lists).
For something like a generic approach, you could also do something like this:
public class CustomListSerializer extends JsonSerializer<List<? extends ListElement>>
{
#Override
public void serialize(List<? extends ListElement> list, JsonGenerator json,
SerializerProvider provider) throws IOException
{
if (list == null || list.isEmpty())
return;
json.writeFieldName(list.get(0).getElementType()+"Array");
json.writeStartArray();
for (ListElement le : list)
{
json.writeStartObject();
json.writeObjectField(le.getElementType(), le);
json.writeEndObject();
}
json.writeEndArray();
}
}
Then you could use it for all your Lists:
#JsonSerialize(using = CustomListSerializer.class)
private List<CollegeEducation> collegeEducation = new ArrayList<>();
#JsonSerialize(using = CustomListSerializer.class)
private List<ManagementCommittee> managementCommittee = new ArrayList<>();
For this I added a customProperties Map, which may be prone to errors here. So the approach would be writing manually the desired fields, instead of using json.writeObjectField(le.getElementType(), le);
public abstract class ListElement
{
public abstract String getElementType();
public Map<String, String> properties = new HashMap<>();
public final String name;
public final int id;
public ListElement(String name, int id)
{
this.name = name;
this.id = id;
}
}
and, f.e:
public class CollegeEducation extends ListElement
{
protected String location;
protected String director;
public CollegeEducation(String name, int id, String location, String director)
{
super(name,id);
this.director = director;
this.location= location;
properties.put("director", director);
properties.put("location", location);
//...
}
public String getElementType()
{
return "CollegeEducation";
}
//...
}
//...
In the serializer:
json.writeFieldName(list.get(0).getElementType()+"Array");
json.writeStartArray();
for (ListElement le : list)
{
json.writeStartObject();
json.writeFieldName(le.getElementType()+"-"+le.id);
json.writeStartObject();
json.writeStringField("name", le.name);
for(Map.Entry<String,String> kv : le.properties.entrySet())
json.writeStringField(kv.getKey(), kv.getValue());
json.writeEndObject();
json.writeEndObject();
}
json.writeEndArray();
You should get something like:
{
"CollegeEducationArray":
[
{ "CollegeEducation-22" :
{
"name" : "Lauaxeta",
"director" : "AJerk",
"location": "Bilbao"
}
},
{ "CollegeEducation-55" :
{
"name" : "Harvard",
"director" : "OtherJerk",
"location": "Rwanda"
}
}
]
}

How to fix missing descriptor for class POJO after update server? [duplicate]

I'm using EclipseLink to run some Native SQL. I need to return the data into a POJO. I followed the instructions at EclipseLink Docs, but I receive the error Missing descriptor for [Class]
The query columns have been named to match the member variables of the POJO. Do I need to do some additional mapping?
POJO:
public class AnnouncementRecipientsFlattenedDTO {
private BigDecimal announcementId;
private String recipientAddress;
private String type;
public AnnouncementRecipientsFlattenedDTO() {
super();
}
public AnnouncementRecipientsFlattenedDTO(BigDecimal announcementId, String recipientAddress, String type) {
super();
this.announcementId = announcementId;
this.recipientAddress = recipientAddress;
this.type = type;
}
... Getters/Setters
Entity Manager call:
public List<AnnouncementRecipientsFlattenedDTO> getNormalizedRecipientsForAnnouncement(int announcementId) {
Query query = em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT, AnnouncementRecipientsFlattenedDTO.class);
query.setParameter(1, announcementId);
return query.getResultList();
}
I found out you can put the results of a Native Query execution into a List of Arrays that hold Objects. Then one can iterate over the list and Array elements and build the desired Entity objects.
List<Object[]> rawResultList;
Query query =
em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT);
rawResultList = query.getResultList();
for (Object[] resultElement : rawResultList) {
AnnouncementDeliveryLog adl = new AnnouncementDeliveryLog(getAnnouncementById(announcementId), (String)resultElement[1], (String)resultElement[2], "TO_SEND");
persistAnnouncementDeliveryLog(adl);
}
You can only use native SQL queries with a class if the class is mapped. You need to define the AnnouncementRecipientsFlattenedDTO class as an #Entity.
Otherwise just create the native query with only the SQL and get an array of the data back and construct your DTO yourself using the data.
Old question but may be following solution will help someone else.
Suppose you want to return a list of columns, data type and data length for a given table in Oracle. I have written below a native sample query for this:
private static final String TABLE_COLUMNS = "select utc.COLUMN_NAME, utc.DATA_TYPE, utc.DATA_LENGTH "
+ "from user_tab_columns utc "
+ "where utc.table_name = ? "
+ "order by utc.column_name asc";
Now the requirement is to construct a list of POJO from the result of above query.
Define TableColumn entity class as below:
#Entity
public class TableColumn implements Serializable {
#Id
#Column(name = "COLUMN_NAME")
private String columnName;
#Column(name = "DATA_TYPE")
private String dataType;
#Column(name = "DATA_LENGTH")
private int dataLength;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public TableColumn(String columnName, String dataType, int dataLength) {
this.columnName = columnName;
this.dataType = dataType;
this.dataLength = dataLength;
}
public TableColumn(String columnName) {
this.columnName = columnName;
}
public TableColumn() {
}
#Override
public int hashCode() {
int hash = 0;
hash += (columnName != null ? columnName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof TableColumn)) {
return false;
}
TableColumn other = (TableColumn) object;
if ((this.columnName == null && other.columnName != null) || (this.columnName != null && !this.columnName.equals(other.columnName))) {
return false;
}
return true;
}
#Override
public String toString() {
return getColumnName();
}
}
Now we are ready to construct a list of POJO. Use the sample code below to construct get your result as List of POJOs.
public List<TableColumn> findTableColumns(String table) {
List<TableColumn> listTables = new ArrayList<>();
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery(TABLE_COLUMNS, TableColumn.class).setParameter(1, table);
listTables = q.getResultList();
em.close();
return listTables;
}
Also, don't forget to add in your POJO class in persistence.xml! It can be easy to overlook if you are used to your IDE managing that file for you.
Had the same kind of problem where I wanted to return a List of POJOs, and really just POJOs (call it DTO if you want) and not #Entity annotated Objects.
class PojoExample {
String name;
#Enumerated(EnumType.STRING)
SomeEnum type;
public PojoExample(String name, SomeEnum type) {
this.name = name;
this.type = type;
}
}
With the following Query:
String query = "SELECT b.name, a.newtype as type FROM tablea a, tableb b where a.tableb_id = b_id";
Query query = getEntityManager().createNativeQuery(query, "PojoExample");
#SuppressWarnings("unchecked")
List<PojoExample> data = query.getResultList();
Creates the PojoExample from the database without the need for an Entity annotation on PojoExample. You can find the method call in the Oracle Docs here.
edit:
As it turns out you have to use #SqlResultSetMapping for this to work, otherwise your query.getResultList() returns a List of Object.
#SqlResultSetMapping(name = "PojoExample",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "type", type = String.class)
},
targetClass = PojoExample.class)
)
Just put this anywhere under your #Entity annotation (so in this example either in tablea or tableb because PojoExample has no #Entity annotation)

java.lang.ClassCastException: java.lang.String cannot be cast to [Ljava.lang.Object; when trying to get colum values to a list by Hibernate

I'm developing a spring web application with Hibernate. I have faced an error when getting column values from a table to a list. But this error keeps coming. Please help me put. Thanks in advance.
#Repository
#Transactional
public class GetProjectsDaoImpl implements GetProjectsDao {
#Autowired
private HibernateUtilImpl hibernateutilimpl;
public List<Projects> getProjects() {
String sql = "select project_id from project";
List<Object[]> projectObjects = hibernateutilimpl.fetchAll(sql);
List<Projects> projectsList = new ArrayList<Projects>();
for(Object[] projectObject: projectObjects) {
Projects project = new Projects();
String id = (String) projectObject[0];
project.setProjectId(id);
projectsList.add(project);
}
return projectsList;
}
}
#Repository
public class HibernateUtilImpl implements HibernateUtil {
#Autowired
private SessionFactory sessionFactory;
public <T> Serializable create(final T entity) {
return sessionFactory.getCurrentSession().save(entity);
}
public <T> T update(final T entity) {
sessionFactory.getCurrentSession().update(entity);
return entity;
}
public <T> void delete(final T entity) {
sessionFactory.getCurrentSession().delete(entity);
}
#SuppressWarnings("rawtypes")
public <T> List<T> fetchAll(String query) {
return sessionFactory.getCurrentSession().createNativeQuery(query).list();
}
}
I have following suggestions. First change:
List<Object[]> projectObjects = hibernateutilimpl.fetchAll(sql);
to:
List<Object> projectObjects = hibernateutilimpl.fetchAll(sql);
Next, change:
for(Object[] projectObject: projectObjects) {
String id = (String) projectObject[0];
to
for(Object projectObject: projectObjects) {
String id = (String) projectObject;
The above change is needed because you are selecting only a single column. Object[] is used only when selecting more then one column.

how to use map to reduce objects creation in spring MVC web app?

Situation : I am designing a spring MVC based web app , i have a table called customers it consists of 3 columns id , property , property value .
id is not primary key.
Following is the Model class i am using :
public class prop {
private String id;
private String property;
private String property_value;
/*setter and getters of these three variables ...*/
}
and my Dao is :
#Repository("Dao")
public class Dao implements{
#Autowired
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List<Model> listProp(String id) {
final String sql = "select * from customers where id = ? ";
final List<Model> list = jdbcTemplate.query(sql, new Object[]{id}, new Mapper());
return list;
}
}
And my Mapper class is :
public class Mapper implements RowMapper<Model> {
public Model mapRow(ResultSet rs, int rowNum) throws SQLException {
Model m = new Model();
m.setId(rs.getString(1));
m.setProperty(rs.getString(2));
m.setValue(rs.getString(3));
return wl;
}
}
Problem :Now i have a scenario in which id=1 has 4 properties , so it will have 4 corresponding rows and 4 Model objects are created,
if id=1 has say 100 properties , then 100 model objects are created which is inefficient , i want that , for all rows with id=1 one Modelobject must be created , i tried using map but couldn't implement it properly can somebody please help ?
Note: In UI i am displaying all the records as they are present in DB
You can modify class prop as
public class prop {
private String property;
private String property_value;
/*setter and getters of these three variables ...*/
}
Modify Mapper code accordingly to use HashMap.
public class Mapper {
Map<String ,List<Model>> map = new HashMap<String , List<Model>>();
public Map<String , Model> mapRow(ResultSet rs, int rowNum) throws SQLException {
Model m = new Model();
m.setProperty(rs.getString(2));
m.setValue(rs.getString(3));
if(map.containsKey(rs.getString(1))) {
List<Model> modelList = map.get(rs.getString(1));
modelList.add(m);
} else {
List<Model> modelList = new ArrayList<Model>();
modelList.add(m);
map.put(rs.getString(1),modelList);
}
return map;
}
}

ParameterizedRowMapper That Maps Object List to Object

I am trying to set the Parent List in a ParameterizedRowMapper how is this written or approached. I have two Objects one for parent and one for children however children contains a ListThe parents for each child are stored in a separate table in the database and the mapping is 1 - many.
The select for the records for the parents will be done in a separate ResultSet. Will the mapping have to be done separately (separate ParameterizedRowMapper), if so how will i have to write the ParameterizedRowMapper this is the major concern how ParameterizedRowMapper is written to accommodate a list items.
ParameterizedRowMapper
public static class ChildrenMapper implements ParameterizedRowMapper<Children>{
public Children mapRow(ResultSet rs, int rowNum) throws SQLException {
Children child = new Children();
child.setFirstName(rs.getString("firstName"));
child.setLastName(rs.getString("lastName"));
//a child can have many Parents or gaurdians
child.setParent(List<Parent>);
return child;
}
}
Based on my research i have found that i need to use ResultSetExtractor, however i have a questions on the use of that. Do i integrate it into the class at the point of setting the Parent? Can someone guide me on how it can be done the correct way
Children.java
Public class Children(){
int cid;
String firstName;
String lastName;
List<Parent>parents;
..
//getters/setters
}
Parent.java
Public class Parent(){
int pid;
String firstName;
String lastName;
..
//setters/getters
}
I will show how to do this for a canonical 1-to-many example, you can adapt it to your vo class / table.
Order class
public class Order {
private Long orderId;
private String user;
private List<LineItem> items;
// Getter / setter omitted
}
Item class
public class LineItem {
private Long lineItemId;
private String product;
private int quantity;
// Getter / setter omitted
}
Use two rowmappers one for each class and then use a result set extractor to convert multiple rows into one order + line items
OrderRepository
public final static RowMapper<Order> orderMapper = ParameterizedBeanPropertyRowMapper.newInstance(Order.class);
public final static RowMapper<LineItem> lineItemMapper = ParameterizedBeanPropertyRowMapper.newInstance(LineItem.class);
public Order findOrderWithItems(Long orderId) {
return jdbcTemplate.query("select * from orders, line_item "
+ " where orders.order_id = line_item.order_id and orders.order_id = ?",
new ResultSetExtractor<Order>() {
public Order extractData(ResultSet rs) throws SQLException, DataAccessException {
Order order = null;
int row = 0;
while (rs.next()) {
if (order == null) {
order = orderMapper.mapRow(rs, row);
}
order.addItem(lineItemMapper.mapRow(rs, row));
row++;
}
return order;
}
}, orderId);
}
public List<Order> findAllOrderWithItmes() {
return jdbcTemplate.query("select * from orders, line_item "
+ " where orders.order_id = line_item.order_id order by orders.order_id",
new ResultSetExtractor<List<Order>>() {
public List<Order> extractData(ResultSet rs) throws SQLException, DataAccessException {
List<Order> orders = new ArrayList<Order>();
Long orderId = null;
Order currentOrder = null;
int orderIdx = 0;
int itemIdx = 0;
while (rs.next()) {
// first row or when order changes
if (currentOrder == null || !orderId.equals(rs.getLong("order_id"))) {
orderId = rs.getLong("order_id");
currentOrder = orderMapper.mapRow(rs, orderIdx++);
itemIdx = 0;
orders.add(currentOrder);
}
currentOrder.addItem(lineItemMapper.mapRow(rs, itemIdx++));
}
return orders;
}
});
}

Categories