ORM in Morphia for a Nested Class - java

My Json document in the Morphia DB looks like this -
{
"_id" : ObjectId("58fcdf7e"),
"status" : "ACTIVE",
"user" : {
"id" : NumberLong(228),
"email" : "testing#domian.com"
}
}
I have created a Java class for this collection which looks like this -
#Entity("member_offer")
public class MemberOffer {
#Id
private ObjectId objectId;
#Property("status")
private String status;
#Embedded("user")
private UserDetail user;
#Embedded
class UserDetail {
#Property("id")
public long memberId;
#Property("email")
public String email;
UserDetail() {
}
}
public ObjectId getObjectId() {
return objectId;
}
public void setObjectId(ObjectId objectId) {
this.objectId = objectId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public UserDetail getUser() {
return user;
}
public void setUser(UserDetail user) {
this.user = user;
}
}
Now when I am trying to fetch the data I am getting this exception -
java.lang.RuntimeException:
org.mongodb.morphia.mapping.MappingException: No usable constructor
for vo.MemberSubscription$UserDetail
Caused by: org.mongodb.morphia.mapping.MappingException: No usable
constructor for vo.MemberSubscription$UserDetail
Caused by: org.mongodb.morphia.mapping.MappingException: No usable constructor for vo.MemberSubscription$UserDetail
Caused by: java.lang.NoSuchMethodException: vo.MemberSubscription$UserDetail.()
Any idea how I can resolve this issue? I want UserDetail to be nested class only, I know if I create it as an independent class this error can be resolved. But my question here is can something like this (having nested class) can be achieved in Morphia?
Also if there is some fundamental flaw in my design please educate me about it.

You should try to use public modifier for the constructor, also make UserDetail (inner class) is static.

Related

How to change JSON representation for single value java object?

I had a class like:
public class EmailAddress {
public String value;
public String tld() {...}
public String host() {...}
public String mailbox() {...}
}
Now I use this class in an Object / Entity:
#Entity
public class Customer {
public String name;
public EmailAddress mail;
}
Now, when I do a rest service for Customer, I get this format:
{
"id": 1,
"name": "Test",
"email": {
"value": "test#test.de"
}
}
But I only want "email": "test#test.de"
{
"id": 1,
"name": "Test",
"email": "test#test.de"
}
What I must do? I use Spring Boot and Hibernate Entities.
Thank you for any support
You should use DTO class in request handling and make mappings from DTO to Entity and backwards, e.g.:
public class CustomerDTO {
private Integer id;
private String name;
private String email;
}
You should use DataTransferObjects for your (REST) APIs.
The DTOs only contain the fields the interface should provide (or receive).
When receiving objects from the client and before returning the object from your Controller you can convert the DTOs to your domain model (Which could be your JPA entites classes).
Example for a controller method. We assume you get an object from an user-editor which contains all data you want to update in your database-objects and return the updated company DTO:
#PutMapping
public CustomerDto updateCustomer(CustomerEditorDto updatedCustomerDto) {
Customer updatedCustomer = CustomerConverter.convert(updatedCustomerDto);
updatedCustomer = customerService.updateCustomer(updatedCustomer);
return CustomerConverter.convert(updatedCustomer);
}
and your Converter class:
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CustomerConverter {
public static CustomerDto convert(Customer customer) {
CustomerDto result = null;
if (customer != null) {
// TODO: set fields in result-dto
}
return result;
}
public static Customer convert(CustomerEditorDto customer) {
Customer result = null;
if (customer != null) {
// TODO set fields in result;
}
return result;
}
}
and here are the DTOs
#Getter
#Setter
public class CustomerDto {
private Integer id;
private String name;
private String email;
}
#Getter
#Setter
public class CustomerEditorDto {
private Integer id;
private String firstName;
private String lastName;
private String email;
private String otherPropertyOrStuff;
}
This way you can separate the API modell from your JPA entites. You can use the same models for input/output. And you can even use a different model to work with inside your services and the finally convert them into your JPA entites, before persisting the data (or after reading the data).
There are tools which can take care of the conversion, like mapstruct.
* The above annotations #Getter, #Setter, ... are from project lombok and very are handy to generate boiler-plate code automatically.
I found an other easier solution, use a JsonSerializer on the entity Property:
#JsonSerialize(using = EmailAddressSerializer.class)
private EmailAddress email;
The serializer class:
public class EmailAddressSerializer extends StdSerializer<EmailAddress> {
public EmailAddressSerializer() {
super(EmailAddress.class);
}
protected EmailAddressSerializer(Class<EmailAddress> t) {
super(t);
}
#Override
public void serialize(EmailAddress email,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(email.value);
}
}

Jackson ignores JsonProperty annotation in deserialization

I've got a bit of a conundrum. I'm trying to deserialize a json message into a pojo using the builder pattern and I'm getting the following error:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Information"
(class com.verification.client.models.response.Response$Builder), not marked as ignorable (3 known properties: "status", "products", "information"])
This is very confusing to me as I've clearly marked the field in the pojo with a JsonProperty annotation:
#JsonDeserialize(builder = Response.Builder.class)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Response {
#JsonProperty("Status")
private final Optional<Status> status;
#JsonProperty("Products")
private final Optional<List<ResponseProduct>> products;
#JsonProperty("Information") //Here's where the field is defined
private final Optional<List<ResponseInformation>> information;
private Response(final Builder b){
this.status = b.status;
this.products = b.products;
this.information = b.information;
}
public Optional<Status> getStatus() {
return status;
}
public Optional<List<ResponseProduct>> getProducts() {
return products;
}
public Optional<List<ResponseInformation>> getInformation() {
return information;
}
#JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
public static class Builder{
private Optional<Status> status;
private Optional<List<ResponseProduct>> products = Optional.empty();
private Optional<List<ResponseInformation>> information = Optional.empty();
public Builder(){}
public Builder status(final Status status){
this.status = Optional.of(status);
return this;
}
public Builder products(final List<ResponseProduct> products){
this.products = Optional.of(products);
return this;
}
public Builder information(final List<ResponseInformation> information){
this.information = Optional.of(information);
return this;
}
public Response build(){
return new Response(this);
}
}
}
I have a feeling it's something small, but at this point I am at a loss for why my code is behaving this way.
P.S.
Here's the json I'm deserializing
{
"Information": [{
"InformationType": "error-details",
"Code": "internal_application_error",
"Description": "Error: Internal",
"DetailDescription": []
}]
}
Solved this a while back, answering for posterity.
The issue I was having is that the build methods were not being correctly interpreted, but by defining #JsonSetter annotations on the methods of the static build class Jackson was able to correctly interpret the input and build the object.

Spring JSR 303 Validation access other field value while Edit/Add

I have a requirement wherein I want to use a Bean for both update/add. Now i have a validation as in the name should be unique.
Now during add the validation part is working correctly as it is checking for unique value by querying DB.
Now when i wanted to update the same record, it is trying to check the unique constraint in the DB and fails as the record already exists.
Role Bean
public class Role {
#NotEmpty
#Pattern(regexp = "[a-zA-Z ]*")
#UniqueValue(query = AppConstants.UNIQUE_VALIDATION_DB_QUERY)
private String roleName;
private String roleDesc;
private boolean active;
private String maskRoleName;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
My Custom Annotation Validator
public class UniqueValueValidator implements ConstraintValidator<UniqueValue, String> {
#Autowired
private ValidationDAO validationDAO;
private String query;
public void initialize(UniqueValue uniqueValue) {
this.query = uniqueValue.query();
}
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (!StringUtils.isEmpty(value) && !StringUtils.isEmpty(query)) {
return validationDAO.isValidUniqueField(query, value);
}
return true;
}
}
Now when I update only the RoleDesc Field from screen the role name is validated and throws the validation error as the same role name exists in DB. Is there a way wherein I can send other variable to my custom validator from screen saying the following is update screen so only validate the field if it is changed from its previous value?
I came with a work around by annotating on a getter method where all the required fields are returned as a single map through that method and in the validationIMPL I retrieved all the required information and processed accordingly.
private String roleName;
#UniqueValue(query = AppConstants.UNIQUE_VALIDATION_DB_QUERY)
public Map<String,String> getUniqueValidator(){
Map<String,String> validatorMap=new HashMap<String,String>();
validatorMap.put("ACTION",type of action(update/new)):
validatorMap.put("VALUE",this.roleName):
return validatorMap;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
What you are probably looking for are Groups. You would modify your annotation to:
#UniqueValue(query = AppConstants.UNIQUE_VALIDATION_DB_QUERY, groups = {CreationGroup.class})
You'll also need to create a CreationGroup interface.
Then you will need to update your interceptor that calls the bean validation to use contextual information (possibly provided by another annotation wrapping the method where the validation is happening) to be something like this:
if (myMethodIsCreatingANewRecord()) {
validator.validate(address, Default.class, CreationGroup.class);
} else {
validator.validate(address, Default.class);
}

JPA AccessType.Property failing to retrieve value from nested classes

I'm attempting to parse JSON into an #Entity in order to persist the data in a table. I confirmed the parsing works but I have run into an issue when attempting to persist the data. In order to retrieve the needed value in the nested classes (two deep) I am using the #Access(AccessType.PROPERTY) annotation on a "helper" method.
My best guess is the Entity object is being created prior to retrieving a value from the nested classes and when it attempts to retrieve the "nested" value those class instances are NULL. As a result it is unable to map the value to the persistence field.
Below is what I ascertain to be the important part of the exception:
Exception Description: The method [getNestedHref] on the object [com.something.dotcom.bleep.parsers.HierarchyV2] triggered an exception.
Internal Exception: java.lang.reflect.InvocationTargetException
Target Invocation Exception: java.lang.NullPointerException
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[nestedHref-->schema.tbl_Hierarchy.HREF]
Descriptor: RelationalDescriptor(com.something.dotcom.bleep.parsers.HierarchyV2 --> [DatabaseTable(schema.tbl_Hierarchy)])
Below is the JSON I'm parsing:
{
"links": {
"self": {
"href": "http:\/\/data.something.com\/v2\/hierarchy\/blah\/id\/blahblah"
}
},
"name": "nameValue",
"id": "idValue",
"hierarchyId": "hierarchyValue",
"narrowerTerm": [
{
"href": "http:\/\/data.something.com\/v2\/hierarchy\/blah\/id\/somethingElse1",
"sequence": 0
},
{
"href": "http:\/\/data.something.com\/v2\/hierarchy\/blah\/id\/somethingElse2",
"sequence": 1
},
{
"href": "http:\/\/data.something.com\/v2\/hierarchy\/blah\/id\/somethingElse3",
"sequence": 2
}
]
}
I'm having no issue persisting the NAME, ID, HIERARCHY_ID, UPD and CRT dates. I am also able to LOG the href using the toString() method. However, I cannot seem to persist this value (see href in links-->self-->href).
#JsonIgnoreProperties({"href","parentId","recordCreateDate","recordUpdateDate"})
#Entity
//#Cache(isolation = CacheIsolationType.ISOLATED)
#Table(name = "HIERARCHY_V2", schema = "SCHEMA")
#Access(AccessType.FIELD)
public class HierarchyV2{
#Id
private String id;
private String name;
#Column(name = "HIERARCHY_ID")
private String hierarchyId;
#Column(name = "PARENT_ID")
private String parentId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "REC_CRT_DT")
private Date recordCreateDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "REC_UPD_DT")
private Date recordUpdateDate;
#Transient
private HierarchyLinks links;
#Transient
private List<HierarchyTerm> broaderTerm;
#Transient
private List<HierarchyTerm> narrowerTerm;
//Typical getters, setters, overridden methods....
#Access(AccessType.PROPERTY)
#Column(name = "HREF")
protected String getNestedHref(){
return this.links.getSelf().getHref();
}
protected void setNestedHref(String href){
HierarchyLinks links = new HierarchyLinks();
this.links = links;
HierarchyV2Self hvs = new HierarchyV2Self();
this.links.setSelf(hvs);
hvs.setHref(href);
}
#Override
public String toString(){
return this.name + "\t" + this.id + "\t" + this.hierarchyId + "\t" + this.getNestedHref() + "\t" + this.parentId;
}
Following are the "nested" classes. I quickly fooled around with #Embeddable and #Embedded annotations in an attempt to make this work - and without putting much thought into it as my brain is now mush. I initially had these classes as static inner classes and then moved them out of the Entity class.
I spent about four hours writing and rewriting and I'm now swallowing my pride. Any help is appreciated.
public class HierarchyLinks {
private HierarchyV2Self self;
public HierarchyV2Self getSelf() {
return self;
}
public void setSelf(HierarchyV2Self self) {
this.self = self;
}
}
public class HierarchyV2Self {
private String href;
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
}
Your #Transient annotation may be causing problem here.
#Transient
private String href;
#Transient used to indicate that perticular field should not be persisted in underlying persistance system i.e. database.
you may follow this link as well

Getting JsonMappingException while sending data to view

I am trying to show DB data to my webpage.
I have made following code when GET request to the #RequestMapping(value = "/api/binder").
but when get request came to this method it will fetch data (I have print on console and display well) but it doesn't map to my Java Script Ajax call, it's showing me an error.
Following is my code for to fetch data :
#Autowired
IBinderViewRepository repository;
#RequestMapping(method= RequestMethod.GET)
public #ResponseBody
List<BinderResponse> getBinders(){
List<BinderView> binders = repository.getBinders();
List<BinderResponse> responses = new ArrayList<>();
ModelMapper mapper = Mapper.getInstance();
for(int i = 0; i < binders.size(); i++){
System.out.println("In Loop");
BinderResponse response = mapper.map(binders.get(i),BinderResponse.class);
System.out.println("Data :: " + response.getBinderName());
responses.add(response);
}
return responses;
}
but it shows me following error :
HTTP Status 500 - Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"])
Here is ajax call from knockout js :
ajax.get('api/binder').done(function(response){ ... }
Here BinderView and BinderResponse have same fields :
private String binderName;
private String binderAddress1;
and getter setter as well in both.
and repository.genBinders() method bring data from DB.
Here is insert method and works fine for me :
#RequestMapping(method= RequestMethod.POST,consumes = "application/json")
public #ResponseBody
IWebApiResponse addBinder(#RequestBody AddBinderForm binder){
.....
}
Shall I have to put any json annotation on my BinderResponse class ?
I don't understand where am i wrong ?Anyone pleas guide me.
UPDATE :
public class BinderResponse extends WebApiResponseBase {
private String binderName;
private String binderAddress1;
public String getBinderName() {
return binderName;
}
public void setBinderName(String binderName) {
this.binderName = binderName;
}
public String getBinderAddress1() {
return binderAddress1;
}
public void setBinderAddress1(String binderAddress1) {
this.binderAddress1 = binderAddress1;
}
}
BinderView :
public class BinderView extends BaseView {
private String binderName;
private String binderAddress1;
public String getBinderName() {
return binderName;
}
public void setBinderName(String binderName) {
this.binderName = binderName;
}
public String getBinderAddress1() {
return binderAddress1;
}
public void setBinderAddress1(String binderAddress1) {
this.binderAddress1 = binderAddress1;
}
}
In console it prints data / BinderName :
In Loop
Data :: ada
In Loop
Data :: tya
New Update :
Here is BaseView :
#MappedSuperclass
public abstract class BaseView implements IEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
private long id;
public long getId() {
return id;
}
public void setId(long id) {
if (this.id != 0 && this.id != id) {
throw new IllegalStateException(
"The ID must not be changed after it is set.");
}
this.id = id;
}
}
and In IEntity :
public interface IEntity extends Serializable {
long getId();
void setId(long id);
}
WebApiResponseBase :
public class WebApiResponseBase implements IWebApiResponse {
private String _uri;
#Override
public String getUri() {
return _uri == null ? "" : _uri;
}
#Override
public void setUri(String uri) {
_uri = uri;
}
}
Jackson, by default, serializes an object's whole inheritance hierarchy, ie. the parent class fields as well. In the case of
public class BinderResponse extends WebApiResponseBase {
it seems like
Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"])
Jackson tries to serialize a field called valid from a getter called isValid (which is a conventional bean property name). The getter method, however, seems to throw a NullPointerException for whatever reason.
If you want Jackson to ignore it, you can annotate the getter with #JsonIgnore or your class with #JsonIgnoreProperties and specify the property name, ie. valid.
In my case when I used #JsonIgnore the exception has been gone but the problem was it couldn't receive that value from API Request anymore and Spring ignored it (obviously because of #JsonIgnore) So I investigated about the issue and figured out that the problem was the getter and setter.
I had the Integer property while my getter was int. So when I changed the getter to Integer my problem solved and error's gone.
private Integer purchaseId;
#JsonIgnore
public int getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(int purchaseId) {
this.purchaseId = purchaseId;
}
Changed to :
private Integer purchaseId;
public Integer getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(Integer purchaseId) {
this.purchaseId = purchaseId;
}
#Column(name="createddate")
private Date createdDate;
#Transient
private String formatedCreatedDate;
public String getFormatedCreatedDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
return dateFormat.format(this.getCreatedDate());
}
It throws the same exception because here may be null by calling getCreatedDate() value come so it can't format null date so keep null check here like:
Solution
public String getFormatedCreatedDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
Date createDdate=this.getCreatedDate();
if(createDdate!=null){
return dateFormat.format(createDdate);
}
return "-";
}

Categories