JPA with PostgreSQL - simple relationship - java

Tables:
pattient ( id, name, id_status, ...) -> FK to pattient_status
pattient_status (id, description) -> target table
All I need is to obtain pattient_status.description inside my pattient.class, because my GET method needs this information on the JSON return.
Code:
#Entity
#Table(name="cad_paciente")
public class Paciente {
... (other columns)
#OneToOne
#JoinColumn(insertable=false, updatable=false, name = "id_status_paciente", referencedColumnName = "id")
private StatusPaciente status;
public String getStatusPaciente(){
return status.getStatus();
}
----
#Entity
#Table(name="cad_status_paciente")
public class StatusPaciente {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="ds_status")
#Size(max=50)
private String status;
This lists my information correctly, but on POST method, JPA saves correctly but returns the message:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.spin.spincare.model.Paciente["statusPaciente"])]
What should I do?

It's an issue with your getter:
public String getStatusPaciente() {
return status.getStatus();
}
In your POST call status is null, so when Jackson uses this getter to generate the JSON it gets a null pointer exception. Update it to something like:
public String getStatusPaciente() {
if (status == null) {
return null;
}
return status.getStatus();
}

Use #MapsId. This will make ids of entities match. Read more here.
#Entity
#Table(name="cad_status_paciente")
public class StatusPaciente {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#MapsId
#OneToOne
private Paciente paciente;
#Column(name="ds_status")
#Size(max=50)
private String status;
}

Related

How do I can bind an entity object to a local #Transient property?

EmployeeInfo entity class
#Entity
#Table(name = "employeeinfo")
public class EmployeeInfo{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "employeeId")
private String employeeId;
#Column(name = "firstName")
private String firstName;
#Column(name = "middleName")
private String middleName;
#Column(name = "lastName")
private String lastName;
......
}
Another Entity class ProjectTaskComments
#Entity
#Table(name = "projecttaskcomments")
public class ProjectTaskComments{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#Basic(optional = false)
#Column(name = "comments")
private String comments;
#Basic(optional = false)
#Column(name = "commentTime")
#Temporal(TemporalType.TIMESTAMP)
private Date commentTime;
#Column(name = "fkCommentedBy")
private Long fkCommentedBy;
#Transient
#JsonIgnoreProperties
private EmployeeInfo commentedEmployee;
#Transient
#Autowired
EmployeeInfoService employeeInfoService;
public EmployeeInfo getCommentedEmployee() {
EmployeeInfo employeeInfo;
employeeInfo = employeeInfoService.getSingleEmployeeInfoByFkUserId(this.fkCommentedBy);
if(employeeInfo != null) {
this.commentedEmployee.setEmployeeId(employeeInfo.getEmployeeId());
this.commentedEmployee.setFirstName(employeeInfo.getFirstName());
this.commentedEmployee.setMiddleName(employeeInfo.getMiddleName());
this.commentedEmployee.setLastName(employeeInfo.getLastName());
this.commentedEmployee.setPhoto(employeeInfo.getPhoto());
return commentedEmployee;
} else {
return null;
}
}
}
I tried to find an EmployeeInfo object in getCommentedEmployee() method by fkCommentedBy property and set to #Transient property commentedEmployee.
I found the following errors:
2018-10-11 13:07:56.834 WARN 16756 --- [nio-8081-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[1]->com.activeboss.model.pm.ProjectTasks["projecttaskcommentsCollection"]->org.hibernate.collection.internal.PersistentBag[0]->com.activeboss.model.pm.ProjectTaskComments["commentedEmployee"])
2018-10-11 13:07:56.853 WARN 16756 --- [nio-8081-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[1]->com.activeboss.model.pm.ProjectTasks["projecttaskcommentsCollection"]->org.hibernate.collection.internal.PersistentBag[0]->com.activeboss.model.pm.ProjectTaskComments["commentedEmployee"])
How do I solve it?
The purpose of #Transient is to model a non persistent attribute, so it is unclear to me why do you want to #Transient on commentedByEmployee attribute when you have it persisted via "fkCommentedBy" attribute. IMO, #ManyToOne is more appropriate in this case.
#Entity
#Table(name = "projecttaskcomments")
public class ProjectTaskComments {
// .... other declarations
#ManyToOne
#JoinColumn(name="fkCommentedBy")
private EmployeeInfo commentedEmployee;
// ..... other code
}
Now if you still want to use #Transient, then in the getter method you need to make sure that you have a valid referent to EmployeeInfoService object. #Autowired will not work here as the ProjectTaskComments is not a spring managed bean.
Agree #KishoreKirdat, neet to check null and do some initialization:
public EmployeeInfo getCommentedEmployee() {
// check here
if (employeeInfoService == null) return null;
EmployeeInfo employeeInfo = employeeInfoService.getSingle...;
if (employeeInfo != null) {
// init here
commentedEmployee = new EmployeeInfo();
commentedEmployee.set...;
return commentedEmployee;
} else {
return null;
}
}
private void setCommentedEmployee(EmployeeInfo employeeInfo) {
// do nothing
}
Yes, finally I could solve it. I just did the following works:
Added #Component to ProjectTaskComments class:
#Entity
#Component
#Table(name = "projecttaskcomments")
public class ProjectTaskComments{
........
Declared EmployeeInfoService as static and added a seter method for the service and #Autowired it.
#Transient
private static EmployeeInfoService employeeInfoService;
#Autowired
public void setEmployeeInfoService(EmployeeInfoService employeeInfoService) {
this.employeeInfoService = employeeInfoService;
}

Deserializing JSON from Hibernate

I'm having a problem de deserializing a class in Spring Boot. When my controller tries to deserialize it, it crashes. Here is the class:
#Entity
#Table(name="trash_cans")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TrashCan {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="TRASH_CAN_ID")
long id;
#Column(name="TRASH_CAN_NAME")
String name;
#ManyToOne
#JoinColumn(name="PLACE_ID")
private Place place;
#OneToMany(mappedBy="trashCan", targetEntity=TrashMeter.class, fetch=FetchType.LAZY)
private List<TrashCan> trashMeterList;
#OneToMany(mappedBy="trashCan", targetEntity=TrashSensor.class, fetch=FetchType.LAZY)
private List<TrashSensor> trashSensorList;
public TrashCan() {
}
public TrashCan(long id, String name) {
super();
this.id = id;
this.name = name;
}
[getters and setters]
}
That depends on this one:
#Entity
#Table(name="trash_sensor")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TrashSensor {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="name")
private String name;
#Column(name="description")
private String description;
#ManyToOne
#JoinColumn(name="TRASH_CAN_ID")
private TrashCan trashCan;
#OneToMany(mappedBy = "trashSensor", targetEntity = Measurement.class, fetch = FetchType.LAZY)
private List<Measurement> measurementList;
public TrashSensor() {
super();
}
And Trash Sensor Depends on this Class:
#Entity
#Table(name="measurement")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Measurement {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="value")
private float value;
#Column(name="last_measure")
private LocalDateTime dateTime;
#ManyToOne
#JoinColumn(name="trash_sensor_id")
private TrashCan trashSensor;
public Measurement() {
}
}
My Controler:
#RequestMapping(value="/trashCan", method=RequestMethod.GET)
public ResponseEntity<Iterable<TrashCan>> getPlaces(){
Iterable<TrashCan> trashCanIterable = trashCansRepository.findAll();
return new ResponseEntity<>(trashCanIterable, HttpStatus.OK);
}
When I call the webservice, I get this error:
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: could not deserialize (through reference chain: java.util.ArrayList[0]-br.com.simplepass.cleanerway.domain.TrashCan["trashSensorList"]-org.hibernate.collection.internal.PersistentBag[0]-br.com.simplepass.cleanerway.domain.TrashSensor["measurementList"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not deserialize (through reference chain: java.util.ArrayList[0]-br.com.simplepass.cleanerway.domain.TrashCan["trashSensorList"]-org.hibernate.collection.internal.PersistentBag[0]-br.com.simplepass.cleanerway.domain.TrashSensor["measurementList"])
I can't interpret this error =/. Any help with this problem greatly appreciated.
You are getting this error since your json is entering a loop, to avoid this, use #JsonIgnore annotation:
#OneToMany(mappedBy = "trashSensor", targetEntity = Measurement.class, fetch = FetchType.LAZY)
#JsonIgnore
private List<Measurement> measurementList;
It happens when you use relations between entities. Imagine that your TrashCan has link to Trash in it. And your trash has link to it's wrapper - trashcan. So what you try to serialize TrashCan entity you also serializing Trash. And then when you are serializing trash trashcan is serialized again inside it. And so on. It's a loop. You can use #JsonIgnore on every entity that may cause loop.
#JsonIgnore
#ManyToOne
#JoinColumn(name="PLACE_ID")
private Place place;
#JsonIgnore
#OneToMany(mappedBy="trashCan", targetEntity=TrashMeter.class, fetch=FetchType.LAZY)
private List<TrashCan> trashMeterList;
#JsonIgnore
#OneToMany(mappedBy="trashCan", targetEntity=TrashSensor.class, fetch=FetchType.LAZY)
private List<TrashSensor> trashSensorList;
But it's a bad way. It's strongly recommended to use DTO (Data transfer object) pattern for you serialization/deserialization. It also gives you more flexibility. You can read about it here
If you need trashMeterList and trashSensorList in response then follow this answer.
Due to hibernate lazy loading and no session while deserialisation, you are getting this exception.
To fix just change your controller:
#RequestMapping(value="/trashCan", method=RequestMethod.GET)
public ResponseEntity<Iterable<TrashCan>> getPlaces(){
Iterable<TrashCan> trashCanIterable = trashCansRepository.findAll();
List<TrashCan> responseList = new ArrayList<TrashCan>(trashCanIterable.size())
while(trashCanIterable.hasNext()){
TrashCan trashCan = trashCanIterable.next();
for(TrashMeter trashMeter : trashCan.trashMeterList){
}
for(TrashSensor trashSensor : trashCan.trashSensorList){
}
responseList.add(trashCan);
}
return new ResponseEntity<>(responseList, HttpStatus.OK);
}

Javax validation error when save a class which include ManyToOne relationship

I want to save Tesis class using entityManager.persist() method but I get following error.
Caused by: javax.validation.UnexpectedTypeException: HV000030: No validator could be found for type: thymeleafexamples.layouts.acenta.Acenta.
#Entity
public class Tesis {
public Tesis(){
}
public Tesis(String adi, Acenta acenta) {
this.adi = adi;
this.acenta = acenta;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotEmpty
private String adi;
#NotEmpty
#ManyToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
#JoinColumn(name="acenta_id")
private Acenta acenta;
//GETTERS AND SETTERS
}
/////////////////////////////////////////////////////////////////////
#SuppressWarnings("serial")
#Entity
public class Acenta implements java.io.Serializable {
public Acenta(String adi) {
this.adi = adi;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotEmpty
private String adi;
#OneToMany(mappedBy="acenta")
private Set<Tesis> tesiss;
}
According to Hibernate Validator's API org.hibernate.validator.constraints.NotEmpty annotation
"asserts that the annotated string, collection, map or array is not
null or empty"
Based on the above definition Acenta type is not a valid type to check. You may consider using javax.validation.constraints.NotNull annotation instead as it's valid for all types and moreover not vendor specific.

#Formula not working in hibernate with object

I have a enum of few status value
NEW, REVIEWD, PUBLISHED, PENDING, UPDATED, SPAM, DUPLICATE, IRRELEVANT, UNPUBLISHED
I don't want to use them as enumerated so created one entity for that. For convenient I want to keep a column in entity to initialize status from enum and convert that enumerated value to a Object of status entity. for this..
I have two entity. I want to refer a column with value from another entity.
Basically I want to initialize a object with formula.
Entities are
#Entity
#Table(name = "event_status")
public class EventStatus {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="eventStatusId")
private Integer eventStatusId;
#Enumerated(EnumType.STRING)
#Column(unique = true,name="eventStatusType")
private EventStatusType eventStatusType;
public EventStatus() {
this(EventStatusType.NEW);
}
public EventStatus(EventStatusType eventStatusType) {
super();
this.eventStatusType = eventStatusType;
}
public Integer getEventStatusId() {
return eventStatusId;
}
public EventStatusType getEventStatusType() {
return eventStatusType;
}
public void setEventStatusId(Integer eventStatusId) {
this.eventStatusId = eventStatusId;
}
public void setEventStatusType(EventStatusType eventStatusType) {
this.eventStatusType = eventStatusType;
}
}
I have another entity in which I am referring object of this entity
#Entity
#Table(name = "event_")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Event implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id_")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Transient
public EventStatusType eventStatusType = EventStatusType.NEW;
#ManyToOne(fetch = FetchType.EAGER, targetEntity = EventStatus.class)
#Formula("select * from event_status where eventStatusId= 1")
private EventStatus status;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public EventStatus getStatus() {
System.out.println("Event.getStatus() " + status);
return status;
}
public void setStatus(EventStatus status) {
System.out.println("Event.setStatus()");
this.status = status;
}
}
This is not giving any exception but not initializing this value.
Is it possible to initialize this EntityStatus with value of eventStatusType in Event entity
I would like to explain that based on the documentation:
5.1.4.1.5. Formula
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column. You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment).
#Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
The SQL fragment can be as complex as you want and even include subselects.
...
5.1.7.1. Using a foreign key or an association table
...
Note
You can use a SQL fragment to simulate a physical join column using the #JoinColumnOrFormula / #JoinColumnOrformulas annotations (just like you can use a SQL fragment to simulate a property column via the #Formula annotation).
#Entity
public class Ticket implements Serializable {
#ManyToOne
#JoinColumnOrFormula(formula="(firstname + ' ' + lastname)")
public Person getOwner() {
return person;
}
...
}
Also, we should use insertable = false, updatable = false, because such mapping is not editable

Not-null property references a transient value - transient instance must be saved before current operation

I have 2 domain models and one Spring REST Controller like below:
#Entity
public class Customer{
#Id
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;
// other stuff with getters/setters
}
#Entity
public class Country{
#Id
#Column(name="COUNTRY_ID")
private Integer id;
// other stuff with getters/setters
}
Spring REST Controller:
#Controller
#RequestMapping("/shop/services/customers")
public class CustomerRESTController {
/**
* Create new customer
*/
#RequestMapping( method=RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public com.salesmanager.web.entity.customer.Customer createCustomer(#Valid #RequestBody Customer customer, Model model, HttpServletRequest request, HttpServletResponse response) throws Exception {
customerService.saveOrUpdate(customer);
return customer;
}
// other stuff
}
I am trying to call above REST service with below JSON as body:
{
"firstname": "Tapas",
"lastname": "Jena",
"city": "Hyderabad",
"country": "1"
}
Where country code 1 is already there in Country table. The problem is when I am calling this service getting below error:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.test.model.Customer.country -> com.test.model.Country; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.test.model.Customer.country -> com.test.model.Country
Any help will be appreciated!
Try putting CascadeType.ALL
#OneToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
#JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;
I had a similar problem. Two entities: Document and Status.
Document had a relationship OneToMany with Status, that represented the history of Status the Document had.
So, there was a #NotNull #ManyToOne reference of Document inside Status.
Also, I needed to know the actual Status of Document. So, I needed another relationship, this time #OneToOne, also #NotNull, inside Document.
The problem was: how can I persist both entities the first time if both had a #NotNull reference to the other?
The solution was: remove #NotNull reference from actualStatus reference. This way, it was able to persist both entities.
Just to add an additional scenario that led me to this exact same error:
Make sure that any backward references that may exist are not null.
Specifically in my case, I was using Mapstruct to update some fields of the entity, e.g.
MyClass newInstance = //...
MyClass dbInstance = repository.findByField(someField);
MyClassMapper.MAPPER.update(dbInstance, newInstance);
repository.save(dbInstance);
And my poor implementation of MyClassMapper led the backward references of dbInstance fields to be set to null when they should be pointing back to dbInstance.
I got same error and this is how I solved it:
1st Entity:
#Entity
public class Person implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int personId;
private String name;
private String email;
private long phoneNumber;
private String password;
private String userType;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "personCustomer", cascade
= CascadeType.ALL)
private Customer customer;
2nd Entity:
#Entity
public class Customer implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int customerId;
#OneToOne(fetch = FetchType.LAZY, optional = false, cascade =
CascadeType.ALL)
#JoinColumn(name = "person_customer")
#JsonIgnore
private Person personCustomer;
My Controller:
#PostMapping("/customer/registration")
public PersonCustomer addCustomer(#RequestBody Person person)
{
Customer customer = new Customer(person);
person.setCustomer(customer);
Customer cust = customerRepo.save(customer);
logger.info("{}", cust);
Optional<Person> person_Cust =
personRepo.findById(cust.getPersonCustomer().getPersonId());
Person personNew = person_Cust.get();
PersonCustomer personCust = new PersonCustomer();
if(cust.equals(null))
{
personCust.setStatus("FAIL");
personCust.setMessage("Registration failed");
personCust.setTimestamp(personCust.timeStamp());
}
personCust.setStatus("OK");
personCust.setMessage("Registration OK");
personCust.setTimestamp(personCust.timeStamp());
personCust.setPerson(personNew);
return personCust;
}
The problem got solved when I added "person.setCustomer(customer);".
As both POJO classes has each others reference, so we have to "set" each others reference before using the JPA repository method(customerRepo.save(customer));
I had the exact same problem. The solution seems to be to send the JSON like this:
{
"firstname": "Tapas",
"lastname": "Jena",
"city": "Hyderabad",
"country": {"id":"1"}
}
I guess #RequestBody tries to map an entity not a single field since the Customer instance is referencing a Country instance.
(I have similarly two entities, joined. In the DB, records for the referenced entity (Country in your case) were already created but the entity creation (Customer in your case) with a json, provided the same error message. For me CascadeType.ALL not helped but the above written change in the JSON solved the problem. For further config of course CascadeType can be considered.)
you should change :
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;
to :
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="COUNTRY_ID")
private Country country;
just delete nullable setting.

Categories

Resources