"Parameter specified as non-null is null" is thrown when trying to reference one JDL entity from another entity in JHipster Kotlin - spring

I'm trying to build a website in JDL Kotlin, but am having a problem with the JDL generated entities and edit forms. I have a JDL entity called Provider, an entity called Plan that needs to reference which provider it belongs to, and a third entity called PerDayPricing that references both Provider and Plan, all as per the following JDL configuration:
entity Provider {
// [...]
}
entity Plan {
// [...]
}
entity PerDayPricing {
// [...]
}
relationship OneToMany {
Provider to Plan
}
relationship OneToMany {
Provider to PerDayPricing
Plan to PerDayPricing
}
service all with serviceImpl
However, when I try to create or set the Provider field on a Plan item, it spits out the following error:
WARN [PID] --- [ XNIO-1 task-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageConversionException: JSON conversion problem: Parameter specified as non-null is null: method com.example.project.domain.Plan.setPerDayPricings, parameter <set-?>; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Parameter specified as non-null is null: method com.example.project.domain.Plan.setPerDayPricings, parameter <set-?> (through reference chain: com.example.project.domain.Plan["perDayPricings"])]
PerDayPricing is referenced here even though I didn't change any of its items. When I do try to set the Provider field on a PerDayPricing item, it gives the following error:
WARN [PID] --- [ XNIO-1 task-32] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageConversionException: JSON conversion problem: Parameter specified as non-null is null: method com.example.project.domain.Provider.setPlans, parameter <set-?>; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Parameter specified as non-null is null: method com.example.project.domain.Provider.setPlans, parameter <set-?> (through reference chain: com.example.project.domain.PerDayPricing["provider"]->com.example.project.domain.Provider["plans"])]
I actually have no idea what's going on here as I don't have a lot of experience with JHipster. I simply imported the JDL source file and let JHipster create files based on its configuration, without changing any of them, and this is already happening. References to the method names setPlans and setPerDayPricings don't even exist in the codebase, so I'm assuming they are being generated by Kotlin in the background?
Does anyone know what's going on and how I can fix it?
Edit: The following are the signatures of the entity classes (regular fields were removed for brevity):
// src/main/kotlin/domain/Provider.kt
#Entity
#Table(name = "plan")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class Plan(
// [...]
#OneToMany(mappedBy = "plan")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var perDayPricings: MutableSet<PerDayPricing> = mutableSetOf(),
#OneToMany(mappedBy = "plan")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var fixedDurationPricings: MutableSet<FixedDurationPricing> = mutableSetOf(),
#ManyToOne #JsonIgnoreProperties("plans")
var provider: Provider? = null
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
) : Serializable
// src/main/kotlin/domain/Plan.kt
#Entity
#Table(name = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class Provider(
// [...]
#OneToMany(mappedBy = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var plans: MutableSet<Plan> = mutableSetOf(),
#OneToMany(mappedBy = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var perDayPricings: MutableSet<PerDayPricing> = mutableSetOf(),
#OneToMany(mappedBy = "provider")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var fixedDurationPricings: MutableSet<FixedDurationPricing> = mutableSetOf()
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
) : Serializable
// src/main/kotlin/domain/PerDayPricing.kt
/**
* A PerDayPricing.
*/
#Entity
#Table(name = "per_day_pricing")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
data class PerDayPricing(
// [...]
#ManyToOne #JsonIgnoreProperties("perDayPricings")
var provider: Provider? = null,
#ManyToOne #JsonIgnoreProperties("perDayPricings")
var plan: Plan? = null
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
) : Serializable

If it generates data classes, then compiler will generate you the setters and getters. I suppose, when you want to set the entity to one another, you did not provide those fields from the exception. Kotlin not support null values by default. So either you have to provide those fields or mark the type as nullable with ? (question mark) at the and of the type. For example
var perDayPricings:String?
You should update the question with the given code, so we can see what is the problem.

Related

Repository.saveAll throws InvalidDataAccessApiUsageException

I query a Bugzilla REST API with Spring, which returns a JSON object with the following structure:
https://bugzilla.mozilla.org/rest/bug/35
I have the following JPA class (excerpt):
#Entity
#Table
public class bug {
....
#ManyToOne (fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn (name = "assigned_to_user_id")
#SerializedName ("assigned_to_detail")
protected BugUser assignedTo;
#ManyToMany (cascade = CascadeType.ALL)
#JoinColumn (/ * ... shortened ... * /)
#SerializedName ("c_detail")
protected List <BugUser> cc;
...
}
The web service call takes place with the help of the Spring remainder template. Then the object is mapped from the rest template into my JPA class. However, when the data set is persisted by the Spring repository, I get the following exception:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException:
Multiple representations of the same entity [BugUser # 2] are being
merged. Detached: [BugUser # 21]; Detached: [BugUser # 12];
As soon as I only comment one member variable of type BugUser in the Bug class, everything works.
How can I work around this problem?
You will have to make sure there is only one object with the same primary key. You can go through the list before merging and do something like the following:
Bug b = ...
BugUser assignee = b.assignedTo;
ListIterator<BugUser> iter = b.cc.listIterator();
while (iter.hasNext()) {
if (assignee.id.equals(iter.next().id) {
iter.set(assignee);
}
}
This way you ensure that the objects in the list of the same primary key are the same as the assignee. That is exactly why Hibernate complains.

JaversException ENTITY_INSTANCE_WITH_NULL_ID for ignored id

Using javers 5.11.2 I get the following exception although the id is set to be ignored. Why is that?
JaversException ENTITY_INSTANCE_WITH_NULL_ID: Found Entity instance 'my.package.javers.Leaf' with null Id-property 'id'
Update: I learned that
JaVers matches only objects with the same GlobalId
The id is specified using javax.persistence.Id. However, with each ORM it is possible to have an entity with a collection, then add a new element without id to that entity and then save it (CascadeType.Persist).
Is there any way to compare objects with javers in such a case?
Example (used lombok for boiler plate code).
The leaf:
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Data
#Entity
public class Leaf {
#DiffIgnore <============ id is ignored
#Id
private Long id;
private String color;
}
The tree:
#Builder
#Data
#Entity
public class Tree {
#Id
private Long id;
private String name;
#OneToMany
private Set<Leaf> leafs;
}
Test adds a leaf to the oakSecond without an id set. The diff cannot be made. An Exception is thrown.
#Test
public void testCompare_AddLeafToTree() {
Leaf leaf = Leaf.builder().id(1L).color("11").build();
Set<Leaf> leafsOfOakFirst = new HashSet<>();
leafsOfOakFirst.add(leaf);
Tree oakFirst = Tree.builder().id(1L).name("oakFirst").build();
oakFirst.setLeafs(leafsOfOakFirst);
Set<Leaf> leafsOfOakSecond = new HashSet<>();
leafsOfOakSecond.add(leaf);
leafsOfOakSecond.add(Leaf.builder().color("12").build());
Tree oakSecond = Tree.builder().id(1L).name("oakFirst").build();
oakSecond.setLeafs(leafsOfOakSecond);
Javers javers = JaversBuilder.javers().build();
Changes changes = javers.compare(oakFirst, oakSecond).getChanges();
assertThat(changes).isNotEmpty();
}
Same with the following definition of the Javers instance:
EntityDefinition leafEntityDefinition = EntityDefinitionBuilder.entityDefinition(Leaf.class).withIgnoredProperties("id").build();
Javers javers = JaversBuilder.javers().registerEntity(leafEntityDefinition).build();
You can't pass an Entity to Javers with null Id because it would be non-identifiable. If you use Hibernate to generate your Ids, make sure that you pass your object to javers.commit() after hibernate are done with its job. That's how the #JaversSpringDataAuditable aspect works.
Alternatively, you can model those objects with unstable IDs as Value Object in Javers.

Spring Data Rest & Lombok - Exception while adding adding relation

In my project I have 2 entities. Survey and entries to survey. They are in relation one to many (thare can be many entries to one survey).
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "survey_entries")
#TypeDef(name = "SurveyEntry", typeClass = SurveyEntry.class)
public class SurveyEntryEntity extends AbstractEntity {
#ManyToOne
#JoinColumn(name = "survey_id")
private SurveyEntity survey;
#NonNull
#Type(type = "SurveyEntry")
#Column(name = "responses")
// JSON db column type mapped to custom type
private SurveyEntry responses;
}
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "surveys")
#TypeDef(name = "Survey", typeClass = Survey.class)
public class SurveyEntity extends AbstractEntity {
#NonNull
#Type(type = "Survey")
#Column(name = "template")
// JSON db column type mapped to custom type
private Survey survey;
#OneToMany(mappedBy = "survey")
private List<SurveyEntryEntity> entries;
}
I have also created 2 rest repositories using Spring Data Rest:
#RepositoryRestResource(collectionResourceRel = "survey_entries", path = "survey-entries")
public interface SurveyEntryRepository extends PagingAndSortingRepository<SurveyEntryEntity, Long> {
}
#RepositoryRestResource(collectionResourceRel = "surveys", path = "surveys")
public interface SurveyRepository extends PagingAndSortingRepository<SurveyEntity,Long> {
}
I have successfully added survey by rest POST request and I can access it entries (currently empty) by sending GET to /api/surveys/1/entries.Now I want to add entry to exisiting survey. And while I can add it by sending POST (content below) to /api/survey-entries I have troubles adding it directly as a reference to survey. I'm using POST method with the same content and url /api/surveys/1/entries. What is interesting, I'm getting NullPointerException in logs and entry is not inserted but audit modify timestamp in survey is changed. What am I doing wrong? Did I miss same configuration? Or should I use different content?
Content of POST with entry:
{
"responses": {
"question1": "response1",
"question2": "response2",
"question3": "response3"
}
}
Content of POST with survey:
{
"survey": {
//survey structure
}
}
Exception:
08:41:14.730 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod - Failed to resolve argument 1 of type 'org.springframework.data.rest.webmvc.PersistentEntityResource'
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
#EDIT
I have tried adding entry by POST to /api/survey-entries with 'application/hal+json' Content-Type header and content as below, but now I'm getting other exception:
Content:
{
"survey" : "http://localhost:8080/api/surveys/1",
"responses": {
"question1": "response1",
"question2": "response2",
"question3": "response3"
}
}
Exception:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.domain.SurveyEntity` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/surveys/1')
at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 41] (through reference chain: com.domain.SurveyEntryEntity["survey"])
#Edit 2
Added Lombok annotations present on Entity classess
Unfortunatelly problem lied in Lombok annotations which weren't included in sample code. I added them now so any one can see where the problem lies.
I managed to solve it by downgrading Lombok to version (1.16.14) and changing annotation #AllArgsConstructor to #AllArgsConstructor(suppressConstructorProperties = true). It's immposible to achieve in later Lombok versions as this property is currently removed.
I have found solution on Spring Data Rest JIRA. There is already issue DATAREST-884 mentioning problem and presenting solution/workaround.
Sorry for wasted time while it was impossible to see solution without all the code.

Spring batch can't find entity persisted while processing

In one of our spring batch jobs, we create additional entities (CompanyProfile) during processing and persist them to the DB (in a separate transaction). These entities are referenced by other entities (Vacancy), which will be persisted by the writer, but unfortunate the writer fails with this error:
Caused by: javax.persistence.EntityNotFoundException: Unable to find com.company.CompanyProfile with id 1409881
The model is as follows:
#Entity
public class Vacancy {
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "company", nullable = true)
private CompanyProfile company;
...
}
#Entity
public class CompanyProfile {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
}
In the processor we have this:
CompanyProfile company = companyProfileService.handleCompany(compName);
vacancy.setCompany(company);
Where the method companyProfileService.handleCompany() is annotated with #Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW )
I'm sure the CompanyProfile gets persisted - I can see it in the DB, but when the Vacancy gets saved by the ItemWriter, it fails with the above exception. (also, note that the id of the persisted entity is mention in the exception above)
Do you see any reason why the writer would fail in this case?
With information you gave us my guess is that transaction opened by SB is unable to see data persisted by companyProfileService.handleCompany() method because service component uses a different transaction than SB ones; you have to check database ISOLATION_LEVEL property

Retrieve entity auto generated Id

I am trying to find a way to retrieve the auto generated Id of an entity that is persisted in the database via cascade. I am using Hibernate 4.1.9, Spring data 1.2 and Spring framework 3.2.1. Here are the entities in question : Location, Home, Room.
Location parent class
#Entity
#Table(name = "location")
#Inheritance(strategy = InheritanceType.JOINED)
public class Location implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "location_id", unique = true)
private long uuid;
// other attributes and methods not relevant
}
Home class extending a Location, referencing a set of Rooms
#Entity
#Table(name = "home")
#Inheritance(strategy = InheritanceType.JOINED)
#PrimaryKeyJoinColumn(name = "home_id")
public class Home extends Location implements Serializable
{
#OneToMany(mappedBy = "containingHome", cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Room> rooms;
// other attributes and methods not relevant
}
and finally the Room class referencing a Home object
#Entity
#Table(name = "room")
#PrimaryKeyJoinColumn(name = "room_id")
public class Room extends Location implements Serializable
{
#ManyToOne()
#JoinColumn(name = "home_id")
protected Home containingHome;
// other attributes and methods not relevant
}
I am using Spring data to create Repositories for the entities.
LocationRepository
public interface LocationRepository extends JpaRepository<Location, Long>
{ }
The problem I am having is that I need the id in order to be able to retrieve the different objects from the database and that is generated automatically. The only way I can access the id through the element is if I get the managed object when I save it to the database. But if I try to save each location in turn like so:
Home home = new Home();
home = locationService.save(home) // service that just calls locationRepository.save method
Room bedroom = new Room(home);
bedroom = locationService.save(bedroom);
I get a duplicate entry of room in the database which I think is related to a Hibernate issue https://hibernate.onjira.com/browse/HHH-7404. If I just call
Home home = new Home();
Room bedroom = new Room(home);
locationService.save(home)
there are no doubles but I have no way to retrieve the room object since it was persisted on cascade and its id is 0. Is there a way to solve this without introducing other fields in the location like a unique name that I have to generate myself? Any help is much appreciated.
Edit
If in the last case I have home = locationService.save(home) and then call home.getUuid() I get the right value which is normal I think since I retrieve a managed object. But if I do bedroom.getUuid() I get 0 since bedroom is not managed and so it has not had its id field updated with the value from the database.
Have you tried calling home.getUuid(); (assuming you have a getter for that field) after the persist call?
You might be surprised, but Hibernate (and JPA) will update the in memory copy with the id.

Resources