Spring Data Rest - Creating the parent and embedded object in one request - spring

I have a parent object that looks like this:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(nullable = false)
private String title
#Column(nullable = false)
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Child> sentenceList;
}
And a child object that looks like this:
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(nullable = false)
private String title
#ManyToOne
#JoinColumn(nullable=false)
private Parent parent;
}
What I want to do is POST to the parent's repository to create the parent and also create the embedded children. The POSTed JSON would look something like this:
{
"title": "Parent"
[
{
"title": "Child 1"
},
{
"title": "Child 2"
},
]
}
Is this possible with Spring Data REST? I currently get an error stating that the parent PK can't be null.
Thanks for any help!

Related

Hibernate OneToOne relationship needs child value to be passed from parent domain object

I have created the below OneToOne relationship with Hibernate and Kotlin. However, when I am Initializing Parent() it requires me to set child value as Parent(child=null) which is not desired. Only initializing child should require parent as Child(parent=Parent(...) and if I add both parent to child and child to parent, it creates an infinite loop. What it the right way to avoid that?
#Entity
class Parent(
#Id
#Column(nullable = false, updatable = false, columnDefinition = "uuid")
val id: UUID = UUID.randomUUID(),
#OneToOne(cascade = [CascadeType.ALL], mappedBy = "parent")
#JsonIgnore
#JoinColumn(name = "child_id", referencedColumnName = "id")
val child: Child?
)
#Entity
class Subscriber(
#Id
#Column(nullable = false, updatable = false, columnDefinition = "uuid")
val id: UUID = UUID.randomUUID(),
#OneToOne(cascade = [CascadeType.ALL], optional = false)
#JoinColumn(name = "id", columnDefinition = "uuid")
#MapsId
val parent: Parent
)
As parent and child are mapped one to one and you want to use #MapsId to not create another extra PK in child table. Now Child object will use parent_id has its own PK.
For Parent
#Entity
public class Parent {
#Id
#Column(nullable = false, updatable = false, columnDefinition = "uuid")
private UUID id = UUID.randomUUID();
public UUID getId() {
return id;
}
public Parent setId(UUID id) {
this.id = id;
return this;
}
}
Child
#Entity
public class Child {
#Id
#Column(nullable = false, updatable = false, columnDefinition = "uuid")
private UUID id = UUID.randomUUID();
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private Parent parent;
public UUID getId() {
return id;
}
public Child setId(UUID id) {
this.id = id;
return this;
}
public Parent getParent() {
return parent;
}
public Child setParent(Parent parent) {
this.parent = parent;
return this;
}
}
Check below screenshot for how table will look in database.

Can orphanRemoval be used for depth > 1?

Should all child1 and child2 (depth 2) be deleted, when a parent gets deleted?
Database is Informix, constraints are created in the child tables. Deletion of parent is performed with JpaRepository.deleteById(parent.getId()), both do nothing and no error message occurs (show_sql just lists selects). Spring version is 5.3.19, spring-data-jpa 2.6.4.
Current example code:
#Entity
#Table(name = "parent_table")
public class Parent
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.EAGER)
private Set<Child1> children = new HashSet<>();
}
#Entity
#Table(name = "child1_table")
public class Child1
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "parentid", referencedColumnName = "id", nullable = false)
private Parent parent;
#OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "child1", fetch = FetchType.EAGER)
private Set<Child2> children = new HashSet<>();
}
#Entity
#Table(name = "child2_table")
public class Child2
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "child1id", referencedColumnName = "id", nullable = false)
private Child1 child1;
}
Update
added
#PreRemove
private void deleteChildren()
{
children.clear();
}
to Parent and Child1. Now children get deleted, but not the Parent.
In fact, Parent also had a parent and I had to remove this from it's Set too.
So the solution is:
Clear children Sets
#PreRemove
private void preRemove()
{
children.clear();
}
Remove the root entity from its parent in case it has a parent
#PreRemove
private void preRemove()
{
children.clear();
parentsParent.getParents().remove(this);
}

DTO and Entities mapping

I am building a Spring Rest Application, I need help with DTO's and parsing a result to a endpoint
This is json that I return at the moment to the endpoint:
{
"id": 1,
"name": "Ella - IPA Is Dead",
"description": "2015 IPA is Dead Series. Supremely floral, this hugely under-rated hop is related to Galaxy and was first cultivated in the Australian state of Victoria.",
"method": {
"mash_temp": [
{
"temp": {
"value": 65
}
}
]
}
}
I don't want to return "method" from this json, I just need "id", "name", "description", "mash_temp" - so it should look like this:
{
"id": 1,
"name": "Ella - IPA Is Dead",
"description": "2015 IPA is Dead Series. Supremely floral, this hugely under-rated hop is related to Galaxy and was first cultivated in the Australian state of Victoria. Initially given the same name as a certain Eurolager, their lawyers got involved and the St- prefix was dropped. Ella displays subtle notes of spice, but is fundamentally a truly floral bouquet, redolent of the Southern Hemisphere.",
"mash_temp": [
{
"temp": {
"value": 65
}
}
]
}
Those are the entities that I am using now:
Beer Entity:
#Entity
public class Beer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "beer_id", unique = true, nullable = false)
private Integer id;
#Column(name = "name", nullable = false)
private String name;
#JsonProperty("description")
#Column(name = "description", nullable = false, columnDefinition = "TEXT")
private String description;
#JsonProperty("method")
#OneToOne(cascade = CascadeType.ALL)
private Method method;
}
Method Entity:
#Entity
public class Method implements Serializable
{
#JsonIgnore(value = true)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonProperty("mash_temp")
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "mash_temp")
private List<MashTemp> mash_temp = new ArrayList<>();
}
MashTemp Entity:
#Entity
public class MashTemp implements Serializable
{
#JsonIgnore(value = true)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(cascade = CascadeType.ALL)
private Temp temp;
#ManyToOne
private Method method;
}
Temp Entity:
#Entity
public class Temp implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Integer value;
#JsonIgnore(value = true)
private String unit;
#OneToOne
private MashTemp mashTemp;
}
Does anyone know how to create DTO's from this Entities but without "method" field?
Also this is my Controller:
#GetMapping("/beers")
public ResponseEntity<Set<Beer>> getAllBeers()
{
return new ResponseEntity<>(beerService.getAllBeers(), HttpStatus.OK);
}
#GetMapping("/beers/{id}")
public ResponseEntity<Beer> getById(#PathVariable Integer id) {
Beer beer = beerService.findById(id);
return new ResponseEntity<>(beer, HttpStatus.OK);
}
Have a look at the #JsonUnwrapped annotation (https://fasterxml.github.io/jackson-annotations/javadoc/2.8/com/fasterxml/jackson/annotation/JsonUnwrapped.html). You can put it on the method field in the Beer class, and then the properties of the Method class are serialized directly on the same level as the ones from Beer.

Print sub categories list with categories in JSON response

I have mysql table like this
https://i.stack.imgur.com/ON3JL.png
this is entity class for this table
Where(clause = "active =1")
#Entity
#Table(name = "category", catalog = "businessin")
public class Category implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
private String name;
private Integer parentId;
private Integer active;
private String pic;
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category")
private List<Product> products = new ArrayList<Product>();
setters&getter
}
I'm building RESTful API using spring rest and spring data (jpaRepositories)
I want when printing main categories from controllers as JSON reponse i want them to have also a list of subCategories
example for this
[
{
id: 1,
name: "Electronics",
parentId: 0,
active: 1,
pic: null
}]
to this
[
{
id: 1,
name: "Electronics",
parentId: 0,
active: 1,
pic: null
subCategories: [Mobile, Laptops]
}]
it's simple make your entity like this
#Where(clause = "active =1")
#Entity
#Table(name = "category" )
public class Category implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
private String name;
#Column(nullable=false,columnDefinition="int default 1")
private Integer active;
private String pic;
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category")
private List<Product> products = new ArrayList<Product>();
#ManyToOne
#JoinColumn(name="parent_id")
#JsonIgnore
// #ColumnDefault("0")
private Category parentId;
#OneToMany(mappedBy="parentId")
private List<Category> subCategories=new ArrayList<>();
setters and getters
}

Add entity with OneToOne Relation using JPA and REST

I am using Spring JPA Restful, and I don't understand how to insert an entity with a foreign key.
Activity Entity:
#Entity
#Table(name= "Activity")
public class Activity implements Serializable{
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name="uuid", strategy = "uuid2")
#Column(name = "uuid", nullable = false, unique = true)
private UUID uuid;
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.MERGE)
#JoinColumn(name="type", nullable = false)
private ActivityType type;
#Column(nullable = false)
private String label;
ActivityType Entity:
#Entity
#Table(name= "ActivityType")
public class ActivityType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(nullable = false, unique = true)
private String code;
#Column(nullable = false
private String label;
Is it possible to insert Activity simply? With something like this JSON where ActivityType's id "1" exists:
createActivity:
{"label":"LABEL","type":1}
With this code I have to do:
createActivity:
{"label":"LABEL","type":{"id":1}}
which return value is:
{
"uuid": "a54b27aa-8d49-41fd-8976-70c019c40e3b",
"type": {
"id": 1,
"code": null,
"label": null
},
"label": "LABEL",
"details": null
}
I use the library gson for parsing domain classes into JSON.
//... code for making your activity, let's say you have an Activity object myActivity
Just add the following code where you want to parse your object into JSON.
Gson gson = new GSON();
String json = gson.toJson(myActivity);

Resources