Getting parent_id for nested children - mysql

I'm new to JPA and I'm stumped on how to capture the parent id param for nested children. I have two entities, Data and Property. Right now I successfully managed to create a one to many mapping from Data to Property. The thing is that Property can have its own children of itself. The "first layer" properties are able to get and save the data id value, but properties nested in another property are unable to do so. I was wondering if there is a method to do something like this since I could not find any information on it online.
Data (parent class)
#Entity
#Table(name = "data")
public class Data implements Serializable {
// omitted non relevant code
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#OneToMany(targetEntity = EventProperty.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "data_id", referencedColumnName = "id")
private List<EventProperty> properties;
}
Property (child class)
#Entity
#Table(name = "property")
public class Property implements Serializable {
private static final long serialVersionUID = -1100374255977700675L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name = "data_id", referencedColumnName = "id")
private EventData eventData;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private EventProperty parent;
#OneToMany(targetEntity = EventProperty.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private List<EventProperty> children;
}
I'm wondering if storing the properties as a List in Data is the right way to go, or if there's any way to set data_id of a child Property to be the same as the parent Property if it is null, or if there's some other correct way that I am not aware of. Any help would be greatly appreciated!

Related

failed to lazily initialize a collection of role: <Relation Class>could not initialize proxy - no Session

I am getting the exception while updating the Parent entity with a new Child entity,
Here is my Sample Table
public class NavigationNode implements Serializable,Auditable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "NodeID")
private Long id;
#Embedded
private AuditSection auditSection = new AuditSection();
#Column(name = "Code", nullable = false, unique = true)
private String navCode;
#Column(name = "NodeTitle")
private String title;
#OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST})
#JoinTable(name = "node2linkRel", joinColumns = { #JoinColumn(name = "NavigationNodeID") }, inverseJoinColumns = { #JoinColumn(name = "LinkID") })
private Collection<Link> links;
#ElementCollection
#OneToMany(fetch=FetchType.LAZY, orphanRemoval = true, cascade = {CascadeType.PERSIST} )
#JoinTable(name = "node2nodeRel", joinColumns = { #JoinColumn(name = "ParentID") }, inverseJoinColumns = { #JoinColumn(name = "ChildID") })
private Collection<NavigationNode> children;
#Column(name = "Visible")
private Boolean visible;
}
There is another Table called Link
public class Link implements Serializable,Auditable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "LinkID")
private Long id;
#Embedded
private AuditSection auditSection = new AuditSection();
#Column(name = "LinkUrl")
private String url;
#Column(name = "linkName")
private String linkName;
#Column(name = "VisibleInMenu")
private Boolean visibleInMenu;
#ManyToOne(cascade = {CascadeType.PERSIST})
#JoinTable(name = "node2linkRel", joinColumns = { #JoinColumn(name = "LinkID") }, inverseJoinColumns = { #JoinColumn(name = "NavigationNodeID") })
private NavigationNode node;
}
While Saving without the Links I am getting the above error.
failed to lazily initialize a collection of role: Navigation.links
could not initialize proxy
My requirements are
I can created a Navigaiton with optional children and links, Update an Child/parent with optional children and links.
If I delete Navigation Node then the related children should be deleted but I want to keep only the Link and delete the relation between Navigation and Link.
If I try to delete the LInk record, only the relation between Navigation and link should be deleted.
How Can I achieve that and is this the correct Table configuration.
Also While deleting the Link or Node I am getting another exception.
Please suggest.
Thanks
This is because I was trying to Assign a new Object List to the Links in navigation Table after getting the Object from the DataBase.
I was trying to break a Relation between a Link and Navigation.
For achieving this I just remove the Link from the Links list in the Navigaition Table and save, so that the Relation between those entity breaks.
My requirement is not to delete the Link if I try to unlink the relation between Navigation and Link.

Nested one to many relationship jpa spring boot

I have three classes - Document, Page, Sentence. A Document will have multiple Pages & each Page will have multiple Sentences. I'm trying to map One to Many relationship using Spring Data JPA annotation. But it only works when there are only one layer like - Document>Page. Doesn't work while it's Document>Page>Sentence.
Can anyone please give me a solution for how to do it for nested one to many relationship ?
My classes are given below.
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "FILEID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long idFile;
#Lob
#Column(name = "CONTENT")
private byte[] content;
#Column(name = "NAME")
private String name;
#Column(name = "ID_MAIL_USER")
private String idMailUser;
#Column(name = "NUM_PAGES")
private int numPages;
#Column(name = "TO_ANALIZE")
private boolean toAnalize;
#Column(name = "HASH")
private String hash;
#Column(name = "EXTENSION")
private String extension;
#Column(name = "SIZE")
private double size;
#Column(name = "LINK_TO_DRIVE_FILE")
private String linkToDriveFile;
#Column(name="PATH")
private String path;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#Column(name = "PAGES")
private List<Page> pages = new ArrayList<>();
// Setter Getters
}
.
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
#Id
#Column(name = "PAGE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long idPage;
#Column(name = "PAGE_NUMBER")
private int pageNum;
#Lob
#Column(name = "CONTENT")
private String content;
#ManyToMany(cascade = CascadeType.ALL)
#Column(name = "SENTENCES")
private List<Sentence> sentences = new ArrayList<>();
// Setter Getters
}
.
#Entity
#Table(name = "SENTENCE")
public class Sentence implements Serializable {
//private long idFile;
//private long idPage;
#Id
#Column(name = "SENTENCE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "PAGE_NUMBER")
private int pageNumber;
#Column(name = "ORDER")
private int ord;
#Column(name = "CONTENT")
private String content;
#Column(name = "HASH")
private String hash;
// Setter Getters
}
Your OneToMany mappings are incorrect. Correct it as follows
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
......
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "DOCUMENT_ID") //Name the foreign key column in PAGE table to DOCUMENT_ID
private List<Page> pages = new ArrayList<>();
}
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
....
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "PAGE_ID") //Name the forein key column in PAGE table to PAGE_ID
private List<Sentence> sentences = new ArrayList<>();
}
Use #JoinColumn annotation instead of #Column to give the name of the foreign key that do the physical mapping between tables in your database.
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="document")
#Column(name = "PAGES")
private List<Page> pages = new ArrayList<>();
}
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
#ManyToOne
#JoinColumn(name="DOCUMENT_ID")
private Document document;
#ManyToMany(cascade = CascadeType.ALL, mappedBy="pages")
#Column(name = "SENTENCES")
private List<Sentence> sentences = new ArrayList<>();
}
#Entity
#Table(name = "SENTENCE")
public class Sentence implements Serializable {
#ManyToMany(mappedBy="sentences")
private List<Page> pages;
}
Here a Document One to Many relationship with Pages.
So.. we need define mappedBy in the entity we want to map another entity.. so in this case
#OneToMany(mappedBy="document",cascade = CascadeType.ALL, orphanRemoval = true)
and in referenced entity i.e. Pages we want foreign key DOCUMENT_ID, so we have to define it using
#ManyToOne
#JoinColumn(name="DOCUMENT_ID")

Foreign Keys in Spring Boot (MySQL, Hibernate, JPA)

I am trying to write a RESTful API using Spring Boot and I am not able to figure out a way to map my relations in the database. I have a User and a Reports table. Each User can have multiple Reports, and a single report consists of "FROM USER" and "TO USER" columns to indicate who sent the report and to whom. My User ID is the primary key and for the Report table, I am generating REPORT ID as the primary key using AUTO INCREMENT. Here is my User model class -
#Entity
#Table (name = "user")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
private List<Report> reportReceivedList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Report> reportSentList;
/* Getters and setters ..... */
}
Here is my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne()
#JoinColumn(name = "from_user_id")
private Long fromUserId; //THIS SHOULD BE FROM "USER" TABLE
#ManyToOne()
#JoinColumn(referencedColumnName = "to_user_id")
private Long toUserId; //THIS SHOULD BE FROM "USER" TABLE
#Temporal(TemporalType.DATE)
#CreatedDate
private Date createdAt;
private String observation;
private String context;
//Other variables and getters and setters .....
}
Can someone please show me a way to correctly define this relationship. My current model doesn't work. Also, I want rows from REPORT class to be deleted as soon as a user is deleted. Thanks!
I finally fixed it by changing my User class as follows -
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "to_user_id")
private List<Report> reportReceivedList;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "from_user_id")
private List<Report> reportSentList;
And by changing my Report class as -
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;

Jackson JSON infinite recursion without omiting anything from the serialization

I want to serialize and eventually deserialize an object to perform an export/import operation. I use Jackson library because of the extend annotation provided. I do break the infinite recursion by using the latest tags #JsonManagedReference, #JsonBackReference. But the problem here #JsonBackReference does omit the annotated part from the json file so I am not able to set the relationship while importing.
The relationship btwn entities can be shown:
public class A{
#Id
#Column(name = "ID", unique = true, precision = 20)
#SequenceGenerator(name = "a_generator", sequenceName =
"SEQ_A", initialValue = 1, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"a_generator")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "metricDefinition",
fetch = FetchType.LAZY, orphanRemoval = true)
#Fetch(FetchMode.SELECT)
#NotAudited
#JsonManagedReference
private Set<B> bSet= new HashSet<B>();
}
public class B{
#Id
#Column(name = "id", unique = true, precision = 20)
#SequenceGenerator(name = "b_generator", sequenceName = "seq_b", initialValue = 1, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "b_generator")
private Long id;
#ManyToOne(cascade = {CascadeType.REFRESH})
#JoinColumn(name = "a_id")
#Fetch(FetchMode.SELECT)
#JsonBackReference(value = "a-b")
private A a;
#ManyToOne(cascade = {CascadeType.REFRESH})
#JoinColumn(name = "ref_a_id")
#Fetch(FetchMode.SELECT)
#JsonBackReference(value = "a-ref")
private A refA;
#Column(name = "is_optional")
#Fetch(FetchMode.SELECT)
private boolean isOptional;
#Column(name = "name")
#Fetch(FetchMode.SELECT)
private String name;
When I serialize any A object, it does serialize the B's included but the referenced A and refA are omitted. So, when I import A object of course the B's are also imported but I do want to the relationship between the objects to be set.
Is there any idea how can I break the infinite recursion without omitting the one side of the reference?
Thanks in advance
I did also try to use statement below according to answers given similar questions but it did not work so I asked the question above.
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "#id")
it does not sufficient to break the circle. You should also add below annotation to the id property of your class.
#JsonProperty("id")
We can try to break the loop either at the Parent end or at the Child end by following 3 ways
Use #JsonManagedReference and #JsonBackReference
Use #JsonIdentityInfo
Use #JsonIgnore
Use #JsonIdentityInfo
#Entity
#Table(name = "nodes")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Node {
...
}
#Entity
#Table(name = "relations")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Relation {
...
}
Refer more in detail here with the working demo at the end.

JPA many to many relation not inserting into generated table

I have a many-to-many relation in my project and although I'm able to write in my two Entities table, the relational table does not get anything written.
Here's how I'm declaring this using JPA annotations:
Professor.java
#Entity
#Table(name = "Professor")
public class Professor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "idProfessor", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = #JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
inverseJoinColumns = #JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
private List<Aluno> alunoList;
// getters and setters
}
Aluno.java
#Entity
#Table(name = "Aluno")
public class Aluno implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "idAluno", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(mappedBy = "alunoList", fetch = FetchType.EAGER)
private List<Professor> professorList;
// getters and setters
}
And here is the service layer to insert into database:
#Autowired
private AlunoDao alunoDao;
#Autowired
private ProfessorDao professorDao;
#RequestMapping(value = RestUriConstants.SUBMETER, method = RequestMethod.POST)
public #ResponseBody JsonResponse submeter(#RequestBody final Aluno aluno) {
Professor professor = professorDao.find(1);
aluno.setProfessorList(Arrays.asList(professor));
alunoDao.persist(aluno);
...
}
In this case, please consider that I already have an entry with id "1" for Professor.
As I said, it does write on Aluno and Professor table but does NOT write anything into ALUNO_PROFESSOR table.
I've already taken a look at these three kind of similiar questions but none of them could help me:
Hibernate and Spring: value of many-to-many not inserted into generated table
JPA many-to-many persist to join table
How to persist #ManyToMany relation - duplicate entry or detached entity
EDIT - Adding more code snippets
JpaAlunoDao.java
#Repository
public class JpaAlunoDao implements AlunoDao {
#PersistenceContext
private EntityManager em;
#Transactional
public void persist(Aluno aluno) {
em.persist(aluno);
}
}
JpaExercicioDao.java
#Repository
public class JpaExercicioDao implements ExercicioDao {
#PersistenceContext
private EntityManager em;
#Transactional
public void persist(Exercicio exercicio) {
em.persist(exercicio);
}
}
Try this:
public class Professor {
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = #JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
inverseJoinColumns = #JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
private List<Aluno> alunoList;
}
public class Aluno {
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = #JoinColumn(name = "idAluno", referencedColumnName = "idAluno"),
inverseJoinColumns = #JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"))
private List<Professor> professorList;
}
This will ensure that the metadata for the many-to-many relationship is available on both the entities and that operations on either side of the relationship are cascaded to the other side.
I also suggest replacing FetchType.EAGER with FetchType.LAZY for better performance because this has the potential of loading a very large dataset.
I have the same issue. I swapped where the full mapping declare to the class that we will use save() function on.
In your case:
public class Aluno {
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = #JoinColumn(name = "idAluno"),
inverseJoinColumns = #JoinColumn(name = "idProfessor")
private List<Professor> professorList;
}
public class Professor {
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "professorList",)
private List<Aluno> alunoList;
}
and It worked fine.
...
Normally, Hibernate holds the persistable state in memory. The process of synchronizing this state to the underlying DB is called flushing.
When we use the save() method, the data associated with the save operation will not be flushed to the DB unless and until an explicit call to flush() or commit() method is made.
If we use JPA implementations like Hibernate, then that specific implementation will be managing the flush and commit operations.
One thing we have to keep in mind here is that, if we decide to flush the data by ourselves without committing it, then the changes won't be visible to the outside transaction unless a commit call is made in this transaction or the isolation level of the outside transaction is READ_UNCOMMITTED.
...
From Difference Between save() and saveAndFlush() in Spring Data JPA by baeldung:
https://www.baeldung.com/spring-data-jpa-save-saveandflush
employeeRepository.saveAndFlush(new Employee(2L, "Alice"));
or
employeeRepository.save(new Employee(2L, "Alice"));
employeeRepository.flush();
Its not necessary to set many-to-many relationship on both entities.
Just remove session.setFlushMode(FlushMode.MANUAL);
By default HibernateTemplate inside of Spring set FlushMode.MANUAL
this is source code from HibernateTemplate.
if (session == null) {
session = this.sessionFactory.openSession();
session.setFlushMode(FlushMode.MANUAL);
isNew = true;
}
Some times the problem is in the way that you insert the values. I explain with an example.
User user = userFacade.find(1);
Post post = new Post("PRUEBA");
user.addPostCollection(post);
post.addUserCollection(user);
postFacade.create(post);
You have to add the post in postCollection and the user in userCollection. You have two add the correspond entity in the collections of the two entities.
Class USER
public void addPostCollection(Post post) {
if(postCollection == null){
postCollection = new ArrayList<Post>();
}
postCollection.add(post);
}
#ManyToMany(mappedBy = "userCollection")
private Collection<Post> postCollection;
Class Post
public void addUserCollection(User user){
if(userCollection == null){
userCollection = new ArrayList<User>();
}
userCollection.add(user);
}
#JoinTable(name = "USER_POST_R", joinColumns = {
#JoinColumn(name = "POSTID", referencedColumnName = "ID")}, inverseJoinColumns = {
#JoinColumn(name = "USERID", referencedColumnName = "ID")})
#ManyToMany
private Collection<User> userCollection;
Also, it is important to instance the list, for example userCollection = new ArrayList(). If you do not, the value won´t insert.