Orika Mapping for unmodifiable List - java

I have got two immutable bean classes for which I am using Orika mapping to copy the values from one to other.
However, when I am trying to copy the unmodifiableList through Orika mapping, it fails by throwing below exception:
java.lang.UnsupportedOperationException
at ma.glasnost.orika.ExceptionUtility.newMappingException(ExceptionUtility.java:55)
at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:681)
at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:650)
at com.myproject.OrikaTest.testEmployeeMapping
I have provided the code below with which you can replicate the same issue:
EmployeeDto class:
public final class EmployeeDto {
private final int id;
private final String name;
private final List<String> previousGrades;
public EmployeeDto(int id, String name, List<String> previousGrades) {
this.id = id;//validations removed
this.name = name;//validations removed
//Commented unmodifiableList as it does not work
//this.previousGrades = Collections.unmodifiableList(previousGrades);
this.previousGrades = previousGrades;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public List<String> getPreviousGrades() {
//tried like this, even this does not work
return Collections.unmodifiableList(previousGrades);
}
}
Employee class:
public final class Employee {
//ditto AS EmployeeDto
}
OrikaTest class:
public class OrikaTest {
private static final MapperFactory mapperFactory =
new DefaultMapperFactory.Builder().build();
private static final MapperFacade mapperFacade =
mapperFactory.getMapperFacade();
#Test
public void testEmployeeMapping() {
List<String> employeeGrades = Arrays.asList("A", "B");
Employee employee = new Employee(1234, "John", employeeGrades);
EmployeeDto employeeDto = mapperFacade.map(employee, EmployeeDto.class);
//tests using assertEquals
Assert.assertEquals(employeeGrades, employeeDto.getPreviousGrades());
}
}
I could find a link here on this subject, but it was not that clear as alternatives were not explained properly.
So, can you help with an example or any workaround on how to copy the unmodifiableList through Orika mapping?

If the case is that Orika must be able to mutate the list components of the mapped objects then the following could be a work around:
Add a freeze method to your objects. When the objects are created they are in a mutable state. After freeze has been called it is no longer possible to mutate the objects.
It could be implemented like this:
public final class EmployeeDto {
private final int id;
private final String name;
private List<String> previousGrades;
public EmployeeDto(int id, String name, List<String> previousGrades) {
this.id = id;//validations removed
this.name = name;//validations removed
this.previousGrades = previousGrades;
}
public void freeze() {
previousGrades = Collections.unmodifiableList(previousGrades)
}
public List<String> getPreviousGrades() {
return previousGrades;
}
}

Related

How to convert String to Set<Object>?

I have the following DTOs:
public class ConsumerDTO {
private String amount;
//...some other fields
}
public class ReceiverDTO {
private Set<PriceInfoDto> prices;
//...some other fields
}
public class PriceInfoDto {
private String amount;
//...some other fields
}
I want to convert ConsumerDTO to ReceiverDTO, p.s. map my data between differently structured objects. ConsumerDTO is my source class. ReceiverDTO is my target class. I tried this:
TypeMap<ConsumerDTO , ReceiverDTO> propertyMapper = this.mapper.createTypeMap(ConsumerDTO .class, ReceiverDTO.class);
propertyMapper.addMapping(ConsumerDTO ::getAmount, ReceiverDTO::getAmount);
But having trouble with getting amount from set in my target class. Is there a way to solve this? I also read some articles, but they show examples with simple types.
You can achieve the target object of ReceiverDTO from your source object ConsumerDTO as below:
Approach Here:
Here, I have added few more sample fields in the source as well as target class to show how to map other fields while creating a target object of type ReceiverDTO using getTargetTypeObject method.
public class Test {
public static void main(String[] args) {
//Source class objects
ConsumerDTO sourceObj = new ConsumerDTO();
sourceObj.setAmount("100");
//sample for Other fields in source object
sourceObj.setName("test");
sourceObj.setId(1000);
sourceObj.setFlag(true);
//sample for Other fields in source object
ReceiverDTO targetType = getTargetTypeObject(sourceObj);
System.out.println(targetType);
}
private static ReceiverDTO getTargetTypeObject(ConsumerDTO x) {
//Intermediate object creations
PriceInfoDto dto = new PriceInfoDto();
dto.setAmount(x.getAmount());
//Set other fields like this
dto.setName(x.getName());
dto.setId(x.getId());
dto.setFlag(x.isFlag());
//Set other fields like this
Set<PriceInfoDto> set = new HashSet<>();
set.add(dto);
//Target object
ReceiverDTO receiverDTO = new ReceiverDTO();
receiverDTO.setPrices(set);
return receiverDTO;
}
}
class ConsumerDTO {
private String amount;
private String name;
private int id;
private boolean flag;
//getters and setters
//toString
}
class ReceiverDTO {
private Set<PriceInfoDto> prices;
//getters and setters
//toString
}
class PriceInfoDto {
private String amount;
private String name;
private int id;
private boolean flag;
//getters and setters
//toString
}
Output:
ReceiverDTO{prices=[PriceInfoDto{amount='100', name='test', id=1000, flag=true}]}

Set auto increment value of an attribute while mapping two class using orika

public class Person{
private String name;
private int age;
// constructor and getter & setter methods
}
public class Student{
private long id;
private String studentName;
private int age;
private String country;
// constructor and getter and setter
}
My requirement is to map list of objects of class Person to class Student using orika mapper such that attribute student.id = auto-increment value, student.country = "india".
My code is as follows:
final DefaultMapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(person.class, Student.class)
.field("name", "studentName")
.customize(new CustomMapper<Person, Student>() {
#Override
public void mapAtoB(Person person, Student student, MappingContext context)
{
student.setId(null); // HOW TO SET AUTO_INCREMENT ID ???
student.setCountry("india");
}
})
.byDefault()
.register();
final MapperFacade mapperFacade = mapperFactory.getMapperFacade();
// converting list of perosn object to list of student object
List<Person> personList = new ArrayList<>();
personList = getPersonList();
List<Student> studentList = new ArrayList<>();
perosnList.forEach(p -> studentList.add( mapperFacade.map(p,Student.class)) );
queries:
How to set id value autoincrement while mapping ?
Any simpler and better solution except direct mapping?
Define an atomic counter and use the method getAndIncrement() to increment its value.
AtomicLong counter = new AtomicLong(1);
mapperFactory.classMap(person.class, Student.class)
.field("name", "studentName")
.customize(new CustomMapper<Person, Student>() {
#Override
public void mapAtoB(Person person, Student student, MappingContext context)
{
student.setId(counter.getAndIncrement()); // YOUR SOLUTION
student.setCountry("india");
}
})
.byDefault()
.register();

How to get the name of an Attribute from an Entity

I have the following entity class:
public class Conversation {
private String id;
private String ownerId;
private Long creationDate;
public Conversation(String id, String ownerId, Long creationDate){
this.id = id;
this.ownerId = ownerId;
this.creationDate = creationDate;
}
}
On other submodule through an external service, on each insertion, I recive a map of the following entities:
public class AttributeValue {
private Sring s; //string attribute
private String n; //number attribute
public String getS() {
return this.s;
}
public String getN() {
return this.n;
}
public AttributeValue(String s, String n){
this.s = s;
this.n = n;
}
}
//Example if I insert this conversation: new Conversation("1", "2", 1623221757971)
// I recive this map:
Map<String, AttributeValue> insertStream = Map.ofEntries(
entry("id", new AttributeValue("1", null)),
entry("ownerId", new AttributeValue("2", null)),
entry("creationDate", new AttributeValue(null, "1623221757971"))
);
To read the ownerId field from the map, I have to do this:
String ownerId = insertStream.get("ownerId").getS();
My question is, instead of have to write: insertStream.get("ownerId"), exists any way through Reflection to read the name of the field from the entity (Conversation.ownerId)?
This is because we want to mantain the submodule and If we make a change on the entitity, for example change ownerId for ownerIdentifier, the submodule shows a compilation error or is changed automatically.
Is this what you want? Field#getName()
Example code:
Field[] conversationFields = Conversation.class.getDeclaredFields();
String field0Name = conversationFields[0].getName();
Depending on the JVM used, field0Name can be "id". You can also use Class#getFields(), this method includes all Fields that are accessible in this class (super class's fields).
Another option (not using reflection) would be to refactor your code.
import java.util.Map;
import java.util.HashMap;
public class Conversation {
public static String[] names = {
"id", "ownerId", "creationDate"
};
private Map<String, Object> data = new HashMap<String,Object>();
public Conversation(Object... data) {
if(data.length!=names.length)
throw new IllegalArgumentException("You need to pass "+names.length+" arguments!");
for(int i=0; i<names.length; i++)
data.put(names[i],data[i]);
}
public Map<String,Object> getData() { return data; }
// You can pass "id"/"ownerId" or names[0]/names[1]
public String getString(String key) {
return (String)data.get(key);
}
// You can pass "creationDate" or names[2]
public long getLong(String key) {
return (long)data.get(key);
}
}
You could then create Conversation Objects like before:
Conversation c = new Conversation("myId","myOwnerId",123456789L);
You could also add public static String fields like ID="id", but changing the value of a field will never change the field's name.

Hibernate Criteria Projection of non scalar values

Is there any way to project multiple values for an root entity object using Criteria?
Assume we have these classes (With the proper mappings):
class Boss {
private String name;
private List<Employee> employees;
// setters and getters and more stuff
}
class Employee {
private String name;
// setters and getters and more stuff
}
Then i am trying to do this :
public void test() {
Criteria criteria = this.getSession().createCriteria(Boss.class);
criteria.createAlias("employees","employees");
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("name"), "name");
projectionList.add(Projections.property("employees.name"), "subordinatesNames");
criteria.setProjection(projectionList);
criteria.setResultTransformer(new AliasToBeanResultTransformer(BossBean.class));
List<BossBean> results = criteria.list(); // fails here
for (BossBean bossBean : results) {
System.out.println (bossBean);
}
}
This is how the Bean looks like (nothign special, just for grouping values) :
public static class BossBean {
private String name;
private List<Strings> subordinatesNames;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Strings> getSubordinatesNames() {
return subordinatesNames;
}
public void setSubordinatesNames(List<Strings> subordinatesNames) {
this.subordinatesNames = subordinatesNames;
}
}
The exception is this :
2014-06-06 13:37:38 [main] ERROR org.hibernate.property.BasicPropertyAccessor - expected type: java.util.List, actual value: java.lang.String.
I Guess is trying to fit the String returned from Boss(root object) -> (A)Employee(association) ->name(value) into a List.
I want to auto magically get all inserted in the List. Is there a way to achieve this using Criteria? If not, how i can achieve it?
Thanks in advance!
Grettings
VĂ­ctor

SpringMVC 3 Convert <form:checkboxes ..> return String[] to Entity Object

I have an Entity Campaign that has a OneToOne relationship with CampaignCities cities.
In turn, CampaignCities contains a Set cities;
The campaign entity
#Entity
#javax.persistence.Table(uniqueConstraints={#UniqueConstraint(columnNames={"name","company_id"}), #UniqueConstraint(columnNames={"id"})})
public class Campaign implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Long id;
#NotEmpty
#Size(min=1, max=100)
private String name;
private Date startDate;
private Date endDate;
#Valid
private Deal deal;
#Valid
private Company company;
#OneToOne
private CampaignCities cities = new CampaignCities();
The CampaignCities entity
#Entity
public class CampaignCities {
private long id;
private Set<City> cities = new HashSet<City>();
#Id
#javax.persistence.GeneratedValue(strategy=GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToMany
public Set<City> getCities() {
return cities;
}
public void setCities(Set<City> cities) {
this.cities = cities;
}
}
The City entity:
#Entity
public class City implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private Long id;
#javax.persistence.Id
#javax.persistence.GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My NewCampaignController
#SessionAttributes(value={"campaign", "campaignCities"})
#Controller
public class NewCampaignController {
//private static final Logger logger = LoggerFactory.getLogger(NewDealController.class);
#Autowired
private CampaignManager campaignManager;
#Autowired
private CityManager cityManager;
#Autowired
SimpleDateFormat dateFormat;
#Autowired
CustomDateEditor dateEditor;
#RequestMapping(value = "campaign/new", method = RequestMethod.GET)
public String showForm(Map<String, Object> model) {
//List<Campaign> campaigns = campaignManager.getCampaigns();
Campaign campaignForm = new Campaign();
CampaignCities cities = new CampaignCities();
cities.setCities(new HashSet<City>(cityManager.getCity()));
//campaignForm.setCities(cities);
model.put("campaignCities", cities);
model.put("campaign", campaignForm);
return "campaign/new";
}
#RequestMapping(value = "campaign/new", method = RequestMethod.POST)
public String processForm(#Valid Campaign campaignForm, BindingResult result, Map<String,Object> model) {
new CampaignValidator().validate(campaignForm, result);
if (result.hasErrors()) {
return "campaign/new";
}
this.campaignManager.saveCampaign(campaignForm);
model.put("campaign", campaignForm);
model.put("campaigns", this.campaignManager.getCampaigns());
return "campaign/added";
}
I have been able to get campaign to render in a form and I've rendered the list of cities successfully using:
<form:checkboxes items="${campaignCities.cities}" path="cities" itemLabel="name" itemValue="id" delimiter="<br/>" />
However when i submit the form, I get the following validation error.
Field error in object 'campaign' on field 'cities': rejected value
[2,1]; codes
[typeMismatch.campaign.cities,typeMismatch.cities,typeMismatch.com.groupdealclone.app.domain.CampaignCities,typeMismatch];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [campaign.cities,cities]; arguments []; default message
[cities]]; default message [Failed to convert property value of type
'java.lang.String[]' to required type
'com.groupdealclone.app.domain.CampaignCities' for property 'cities';
nested exception is java.lang.IllegalStateException: Cannot convert
value of type [java.lang.String[]] to required type
[com.groupdealclone.app.domain.CampaignCities] for property 'cities':
no matching editors or conversion strategy found]
I've tried to figure out a way to handle this in SpringMVC 3 but I've been stuck for over a day with no success. I simply want a List or Set or Cities that where checked on the form to be submitted to the controller and added to the Campaign. How do I get around the conversion problem where I can convert the String[] returned to a List or Set of Cities.
The project I'm working on is a public GitHub project, you can download the source and set it up using Maven if you like the project is called Group-Deal-Clone
After what is almost 2 days, the answer was simpler than I expected. Thanks to this thread I was guided to the answer.
In my NewCampaignController I did:
#InitBinder
public void initBinder(WebDataBinder binder) {
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, dateEditor);
binder.registerCustomEditor(CampaignCities.class, "cities", new PropertyEditorSupport() {
#Override
public void setAsText(String text) {
String [] ids = text.split(",");
CampaignCities cities = null;
for(String id:ids){
if(cities == null)
cities = new CampaignCities();
City city = cityManager.getCity(new Long(id));
if(city != null)
cities.getCities().add(city);
}
if(cities != null){
cities.setId(null);
setValue(cities);
}
}
});

Categories

Resources