How do I POST through RestTemplate a class with embedded members? - json

I have a class with a few members, and the associated setters and getters:
public class Tester implements Serializable {
#Column(name="ID", nullable=false, unique=true)
#Id
#GeneratedValue(generator="LOCATION_FACILITYTYPE_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name="LOCATION_FACILITYTYPE_ID_GENERATOR", strategy="native")
private int ID;
#Column(name="Value", nullable=false, unique=true, length=4)
private String value;
#Column(name="Name", nullable=false, unique=true, length=8)
private String name;
#ManyToOne(targetEntity=location.FacilityType.class, fetch=FetchType.LAZY)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="FacilityTypeID", referencedColumnName="ID", nullable=false) })
private location.FacilityType facility;
In a JUnit, I am trying to test creating a Tester element:
Tester trythis = new Tester();
trythis.setName("Herewe");
trythis.setValue("wow1");
Tester jnode = restTemplate.postForObject(TestBase.URL + "tester/", trythis, Tester.class);
This works as expected. However, if I use code like this to include an embedded member:
FacilityType ft = new FacilityType();
ft.setValue("AL");
ft.setName("2adamlec");
Tester trythis = new Tester();
trythis.setName("Herewe");
trythis.setValue("wow1");
trythis.setFacility(ft);
Tester jnode = restTemplate.postForObject(TestBase.URL + "tester/", trythis, Tester.class);
where the embedded member with value=AL does not yet appear in the database, I still get a new row created in the Tester table ... but the value and name columns in Tester are filled with the values (AL and 2adamlec) defined for FacilityType.
Note that we are using the JPARepository framework for FacilityType and Tester. The CRUD functions are thus handled 'under the covers', and I can't debug the POST processing. I wonder if this is associated with the fact that a GET for Tester data will only return the primitive fields in the JSON reply, since there is no projection defined for FacilityType.
Am I doing something wrong to cause the FacilityType fields to be saved in lieu of the desired Tester fields in the Tester table?

The short answer: when creating the item, you have to provide the data in the same JSON format that the server expects it. If you have an embedded class, you have to create a class where the facility member is a String to house a URL, then set that member to the URL corresponding to the existing instance of the embedded class. On the receiving end, you also need a new class like this:
public class FMT_Tester_RCV {
public FMT_Tester_RCV() { }
private String value;
private String name;
private Integer id;
private Integer ormid;
private JsonNode _links;
where you can travel down the JsonNode to get the link to the embedded class instance.

Related

LazyInitializationException when returning JSON in REST Webservice in Quarkus

I'm trying to build a simple application with Quarkus. Currently, I have two entity classes, which are related one-to-many:
#Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birthdate;
#OneToMany(mappedBy = "person")
public List<Address> addresses;
public static Person findByNameFirst(String name) {
return find("name", name).firstResult();
}
}
#Entity
public class Address extends PanacheEntity {
public String street;
...etc...
#ManyToOne
public Person person;
}
These are used by a simple REST webservice, which should store a Person to the database, select it again an return it:
#GET
#Path("storePerson")
#Produces(MediaType.APPLICATION_JSON)
#Transactional
public Person storePerson(
#QueryParam("name")String name,
#QueryParam("birthdate")String birthdate)
{
LocalDate birth = LocalDate.parse(birthdate, DateTimeFormatter.BASIC_ISO_DATE);
Person person = new Person(name, birth);
person.persistAndFlush();
Person p2 = Person.findByNameFirst(name);
return p2;
}
When calling the webservice the first time, the result is a JSON object with the stored data, which is as expected. When called again, an internal server error is thrown:
org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [Person.addresses] - no session and settings disallow loading outside the Session
As I understand, the error is thrown because the transaction only lasts until the storePerson method ends, but the conversion to JSON is happening outside of the method.
How can I prevent this error? I have read about the hibernate parameter "enable_lazy_load_no_trans" but it seems it is not supported in Quakus' application.properties.
The idea is to use a mapper framework such as MapStruct.
We don't recommend to directly expose your entities for 2 reasons:
the issue you have,
API management in the long run: you might have to change your model and not your API or the opposite.
There is an example here: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-quarkus .
The Quarkus version used is a bit old but AFAICS it should still work with latest Quarkus.
You can make the error go away by using Hibernate.initialize(person.addresses), then the collection gets initialized before the transaction ends.

Deep copy of properties one object to anothe from jackson?

I am performing an UPDATE operation such that all the non-null properties set in the incoming POJO shall be copied into another object (same type) and this shall happen for nested properties too.
Ex:
POJO:
public class Person {
private String homePhoneNumber;
private String officePhoneNumber;
private Address address;
public String getHomePhoneNumber() {
return homePhoneNumber;
}
// getter/setters
}
public class Address {
private String street;
private String houseNumber;
public String getStreet() {
return street;
}
// getter/setters
}
// Source
Person sourcePerson = new Person();
sourcePerson.setHomePhone("123");
Address address1 = new Address();
address1.setStreet("Street");
sourcePerson.setAddress(address1);
//Dest person
Person destPerson = new Person();
destPerson.setOfficePhone("456");
destPerson.setHomePhone("123");
Address address2 = new Address();
address2.setStreet("Street2");
address2.setHouseNumber("246");
destPerson.setAddress(address2);
ObjectMapper mapper = new ObjectMapper();
//skip setters for null values
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
Person result = mapper.updateValue(destPerson, sourcePerson);
So I want to copy all non-null properties set in sourcePerson to get copied in destPerson i.e overriding only those properties of destperson which sourcePerson has set, keeping other properties unchanged.
Using
Person result = mapper.updateValue(destPerson, sourcePerson);
is not working for nested properties. It's replacing the whole Address object from source to destination
I searched through jackson to find merge feature in jackson which:
mapper.setDefaultMergeable(true);
However, adding this configuration is making null values in sourcePerson nullify those in destPerson too, which seems strange.
mapper.configOverride(Address.class).setMergeable(true);
This above configuration works for what I wanted. But I have many POJO nested resources, so I don't want specific configurations for each POJO.
Is this can be achieved with jackson in a clean way ?
You can start by enabling error reporting with respect to merging
com.fasterxml.jackson.databind.MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE
This needs to be false.
It's indeed strange that mapper.configOverride() sort of works, but not mapper.setDefaultMergeable().
I don't see setters in your example. Aren't you using #JsonSetter annotations in Person class by any chance? Then they would override mapper configuration.
In jackson-databind Unit Tests I see they are using mapper.readerForUpdating() rather than mapper.updateValue():
private final ObjectMapper MAPPER = objectMapperBuilder()
// 26-Oct-2016, tatu: Make sure we'll report merge problems by default
.disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
.build();
public void testBeanMergingWithNullDefault() throws Exception
{
// By default `null` should simply overwrite value
ConfigDefault config = MAPPER.readerForUpdating(new ConfigDefault(5, 7))
.readValue(aposToQuotes("{'loc':null}"));
assertNotNull(config);
assertNull(config.loc);
// but it should be possible to override setting to, say, skip
// First: via specific type override
// important! We'll specify for value type to be merged
ObjectMapper mapper = newObjectMapper();
mapper.configOverride(AB.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
config = mapper.readerForUpdating(new ConfigDefault(137, -3))
.readValue(aposToQuotes("{'loc':null}"));
assertNotNull(config.loc);
assertEquals(137, config.loc.a);
assertEquals(-3, config.loc.b);
// Second: by global defaults
mapper = newObjectMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
config = mapper.readerForUpdating(new ConfigDefault(12, 34))
.readValue(aposToQuotes("{'loc':null}"));
assertNotNull(config.loc);
assertEquals(12, config.loc.a);
assertEquals(34, config.loc.b);
}
Also worth trying using com.fasterxml.jackson.annotation.JsonMerge directly in Person class.

Fetch related entities as Base Type

I'm currently trying to setup a database – using Java only. Given this simple class that might appear in the average social network app:
#Entity
class User {
#Id
private String email;
private String name;
private String otherInfo;
#ManyToMany
private List<User> contacts;
}
When the user logs in, he should receive the basic information and the list of contacts with their basic info, but not their contacts. To reduce the amount of boiler-plate code, I want to use a standard solution like Gson. However, even with lazy fetch the whole user is loaded on gson.toJson(user).
Therefore I thought of extracting the basic infos into a base class BasicUser and changing the contacts to List<BasicUser>. Now I only need to somehow circumwent the discriminator column when I fetch the contacts – of course they are all saved as complete users on the server. Unfortunately, I don't know how to archieve that. Any ideas?
If you need to get only part of the entity you can use projections. In your case it can be, for example, like this:
public interface BaseUser {
String getEmail();
String getName();
String getOtherInfo();
}
public interface UserRepo extends JpaRepository <User, String> {
List<BaseUser> findAllBy();
}
Using Jackson for serialization, the problem can be solved without writing custom serialization code. BasicUser contains the getters of the attributes, I want to serialize:
public interface BasicUser {
String getEmail();
String getFirstName();
String getLastName();
}
With a single annotation the contacts attribute is interpreted as a list of BasicUsers:
#Entity
public class User implements BasicUser {
#Id
private String email;
private String firstName;
private String lastName;
#ManyToMany
#JsonSerialize(contentAs = BasicUser.class)
private List<User> contacts = new ArrayList<>();
// ... implemented getters
}
You shouldn't have to modify your domain model just to accomodate a serialization library.
If you only want certain fields of a collection to be exposed to JSON, you could use Jackson with #JsonView (see here: How to serialize using #Jsonview with nested objects) not sure if Gson provides a similar feature as I have never used it extensively.

Remove Duplicate entry '59' for key 'PRIMARY in Hibernate

I am very new in Hibernate. I am using Hibernate with JPA. I have an annotated entity class and a table related to that entity class.
#Entity
public class Test implements Serializable {
#Id
#GenericGenerator(name="inc" , strategy="identity")
#GeneratedValue(generator="inc")
private int id;
private String address; // setter getter and constructor
}
When saving this entity, it insert the data into the db. But during application running process another application is inserting data into same table. when my application try to save the data then Duplicate entry '59' for key 'PRIMARY' exception generated. So I want to use a generator which can insert the data and generate id in database level rather than application level and the identifier must saved back to my entity.
Use the Table generator strategy or the sequence generator.
You do not have to specify a generator. You can use the default generator and never set the id manually. If the error still comes post your merge/persist method.
More informations about generators can you found here https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing
#Entity
public class Test implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String address; // setter getter and constructor
}

How to handle bi-directional references with polymorphic types and JSON?

Summary:
Is there any way for Jackson to handle bidirectional references with polymorphic types where #JsonTypeInfo is also used?
A note at the bottom of this page states no but it was written in 2010 and applied to Jackson v1.6.0 so I'm hoping maybe something has changed or someone can suggest an alternative approach.
Background:
I'm getting a JsonMappingException: Infinite recursion error using the Jackson library and JPA. I know I can add #JsonIgnore as suggested here but the downside is that I loose the bidirectional association when the JPA entities are serialized/deserialized.
Jackson v1.6.0 introduced the #JsonManagedReference and #JsonBackReference annotations. This looks great but the documentation from 2010 specifically states these annotations do not work with polymorphic handling using #JsonTypeInfo, which of course is what I have.
Below is a contrived example of my entity classes:
#Entity
public class Owner implements Serializable {
#Id
#GeneratedValue
#Column(name="owner_id")
private Long id;
#OneToMany(mappedBy="pet", orphanRemoval=true, cascade=CascadeType.ALL)
private List<Pet> pets;
public List<Pet> getPets() {return pets;}
public void setPets(List<Pet> pets) {this.pets = pets;}
}
#Entity
#DiscriminatorColumn(name="pet_type")
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = Dog.class, name = "dog"),
#Type(value = Cat.class, name = "cat"),
#Type(value = Bird.class, name = "bird") })
public class Pet implements Serializable {
#Id
#GeneratedValue
#Column(name="pet_id")
private Long id;
#ManyToOne
#JoinColumn(name="owner_id")
private Owner owner;
//#JsonIgnore
public Owner getOwner() {return owner;}
public void setOwner(Owner owner) {this.owner = owner;}
}
This is not an immediate solution, but Jackson 2.0.0 will finally have support for full Object Id handling, using #JsonIdentityInfo annotation.
Documentation is still in-progress (page should be this); but unit tests have decent examples.
The idea will be to indicate types for which Object Id is needed (or, alternatively, indicate properties), and usage is very similar to that of #JsonTypeInfo.
Jackson 2.0.0 RC1 was released a week ago, and the hope is that final 2.0.0 should go out before end of March 2012.