mapstruct - map object with nested list to flat list - mapstruct

I cannot manage a problem that seems to be trivial.
I just want to map relation to flat table.
class Train {
Integer trainNumber;
List<Station> stations;
}
class Station {
String name;
String coords;
}
I want to map this classes to flatten one
class TrainStation {
Integer trainNumber;
String stationName;
}
How to do this with mapstruct?

Related

Sorting strings based on the integer value they represent in hibernate search

I have an entity with a string field in it.
Sometimes this string field stores actual words, so sorting based on lexicographical order makes sense. However, there is a use case where this field stores string values like "0%", "10%", "100%", "20%".
The default string sorting generates the following sequence: 0% 10% 100% 20%.
The ideal sequence after sorting would be 0%, 10%, 20%, 100%(determined by the percentage they represent).
It is guaranteed that there is no mixed data, i.e, you won't have "10%" and "word" appear in the same sequence that needs to be sorted.
My question is if there is a way to achieve the string sorting based on the numerical value they represent?
Ideally, you should alter your model to have two fields: one of integer type where you'll hold the percentage, and one of string type where you will hold the string value:
#Entity
#Indexed
public class MyEntity {
#Field
#SortableField
private int percentage;
#Field
#SortableField
private String notPercentage;
// ... other properties, getters and setters ...
}
Then when sorting you'll just sort on both fields. Since you are certain they are mutually exclusive, it's not a problem to sort on both:
QueryBuilder queryBuilder = ...;
FullTextQuery ftQuery = ...;
ftQuery.sort(queryBuilder.sort().byField("percentage").andByField("notPercentage").createSort());
Alternatively, if you really want to keep a single String property in your ORM model (to each his own...), you can use a custom bridge to apply padding to your values, so that they are sorted correctly:
#Entity
#Indexed
public class MyEntity {
#Field(bridge = #FieldBridge(impl = PaddingIfPercentageBridge.class))
#SortableField
private String percentageOrNot;
// ... other properties, getters and setters ...
}
public class PaddingIfPercentageBridge implements TwoWayStringBridge {
private static final Pattern PERCENTAGE = Pattern.compile("[0-9]+%");
#Override
public String objectToString(Object object) {
if ( object == null ) {
return null;
}
String string = (String) object;
if ( !PERCENTAGE.matcher( string ).matches() ) {
return string;
}
StringBuilder paddedPercentage = new StringBuilder();
for ( int padIndex = string.length(); padIndex < 4; padIndex++ ) {
paddedPercentage.append( '0' );
}
return paddedPercentage.append( string ).toString();
}
#Override
public Object stringToObject(String stringValue) {
// Ideally you should remove the padding here...
return stringValue;
}
}
Then you can sort on a single field:
QueryBuilder queryBuilder = ...;
FullTextQuery ftQuery = ...;
ftQuery.sort(queryBuilder.sort().byField("percentageOrNot").createSort());

Custom annotation processing while coding

The purpose of the project I'm working on is to handle annotation at compile time, it is not focused on what exactly I'm developing.
I took a simple subject for this and I'm writing a custom collection that will store elements and provide methods to manage them.
What I wanna do is to create an annotation #Contains, for example, to generate itemsContains method that could be processed while coding (instead of writing code manually).
public class Array {
private List<String> items;
public Array() {
items = Arrays.asList("abc", "def", "xyz");
}
public boolean itemsContains(String expected) {
return items.contains(expected);
}
}
Generally, I want my class to look something like:
public class Array {
#Contains
private List<String> items;
public Array() {
items = Arrays.asList("abc", "def", "111");
}
}
The important thing I want to reach is to have itemsContains method show up once the annotation is applied to a field. This is how it should look like:
expected result
Alternate existing examples are Lombok's #Getter/#Setter.
So what functionality or configurations should I implement to get the expected result?
Would be grateful for some real implementations or guides how to perform it step by step.
Annotation processing does not change the source file yet it generates a new file,
Lombok on the other hand does a trick to modify the source file itself, meaning that you need to call the generated class somewhere in your code.
One way to do this is to generate a class that extends the main class
#Generated
public class ArrayProxy extends Array {
public boolean itemsContains(String expected) {
return items.contains(expected);
}
}
and in your main class you need to do two things:
first you need to make items protected
you can add factory method to actually create the generated class
public class Array {
#Contains
protected List<String> items;
public static ArrayProxy create(){
return new ArrayProxy();
}
private Array() {
items = Arrays.asList("abc", "def", "111");
}
}
And of course you need to use it like this
ArrayProxy array = Array.create();
array.itemsContains("expected");

How Can I flatten my DTO into VO

Here is a sample simple scenario that I want to map. I have a DTO as follows
Class Person {
String Name;
List<Contact> contacts;
}
Class Contact {
String type; //values 'home', 'work', 'mobile' etc
Phone number;
}
Class Phone {
String areaCode;
String number;
}
I need to map it to a VO object as follows
Class PersonVO {
String Name;
PhoneVO homePhone;
PhoneVO workPhone;
}
Class PhoneVO {
String areaCode;
String number;
}
Is there an easier way to map them using dozer or I have to use customconvertors? how can I use default mapping for phone to phoneVO?
You cannot map Person DTO and PersonVO without using a CustomConverter,
because Person's Phone 'number' variable gets it's value based on whether
'type' variable contains values 'home' or 'work', and so on. Dozer cannot handle such
conditional cases.
However, default mapping for Phone and PhoneVO alone is very simple:
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
<mapping>
<class-a>{package-name}.Phone</class-a>
<class-b>{package-name}.PhoneVO</class-b>
</mapping>
</mappings>

Spring Data Mongodb POJO mapping

I know that Spring Data MongoDB is a POJO centric model for interacting with a MongoDB DBCollection. But in my case the POJO mapping is somewhat irrelevant.For example the JSON string that I need to store as a single document in a collection is as follows
columns:[
{
name:"xyz"
age:12
},
{
name:"abc"
age:34
},
{
name:"pqr"
age:76
}
]
What can be the equivalent POJO mapping in this case?
I think you can achieve it through this mapping (If I understood your question correctly):
class Person {
String name;
int age;
}
class Outer {
#Field("columns")
List<Person> persons;
}

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.