How do I map a HashMap to a Pojo - dozer

I'm having difficulties mapping a HashMap to a Pojo using dozer. Note: the attribute names in my Pojo don't map to the key values in HashMap i've populated. I'm trying to use the set-method attribute in the dozer mapping file to map the hash key to the Pojo setter. When I run the code below, I don't get any exceptions, but the the Pojo isn't populated with any data from the hash. Any suggestions or feedback would be appreciated.
Pojo:
public class Hotel {
private String companyAssignedId;
public Hotel(){}
public String getCompanyAssignedId() {
return companyAssignedId;
}
public void setCompanyAssignedId(String companyAssignedId) {
this.companyAssignedId = companyAssignedId;
}
}
Mapping XML:
<mapping>
<class-a>com.reardencommerce.platformsvc.hadoop.dto.Hotel</class-a>
<class-b>java.util.Map</class-b>
<field>
<a set-method="setCompanyAssignedId">hotel</a>
<b key="COMPANY_ASSG_ID">rawData</b>
</field>
</mapping>
Execution Code:
DozerBeanMapper beanMapper = new DozerBeanMapper();
List<String> mappingFiles = new ArrayList<String>();
mappingFiles.add("dozer-test.xml");
beanMapper.setMappingFiles(mappingFiles);
Map<String, String> rawData = new HashMap <String, String> ();
rawData.put("COMPANY_ASSG_ID","12345");
Hotel hotel = new Hotel();
beanMapper.map(rawData, hotel);

Try using this instead of rawData. Dozer would look for a read/write method for the field rawData in hashmap and would end up with a mapping exception.

Related

JPA query attribute with a converter

I have a SpringBoot 2.6.11 application with JPA 2.2.
I have an entity like this:
#Data
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
....
#Convert(converter = ListConverter.class)
private List<String> referenceCode;
....
}
I have this Converter:
#Converter(autoApply = true)
public class ListConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(List<String> attribute) {
return String.join(";", attribute);
}
#Override
public List<String> convertToEntityAttribute(String dbData) {
return new ArrayList<>(Arrays.asList(dbData.split(";")));
}
}
And when I insert or extract this element all working fine. But now I wanna query that element and I don't know how to do it. If I do something like that:
public List<Entity> findByReferenceCode(String reference);
It doesn't work, if I do:
#Query("select e from Entity e where e.referenceCode IN ?1")
public List<Entity> findByReferenceCode(List<String> reference);
Still doesn't work..
The only way I found is by the nativeQuery but is really an extrema ratio. Ho can I solve this?
Thank you
To really do what you want here, you need to use an #ElementCollection. The reason being that there is no reliable way for JPA to query a single column and treat it as a collection. Reliably querying a collection requires a second table (which is what #ElementCollection does). You can continue to use the #Converter, but your queries will have to be customized to handle the disparity between the entity attribute type (list) and the actual database column type (string).
If you are okay with the limitations of the #Converter then it's fine (I have used them this way) but if you truly need to query the attribute like a collection (e.g. search for multiple independent items, perform counts, aggregations, etc) and you want those queries to be generated by a JPA layer, then you will have to use #ElementCollection and let it create a second table.

JSONB ignores Jackson serialization inclusion

I'd like to persist to jsonb type column (com.vladmihalcea.hibernate.type.json.JsonBinaryType)
#Type(type = "jsonb")
#Column(name = "style", columnDefinition = "jsonb")
private TextStyleJsonB style;
JSON object is relative big and i don't want that it saving fields with null values, so i decided to use custom object mapper, where i ignore null values. But it was not applied and all null values still saved to postgres database.
Any idea how to get rid of null fields and save only fields with real value?
In spring boot i have following application properties
hibernate:
types:
jackson:
object:
mapper: com.xxx.constructor.configurations.CustomObjectMapperSupplier
CustomObjectMapperSupplier looks like this:
public class CustomObjectMapperSupplier
implements ObjectMapperSupplier {
#Override
public ObjectMapper get() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
}
Instead of defining the CustomObjectMapperSupplier in your application.yml, you should create an hibernate.properties file with the property:
hibernate.types.jackson.object.mapper=com.xxx.constructor.configurations.CustomObjectMapperSupplier

Failed to decode property of type BasicDBList with PojoCodecProvider in mongo db

I am tying to load an instance of the class "DataTable" from a mongo database by using the default codec registry (MongoClient.getDefaultCodecRegistry()) and the builder provided by the PojoCodecProvider. I have registered the DataTable class in the codec provider and the object is properly mapped from the database when the records field is null. Nevertheless, I get an error when the records property contains data. Furthermore, I need to have the records field defined as a list of objects with arbitrary attributes. Is it possible to use the default PojoCodecProvider for this purpose? Is there any other alternative?
import com.mongodb.BasicDBList;
import org.bson.types.ObjectId;
import java.util.List;
public class DataTable {
private ObjectId id;
private List<String> fields;
private BasicDBList records;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public List<String> getFields() {
return fields;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
public BasicDBList getRecords() {
return records;
}
public void setRecords(BasicDBList records) {
this.records = records;
}
}
The exception that I get when load an instance of the DataTable class is the following.
2018-03-21T16:32:04,526 [http-bio-8081-exec-4] ERROR ...service.controllers.BaseController - Failed to decode 'records'. Unable to set value for property 'records' in DataTable
org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'records'. Unable to set value for property 'records' in DataTable
at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:192) ~[bson-3.6.3.jar:?]
at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:168) ~[bson-3.6.3.jar:?]
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:122) ~[bson-3.6.3.jar:?]
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:126) ~[bson-3.6.3.jar:?]
I get this exception when I try to load an item with the following code
DataTable item = collection.find(eq(new ObjectId(id))).first();
Well, one alternative you can use is Jackson Serialization.
I think something like this would suit you just fine
Document document = collection
.find(eq(new ObjectId(id)))
.first();
String json = document.toJson();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
DataTable dataTable = mapper.readValue(json, DataTable.class);
See this question converting Document objects in MongoDB 3 to POJOS for reference

Searching by query for attributes of complex objects in Java EE

I have created the object Person, I can deleted and modify it and I can also search for Person by his name or phonenumber... but I don't know for exemple how to search for a person by his ** home address**. Here is my code:
My entity Person.java:
public class Person{
private Long id;
private String name;
#ManyToOne(cascade = CascadeType.ALL)
private Address address;
....
}
My entity Address.java
public class Address{
...
private String streetName;
...
}
And here is the most interesting function that I am trying to modify to get what I want, I would like to search for Persons who live in xxx (streetName = xxx). Here is my function getByQuery:
public List<Person> getByQuery(PersonSearchQuery searchQuery) {
Map<String, String> criteriaQuery = new HashMap<String, String>();
if (searchQuery.getName() != null)
criteriaQuery.put("name",searchQuery.getName());
TypedQuery<Person> query = this.findByQuery(criteriaQuery);
return query.getResultList();
}
The object PersonSearchQuery contains just to attributes name (String) and streetName (String) and their getters.
Function findByQuery:
public TypedQuery<T> findByQuery(Map<String, String> criteriaQuery) {
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(this.entityClass);
Root<T> root = criteria.from(this.entityClass);
criteria.select(root);
Predicate predicate = builder.conjunction();
if (criteriaQuery.size() != 0) {
for (String key : criteriaQuery.keySet()) {
try{
predicate = builder.and(predicate, builder.equal(root.<String>get(key), criteriaQuery.get(key)));
}catch(IllegalArgumentException e){
continue;
}
}
}
criteria.where(predicate);
return this.em.createQuery(criteria);
}
So I can search for Persons by their names by I cannot search for them by streetName the problem is my function getByQuery I would like to do something like this:
if (searchQuery.getStreetName() != null)
criteriaQuery.put("Address.streetName",searchQuery.getStreetName());
The problem is I don't know how to define the key in this case. Thanks for your help
I only use CriteriaBuilder if I have several similar Entities which needs to be used/rendered in the same way, so if person is the only Entity with an Address reference I would just use JPQL, like this:
entityManager.createQuery(
"select p from Person p where p.address.streetName like :streetName", Person.class)
.setParameter("streetName", "xyz" + "%").getResultList()
The main reason I tend to avoid CriteriaBuilder, is because it has a rather steep learning curve, and you need to write a lot of code to express very simple concepts. In contrast any developer familiar with SQL can read and maintain JPQL code.
These days I always use frameworks, like DeltaSpike Data (for EE) and Spring Data, they both implements most of the basic DAO/Repository features, so If you don't mind an extra dependency (and some magic) it can save you a lot of boilerplate JPA code.

javaee 6 rest api named query result

I have a simple JEE6 rest class that gets the data from db2. I am using Jackson in ApplicationConfig class to convert the entity objects to json. It converts with the field names as the key and the value as the right hand value. So for example:
Class Entity {
String name;
String address;
}
converts to
{name:"hello", address:"world"}
The service is as follows:
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
Now I want to only return the name in json format. So I created a named query as follows in the entity class:
#NamedQuery(name = "justGetName", query = "SELECT a.name FROM Applications a")
And the service changed to
public List<T> findAll() {
return getEntityManager().createNamedQuery("justGetName").getResultList();
}
This returns the following array:
[{"first","second","third"}]
But I want to get back:
[{name:"first",name:"second",name:"third"}]
How do I write the named query so that the class field names are added to the json structure? Thank you.
You querying a list of strings from your database and this is what the service returns.
Their are multiple ways to achieve your goal.
Pure JPA
Using #JsonIgnore to tell Jackson not to serialize an attribute
class Application {
String name;
#JsonIgnore
String address;
}
Create a new Entity class that only contains the attributes you would like to share
class ApplicationName {
String name;
}
Alternatively you could introduce a separate class that only contains the attributes you would like to share and convert the results from the query into this class and return than the list of this converted values.