I would like to while post an object, to return the data from the reference table of the relationship.
The relationship is defined as follow:
#Entity
#Table( name = "application")
public class Application {
#Id
#JsonIgnore
private Long id;
#ManyToOne
#JoinColumn(name = "product_category")
private ProductCategory productCategory;
...
}
And:
#Entity
#Table(name = "product_category")
public class ProductCategory {
#Id
#Column(name = "product_category_id")
private Long productCategoryId;
#Column(name = "description")
private String description;
#JsonIgnore
#OneToMany(mappedBy = "productCategory")
private Set<Application> applications;
...
}
My product_category table has the following data:
While posting my Json object with the following structure:
...
{
"productCategory": "0"
}
...
I would like to get the following json output:
...
"productCategory": {
"productCategoryId": 0,
"description": "Personal Loan"
},
...
But instead I'm getting:
...
"productCategory": {
"productCategoryId": 0,
"description": null
},
...
Can you please advise?
There is not much code in your question, but you mentioned in your comment, what you are returning:
return applicationRepository.save(appRequest);
On the other hand, you are not cascading changes from parent to children in your mapping:
#ManyToOne
#JoinColumn(name = "product_category")
private ProductCategory productCategory;
I think changing #ManyToOne to #ManyToOne(cascade=CascadeType.ALL) should help.
Edit:
If you cannot change the model, simply return your entity:
return applicationRepository.findById(id);
This way jpa won't overwrite the entity on saving, it simply reads one from db.
Related
I am using spring boot rest api, data jpa, I just want to use event class and not to update from person class.
Following are entity classes,
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer person_id;
private Integer age;
String first_name;
String last_name;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "person_event",
joinColumns = { #JoinColumn(name = "person_id") },
inverseJoinColumns = { #JoinColumn(name = "event_id") })
private Set<Event> event;
}
Event class is
#Entity
#Table(name = "event")
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer event_id;
private String event_date;
private String title;
}
I am adding the values from postman as json string, I want to add the event values from event entity, But I dont want to update those values from person entity. From person entity I just want to use the event id but I dont want to update the event table.
{
"age": 32,
"first_name": "Srinath",
"last_name": "murugula",
"event": [
{
"event_id": 1
},
{
"event_id": 3
}
]
}
The above string is to adding the person but also updating the other fields of entity class(i.e., event_date and title as null.Because I am not mentioning those fields.In Person class I just want use id but dont want to update event table fields.
I have used:
#Cascade({CascadeType.Detach})
It solved my problem. Working fine.
You can use:
#Cascade({CascadeType.SAVE_UPDATE})
https://mkyong.com/hibernate/cascade-jpa-hibernate-annotation-common-mistake/
Good Evening,
I am relatively new to using Hibernate, and I am running into the following error:
"message": "org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist:
com.company.project.data.relational.models.ListsItems; nested exception is org.hibernate.PersistentObjectException:
detached entity passed to persist: com.company.project.data.relational.models.ListsItems",
I have a JSON object being sent from the front-end that has a nested object. I am trying to get the the nested items in a separate table in MySQL, with a relationship using the original objects ID.
Here's an example of the JSON:
{
"name":"Test",
"type":"App Id List",
"listItems":
[
{
"id":1,
"name":"Test",
"value":" 1"
},
{
"id":2,
"name":"NEW TEST",
"value":" 2"
}
]
}
Here is my Lists model:
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "lists")
public class Lists implements Serializable, OperationalEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(columnDefinition = "char", nullable = false)
private String guid;
private String name;
private String type;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "listItems", orphanRemoval = true)
#Cascade({org.hibernate.annotations.CascadeType.ALL, })
private Set<ListsItems> listItems;
private Date created;
private Date updated;
}
And here is my ListsItems model:
#Getter
#Setter
#Entity
#Table(name = "lists_items")
#NoArgsConstructor
public class ListsItems implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String value;
#NaturalId
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "lists_id", referencedColumnName = "id")
private Lists listItems;
}
Here is the save function:
#PostMapping(value = "/add")
#PreAuthorize("hasRole('ADMIN')")
public #ResponseBody WebResponse<W> create(#RequestBody W webModel) {
D dbModel = asDbModel(webModel);
dbModel.setGuid(UUID.randomUUID().toString());
return WebResponse.success(createWebModelFromDbModel(getDatabaseEntityRepository().save(dbModel)));
}
Any ideas on what might be causing this error? I've searched a bit but nothing I've tried from any other solutions have worked out.
Thanks in advance!
- Travis W.
The answer was to make the following changes to ListItems:
#JsonIgnore // this import will be from jackson
#NaturalId
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "lists_id", referencedColumnName = "id")
private Lists list;
And the following to Lists:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "list", orphanRemoval = true)
#Cascade({org.hibernate.annotations.CascadeType.ALL, })
private Set<ListsItems> listItems;
I also needed to iterate over the results:
#Override
protected Lists asDbModel(WebLists webModel) {
Lists dbModel = new Lists();
dbModel.setId(webModel.getId());
dbModel.setName(webModel.getName());
dbModel.setType(webModel.getType());
dbModel.setListItems(webModel.getListItems());
for(ListsItems item : webModel.getListItems()) {
item.setList(dbModel);
}
return dbModel;
}
I have two classes, Role and Permission with a ManyToMany relationship between them. My problem is that each relationship has some extra data that comes with it therefore I believe I need to create an intermediary class to store these extra data, so that is the RolePermission class.
This is basically what I have, the parameter and domain are the extra data that are required for each relationship.
Here is the code I have right now for my classes.
Role.java
#Entity
#Table(name = "Sec_Role")
#DynamicUpdate
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String description;
private String name;
// This is another relationship which is working just fine (because there are no intermediary data needed.
#ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER)
private Set<Group> groups;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "role")
private List<RolePermission> permissions = new ArrayList<RolePermission>(0);
...Standard getters and setters and constructor
Permission.java
#Entity
#Table(name = "Sec_Permission")
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "permission")
private List<RolePermission> roles = new ArrayList<RolePermission>(0);
...Standard getters and setters and constructor
RolePermission.java
#Entity
#Table(name = "Sec_Role_Permission")
#DynamicUpdate
public class RolePermission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int domain;
private String parameter;
#Column(updatable = false, insertable = false)
private int role_id;
#Column(updatable = false, insertable = false)
private int permission_id;
#ManyToOne(fetch = FetchType.LAZY )
#JoinColumn(name = "role_id", nullable = false)
private Role role;
#ManyToOne(fetch = FetchType.LAZY )
#JoinColumn(name = "permission_id", nullable = false)
private Permission permission;
...Standard getters and setters and constructor
Every class has a standard Repository like so
#RepositoryRestResource(path = "roles")
public interface RoleRepository extends CrudRepository<Role, Integer> {
}
Now this code works fine for reading data and relationships, my problem is that I cannot figure out what I am suppose to do or what end point to call to add/modify/delete a relationship between Role and Permission.
Currently if I call /roles/11/permissions I will get this back:
{
"_embedded": {
"rolePermissions": []
},
"_links": {
"self": {
"href": "http://localhost:8887/api/v1/roles/11/permissions"
}
}
}
How can I add a permission to this role?
I tried executing a POST request to /roles/11/permissions with the following JSON body and I got a 204 No Content response. This basically means success but then when I do a GET request to /roles/11/permissions I do not see permission with ID 1 there so it did not work.
{
"domain": 0,
"parameter": "Some param",
"role_id": 11,
"permission_id": 1
}
Since your mapping is itself an Entity you can model your API based on the Resource ( in this case RolePermission). Basically when you would want to provide an API to add rolepermission
Some thing like
http://localhost:8887/api/v1/rolepermission
POST
{
"roleid":"xxxxx"
"permissionid":"xxxxxx"
"parameterid":"xxxxxx"
"domain":"xxxxx"
}
I have the following Situation:
OrganisationEntity.java
#Entity
#Table(name = "organisation")
public class OrganisationEntity {
// ...
private PersonEntity contactPerson;
// ...
#OneToOne
#JoinColumn(name = "contact_person_id", referencedColumnName = "id", nullable = false)
public PersonEntity getContactPerson() {
return contactPerson;
}
public void setContactPerson(PersonEntity contactPerson) {
this.contactPerson = contactPerson;
}
// ...
}
ContactPerson.java
#Entity
#Table(name = "person")
public class PersonEntity {
private int id;
// ...
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// ...
}
On the database the table Organisation has a non-nullable foreign key to Person. The entity mapping is uni-directional New when I want the persist a new pair of records (one organisation and one person) with merge on the OrganisationEntity I get the following error:
17:10:19.827 WARN [http-nio-8080-exec-2]
[org.hibernate.action.internal.UnresolvedEntityInsertActions] [144]
HHH000437: Attempting to save one or more entities that have a
non-nullable association with an unsaved transient entity. The unsaved
transient entity must be saved in an operation prior to saving these
dependent entities.
Unsaved transient entity:
([ch.freiwilligenarbeit_sempach.entity.PersonEntity#0])
Dependent entities:
([[ch.freiwilligenarbeit_sempach.entity.OrganisationEntity#]])
Non-nullable association(s):
([ch.freiwilligenarbeit_sempach.entity.OrganisationEntity.contactPerson])
This makes perfect sense to me, since it tries to insert the organisation with no reference to the person whatsoever. So I would usually define a cascade behaviour, so that hibernate inserts the person first, sets the reference ond the organisation and then persists the organisation. I tried the following on the organisation entity:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "contact_person_id", referencedColumnName = "id", nullable = false)
public PersonEntity getContactPerson() {
return contactPerson;
}
and
#OneToOne
#JoinColumn(name = "contact_person_id", referencedColumnName = "id", nullable = false)
#Cascade(org.hibernate.annotations.CascadeType.ALL)
public PersonEntity getContactPerson() {
return contactPerson;
}
But neither of the seem to work. I still get the same error. But I think this should actually work.
Any help is highly appreciated! Thanks in advance.
I don't understand completely, what do you want, but I suppose that you want to save a person entity with a person organization.
If you use different ids, you should add the #OneToOne annotation with attribute mappedBy=contactPerson
In the PersonEntity class
#OneToOne(cascade = CascadeType.ALL, mappedBy = "contactPerson")
private OrganisationEntity orgEntity;
In the OrganisationEntity class
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "contact_person_id", nullable = false)
private PersonEntity contactPerson; // name which is pointed as mappedBy attribute
Then you can save this correct writing something like this
orgEntity.setContactPerson(contactPerson);
orgRepo.save(orgEntity);
p.s. I wrote using field injection, but it's not necessary.
This did the trick.
#Cascade(org.hibernate.annotations.CascadeType.ALL)
Although I believe I stopped tomcat and cleaned redeployed the webapp i did not work yesterday. Today it worked as expected, so that the referenced entities (Person) were inserted before the referencing entity (Organisation).
I have two entities Employee and Review. I am trying to create a OneToOne relationship Employee <-> Review.
When I update an Employee with a review, the Employee gets updated where the review becomes the corresponding review,
but the Review doesn't get the 'reviewee' column added with the ID of the employee which is what I expect.
What am I doing wrong?
These are my entities:
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
}
public class Review {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String body;
private char completed;
#OneToOne(mappedBy = "review")
private Employee reviewee;
}
This is my employeeController update function:
#GetMapping(path="/update")
public #ResponseBody Employee updateEmployee (#RequestParam Integer id,
#RequestParam(value = "name", required=false) String name,
#RequestParam(value = "email", required=false) String email,
#RequestParam() Integer reviewId) {
Employee n = EmployeeRepository.findOne(id);
if(name == null) {
name = n.getName();
}
if(email == null) {
email = n.getEmail();
}
n.setName(name);
n.setEmail(email);
Review r = ReviewRepository.findOne(reviewId);
n.setReview(r);
EmployeeRepository.save(n);
return n;
}
The request:
curl 'localhost:8080/employees/update?id=2&reviewId=1'
Because the owner of the relationship (the one with #JoinColumn) is Employee, you have to create/update/delete the association by saving the Employee object.
This is what you are doing so far. But Hibernate will only update the owner when you save it. You should in addition do this before returning your entity:
r.setReviewee(n);
Notice that the next time you will retrieve the review, it will correctly have an Employee object.
Beware: I smell a Jackson infinite loop there when serializing.
Employee.review -> Review -> Review.reviewee -> Employee -> Employee.review...
EDIT
To prevent the Jackson infinite loop:
1. Ignore the serialization.
Employee.java
public class Employee {
// ...
// Do not serialize this field
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
// ...
}
2. Serialize as ID.
Employee.java
public class Employee {
// ...
// Serialize as a single value with the field "id"
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
// Serialize as told by #JsonIdentityInfo immediately (if false -> on second and further occurrences)
#JsonIdentityReference(alwaysAsId = true)
// Rename to "review_id" (would be "review" otherwise)
#JsonProperty(value = "review_id")
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
// ...
}
3. Alternative to serialize as ID: read-only reference to the foreign key.
Employee.java
public class Employee {
// ...
// Do not serialize this field
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
// Read-only access to the foreign key
#Column(name = "Review_id", insertable = false, updatable = false)
private Integer reviewId;
// ...
}
It's seems to be a configuration mismatch. Please try the below one.
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
#OneToOne(mappedBy="reviewee",cascade = CascadeType.ALL)
private Review review; }
public class Review {
#Id
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters={#Parameter(name="property",value="reviewee")})
private Integer id;
private String body;
private char completed;
#OneToOne
#PrimaryKeJoinCloumn
private Employee reviewee; }
I hope the above configuration works as you expected.
Please make sure you're calling the save function under Transaction boundary. Otherwise don't forget to call flush() before closing the session.