Map JoinColum value instead of association in JSON - java

For a given #Entity with a #ManyToOne relation I want to suppress the mapping of the associated type. The JSON representation should contain the forein key (#JoinColumn) instead.
#Entity
public class OrderPosition implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "order_id", referencedColumnName = "id")
//#Json???
private Order order;
// ...
}
orderPosition = new OrderPosition()
// ...
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(orderPosition);
assertThat(json).contains("\"order_id\":");
How could I archieve it?

You can use the following solution:
#JsonProperty("order")
public Integer getOrderId() {
return this.order.getId();
}
The JsonProperty annotation causes to use this getter for serialization the "order" attribute. So you can only return the id for your use case. I would recommand to use a nice name of getter (not getOrder() ...).

Probably you could just add a #JsonIgnore to the order field and another field for the orderId.

Related

Object field mapping problem when mapping MapStruct List to List

I have a problem when mapping a List from a List.
All default fields of Entity are well mapped.
ex) (Entity)String date -> (DTO) String date
However, the Join object field exists in Entity.
We need to pull the data out of this object field and map it anew.
In the case of a single Entity to single DTO rather than a List to List, this was easily possible.
#Mapping(target = ".", source = "user")
This way we were able to map all fields of the user object field that the Entity has to the remaining unmapped fields.
However, trying the same on a List has no effect.
Which method should I use?
#Entity(name = "refund")
public class RefundEntity extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int refundId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private UserEntity user;
private int refundAmount;
}
and
public class AdminRefundUserDto {
#AllArgsConstructor
#Getter
public static class Response {
private int refundAmount;
private String marketName;
private String bizNumber;
private String bizUserName;
}
}
and
#Entity(name = "user")
public class UserEntity extends BaseEntity {
#Id
#GeneratedValue(generator = "uuid4")
#GenericGenerator(name = "uuid", strategy = "uuid4")
#Column(columnDefinition = "BINARY(16)")
private UUID userId;
private String userName;
private String password;
private String phoneNumber;
private String marketName;
private String bizUserName;
private String bizNumber;
}
and I used
#Mapping(target = ".", source = "refundList.user")
List<AdminRefundUserDto.Response> toDtoList(List<RefundEntity> refundList);
First of all, create a method for the mapper with a SourceObject parameter that return a TargetObject
#Named("toResponseDto")
//add your mapping
AdminRefundUserDto.Response toResponseDto(RefundEntity refundEntity);
if you have complex logic of mapping, you can also create a custom method to map a certain target parameter: see Custom Mapper with MapStruct
Then add an IterableMapping
#IterableMapping(qualifiedByName = "toResponseDto")
List<AdminRefundUserDto.Response> toDtoList(List<RefundEntity> refundList);

Clone object without ids using mapstruct

I have an object which contains several list. is there a method to clone this object without id using mapstruct even for the nested object list in an automatic way to be able to persist it.
#Data
public class ParentDto {
#Id
private Long id;
private String name;
private List<Child1Dto> children1;
private List<Child1Dto> children2;
}
#Data
public class Child1Dto {
#Id
private Long id;
private String name;
}
#Data
public class Child2Dto {
#Id
private Long id;
private String name;
}
Actual mapper
#Mapper(mappingControl = DeepClone.class)
public interface CloneParentMapper {
#Mapping(target = "id", ignore = true)
ParentDto cloneWithoutId(ParentDto parentDto );
#Mapping(target = "id", ignore = true)
Child1Dto cloneWithoutId(Child1Dto child1Dto );
#Mapping(target = "id", ignore = true)
Child2Dto cloneWithoutId(Child2Dto child2Dto );
}
is there a way to ignore all id without doing #Mapping(target = "id", ignore = true) on every list?
I really feel this is the best and easiest way to ignore fields.
But still if u wanna ignore fields and not mark them as ignored specifically, then you can use constructors based mappings and have a separate constructor without id field. You will have to mark this constructor as #Default.
https://mapstruct.org/documentation/stable/reference/html/#mapping-with-constructors

#JsonIgnore does not ignore fields in hibernate Entity

I have an Entity "Task" that has an Id property, but I don't need the field to be returned in the JSON file.
#Entity
public class Task {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Integer Id;
#JsonProperty("task")
private String taskName;
private String status;
//getter and setter
}
However, the annotation #JsonIgnore doesn't filter the field when I make the get request, see below:
{
"status": "started",
"timestamps": {
"submitted": "2018-12-31T00:34:20.718+0000",
"started": "2018-12-31T00:34:20.718+0000",
"completed": "2018-12-31T00:34:20.718+0000"
},
"id": 40001,
"task": "q094hiu3o"
}
What is the proper way to prevent "Id" to be displayed?
So here is the problem jackson had issue with hibernate issue, try using #jsonIgnoreProperties on class level
#JsonIgnoreProperties(ignoreUnknown = true,
value = {"id"})
You can try to add the #JsonIgnore only on your getter:
#JsonIgnore
public Integer getId() {
return id;
}
Also I would suggest to add the #JsonProperty annotation on your id field, if it is available in the Jackson version you are using:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonProperty(access = Access.WRITE_ONLY)
private Integer id;
WRITE_ONLY
Access setting that means that the property may only be written (set) for deserialization, but will not be read (get) on serialization, that is, the value of the property is not included in serialization.
Jackson documentation here

JAXB. Advise correct annotations

There is a structure. I want to link the three entities in this way: the Company should contain id, name of company and the list of Departments, each Department has a list of Workers, id and name of department. Each worker has name, id.
+Company
-int companyId
-String companyName
-Set<Department> listOfDepartments = new HashSet<Department>();
+Department
-int departmentId
-String departmentName
-Set<Worker> listOfWorkers = new HashSet<Worker>();
+Worker
-int workerId
-String workerName
#XmlRootElement(name="Company")
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
public class Company {
#XmlAttribute(name = "id")
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int companyId;
#XmlElement(name = "companyName")
private String companyName;
#XmlElement(name="Department")
#OneToMany(mappedBy = "company", cascade=CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Department> listOfDepartments = new HashSet<Department>();
#XmlRootElement(name="Department")
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
public class Department {
#XmlAttribute(name = "id")
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idDepartment;
#XmlElement(name = "departmentName")
private String departmentName;
#ManyToOne()
#JoinColumn(name="companyId")
private Company company;
#XmlElement(name="Worker")
#OneToMany(mappedBy = "department", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Worker> listOfWorkers = new HashSet<Worker>();
#XmlRootElement(name="Worker")
#Entity
public class Worker {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idWorker;
private String workerName;
#ManyToOne
#JoinColumn(name="departmentId")
private Department department;
ERROR:
A cycle is detected in the object graph. This will cause infinitely deep XML: ru.eldarkaa.dto.Company#d1e43ed ->
ru.eldarkaa.dto.Department#6e55f58 -> ru.eldarkaa.dto.Company#d1e43ed]
How to avoid this cycle?
You have a bidirectional relationship in your model. To solve it you could do the following:
Solutions Using Standard JAXB Metadata
1 - #XmlTransient
You can map one direction of the relationship with #XmlTransient. This will cause the field/property not to marshal preventing the infinite loop. This field/property also won't unmarshal meaning that you will need to populate it yourself.
2 - #XmlID/#XmlIDREF
#XmlIDREF is how you map shared references with JAXB you can use this to map the back-pointer relationship.
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
Solutions Leveraging EclipseLink JAXB (MOXy) Extensions
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You can use MOXy's #XmlInverseReference extension for this use case. It acts like #XmlTransient on the marshal operation, but will still populate the value on the unmarshal.
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html

Return complex type from Java Web service

I use Java Persistence, and I want a web method to return a 'portion' of an Entity class. For example, I have a Customer class that represents a record in Customer table with many fields, but I want to return just few of them. Is it possible to use mapping to do that? Or the only way is to create a new class (maybe a superclass for Customer) that has only fields I want to return? I tried binding, but it didn't work (apparently I did it in a wrong way):
#Entity
#Table(name = "Customer", catalog = "test", schema = "")
#XmlType(name = "Customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
#XmlElement
private Integer accountId;
#Basic(optional = false)
#Column(name = "username")
#XmlElement
private String username;
#Basic(optional = false)
#Column(name = "password")
private String password;
I thought that if I don't add #XmlElement annotation to password field, it won't be included into result. However, I got a bunch of "Class has two properties of the same name" errors during deployment.
This is because the default behaviour for XML generation is PUBLIC_MEMBER (http://java.sun.com/javaee/5/docs/api/javax/xml/bind/annotation/XmlAccessorType.html).
Since you are putting the #XmlElement on the fields, it is grabbing both your public getter/setter methods AND any field w/ #XmlElement on it. What you're likely going to want to is set the XmlAccessorType to either FIELD or NONE.
Annotate the class with
#XmlAccessorType(XmlAccessType.NONE)
Then annotate the fields you want to send with
#XmlElement(name="field_name")
There's actually a JAXB issue (that I can't find the reference to right now) that basically says that if you will be reading from XML, you'll want to annotate the setters and not the fields themselves.

Categories