I have the following three entities
#Entity
class Session {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval=true)
#JoinColumn(name="session_id", referencedColumnName="id")
private List<Testcase> testcases;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval=true)
#JoinColumn(name="session_id", referencedColumnName="id")
private List<SessionChart> sessionCharts;
}
#Entity
class SessionChart {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval=true)
#JoinColumn(name="sessionChart_id", referencedColumnName="id")
private List<ChartMeasurement> chartMeasurement;
}
#Entity
class ChartMeasurement {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne
#JoinColumn(name = "testcase_id", nullable=true, referencedColumnName = "id")
private Testcase testcase;
}
and its corresponding getters and setters. Creating a new session and persisting it with out any session charts and chart measurements works great. But I have a save as functionality when I can open a previous saved session from a different user and rename it (change the owner as me) and resave it thus creating a new database record. I do this by setting id 0 to the entity and all its sub entities, that is Testcases list sessionCharts list and all chartMeasurement entitites inside sessionCharts list. But when trying to persist I get the following error
object references an unsaved transient instance - save the transient instance before flushing : ChartMeasurement.testcase -> Testcase
How can I overcome this behavior?
I'm going to assume you are using JPA and thus are using an javax.persistence.EntityManager instance.
I would suggest detaching the object before changing the ID's and performing a persist.
For example,
entityManager.detach(instanceOfYourObjectHere);
What's happening is that the Session is holding a reference to your object instance and is confused as you have changed the primary keys.
Related
I will be much apprecaite for you to explain my questions.
I have 2 entity and 1 bridge table entity,
Let's say they are Team, User and TeamUser.
TeamEntity:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#JsonManagedReference
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<TeamUsers> team_users = new HashSet<>();
UserEntity:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonBackReference
#OneToMany(mappedBy = "user")
private Set<TeamUsers> team_users = new HashSet<>();
TeamUserEntity(bridge table):
#EmbeddedId
private TeamUsersId id;
#ManyToOne
#MapsId("teamId")
#JoinColumn(name = "team_id")
#JsonBackReference
private Team team;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
#JsonManagedReference
private User user;
#Column(name = "active")
private int active;
As you can see I used #JsonManagedReference and #JsonBackReference
to telling the program the direction for the query of the Entity and avoid infinite recrusive.
Now if I run get repo.findAll() on Team CRUDrepository I will get all Team object, and within the content I will get all bridge table data and it also include User details information.
But Let's say sometimes if I want to query the data in oppisite way, I want to get All User information and the object should contain all Team information, looks like the annotation #JsonManagedReference and #JsonBackReference block the result.
In real world development, how should we manage here?
The #JsonManagedReference and #JsonBackReference annotations are used to handle circular references. They can be used to create a JSON structure in a bidirectional way. The #JsonManagedReference annotation is used on a child reference of the target POJO, which means it is a forward reference that includes during the serialization process whereas #JsonBackReference annotation is a backreference that omits during the serialization process, usually used in the corresponding child class.
Hence, The property annotated with #JsonBackReference here, which is the Team in the TeamUsers, won't be serialized. That is why when you try to get all the users having Team inside the TeamUsers, it won't work. Also, if it did, it would violate the purpose of the annotations that they're used for, recursive access mapping.
If you want to fetch data in either way, you should use #JsonIgnoreProperties instead of those two annotations. Change your entity classes as follows and you'll get your desired output.
In Team class, set #JsonIgnoreProperties("team") on the team_users field to ignore mapping team inside this field again to avoid recursive mapping. Change your Team class to:
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnoreProperties("team")
private Set<TeamUsers> team_users = new HashSet<>();
}
Similarly, in User class, set #JsonIgnoreProperties("user") on the team_users field to ignore mapping user inside this field again to avoid recursive mapping. Change your User class to:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
#JsonIgnoreProperties("user")
private Set<TeamUsers> team_users = new HashSet<>();
}
And finally, in TeamUsers class, set #JsonIgnoreProperties("team_users") on both the team and user field to ignore mapping team_users inside these field again to avoid recursive mapping. Change your TeamUsers class to:
public class TeamUsers {
#EmbeddedId
private TeamUserId id;
#ManyToOne
#MapsId("teamId")
#JoinColumn(name = "team_id")
#JsonIgnoreProperties("team_users")
private Team team;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
#JsonIgnoreProperties("team_users")
private User user;
#Column(name = "active")
private int active;
}
Now you can fetch the data in either way without having recursive mapping.
As in the title, when performing the update operation, the previous child loses the reference to the parent.
Parent side
#OneToMany(cascade =CascadeType.ALL)
#JoinColumn(name = "individual_id")
private List<ContactMedium> contactMedium;
Children side
#Entity
#Table(name = "contactMedium")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ContactMedium
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
#ManyToOne
private Individual individual;
Patch operation
public Individual patch(Individual individual, Long id) {
Individual objectToSave = individual;
objectToSave.setId(id);
return individualRepository.save(objectToSave);
}
When updating, the previous property loses references to the child. How can I prevent this?
Your mappings seems wrong. Ideally they should be as below:
#Entity
#Table(name = "contactMedium")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ContactMedium
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
#ManyToOne
#JoinColumn
private Individual individual;
and
#OneToMany(mappedBy = "individual", cascade = CascadeType.ALL)
private List<ContactMedium> contactMedium;
You need to save the ContactMedium and Individual will automatically be saved. Here ContactMedium has the foreign key reference to Individual (and that is what is depicted in your database table screenshot).
Often one use mappedBy as parameter to #OneToMany instead of #JoinColumn to make the relationship two-ways.
Can you please try to change
#OneToMany(cascade =CascadeType.ALL)
#JoinColumn(name = "individual_id")
private List<ContactMedium> contactMedium;
to
#OneToMany(mappedBy = "individual", cascade =CascadeType.ALL)
private List<ContactMedium> contactMedium;
and see if that worked better?
I think you must add the #OneToMany(mappedBy="individual" , cascade =CascadeType.PERSIST) and the #JoinColumn in the #ManyToOne as below:
#OneToMany(mappedBy = "individual", cascade =CascadeType.PERSIST)
private List<ContactMedium> contactMedium;
#ManyToOne
#JoinColumn(name = "individual_id")
private Individual individual;
You should retrieve the entity from the database using the ID first and then update the specific fields and persist the updated entity back.
I have two entities Business which is composed of a list of Departments
#Entity
#Table(name = "Business")
public class Business implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Long id;
#OneToMany(mappedBy = "business",
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Department> departments;
#OneToMany(mappedBy = "business", orphanRemoval = true,
cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private List<Process> processs;
#ManyToMany
private List<Competence> competences;
}
#Entity
#Table(name = "Department")
public class Department implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "father",
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Department> departments;
}
When I try to remove a business instance I get a Mysql Exception
Cannot delete or update a parent row: a foreign key constraint fails (evac_java.Department, CONSTRAINT FK_Department_Business FOREIGN KEY (Business) REFERENCES Business (Id)):HY000 - null
Which means I can't delete the business instance because it has departments associated with it, but a department cannot exists by itself so I want to delete all business's departments when it gets removed. I thought I would achieve this by adding cascade = CascadeType.REMOVE to the #OneToMany annotation in the business entity, but it does not work.
I did a search on the net and I found a lot of questions similar to this one on stackoverflow but they all suggest the same: add cascade = CascadeType.REMOVE or CascadeType.ALL
So I'm wondering if I'm missing somethig.
I'm using Glassfish 4.1 and EclipseLink
I tried with
#OneToMany(mappedBy = "business", orphanRemoval = true)
private List<Department> departments;
on the business entity but it does not work either
Here's the method I'm using to remove entities which is declared in an abstract class
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
JPA can only remove and cascade the remove over entities it knows about, and if you have not been maintaining both sides of this bidirectional relationship, issues like this will arise. If the collection of departments is empty, try an em.refresh() before the remove, forcing JPA to populate all relationships so that they can be correctly removed, though it is better to maintain both sides of the relationship as changes are made to avoid the database hit.
First of all, sorry for my English.
So, I'm working with MS SQL Server with hibernate and i faced with a problem.
I have next mapping of one of the tables in my DB:
#Entity(name = " ... ")
public class Entity extends BaseEntity implements Comparable {
#Id
#Column(name = "...")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "parent_entity_id", insertable = false, updatable = false)
private Integer parentId;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST})
#JoinColumn(name = "parent_entity_id")
private Entity parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE}, orphanRemoval = true)
private Set<Entity> children;
//other fields, setters, getters
}
That means, that my Entity objects can have children, which are also an Entity objects.
So, my problems is that I can't correctly delete parent with all his children. When I try to remove parent, i get an SQL error:
The DELETE statement conflicted with the SAME TABLE REFERENCE
So, any ideas, how to solve this problem?
You have a foreign key defined between parent_entity_id and id. Set it to allow cascading deletes: deleting a parent will delete all it's children, and all their children et cetera.
Be sure you actually want this to happen!
I am working with JPA and use Hibernate as a provider to my SQL Server database.
I need a many-to-many self referencing relation that has an additional column or even more additional columns.
That is my current code. I am getting exceptions by Hibernate:
#Entity
public class Person {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person", fetch = FetchType.EAGER)
private Set<Relation> relations;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "relPerson", fetch = FetchType.EAGER)
private Set<Relation> inverseRelations;
}
#Entity
public class Relation implements Serializable {
#Id
#ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn(name = "PersonID", referencedColumnName = "id")
private Person person;
#Id
#ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn(name = "RelPersonId", referencedColumnName = "id")
private Person relPerson;
}
During runtime i get an exception from hibernate:
org.hibernate.TransientObjectException: object references an unsaved transient instance
Is there any way to implement this a little bit more intelligent and nicely?? Without getting that exception.
Thanks,
ihrigb
If an object not associated with a Hibernate Session, the object will be Transient.
An instance of Relation list may be Transient(Normally, There is no identifier value for that instance) when you save Person.
Here is better way to understand object state.