• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Bear Bibeault
  • Liutauras Vilda
Sheriffs:
  • Jeanne Boyarsky
  • Junilu Lacar
  • Henry Wong
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Jj Roberts
  • Tim Holloway
  • Piet Souris
Bartenders:
  • Himai Minh
  • Carey Brown
  • salvin francis

Problems about @OneToMany and @ManyToOne

 
Greenhorn
Posts: 6
1
MyEclipse IDE IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello to everyone!

I am working on a small Spring Boot, Hibernate, Angular, MySQL application. I have a problem to create a relationship and to get what I want it.

I have Town which can have many Students. Student(s) can exist without Town and Town can exist without Student(s).

Here are parts of the codes:


Base class


Student entity:



Town entity:


I need first to understand did I do well. A town is the OWNER of a relationship. ONE Town can have MANY Students, MANY Students can have ONE Town. To tell Hibernate who is the owner we use @OneToMany(mappedBy = "townEntity"). And this means in the Student table it will be created foreign key town_id, right? This should be bidirectional. If I understood well I chose it because I want when I fetch Students to see all their Towns, and when I fetch Town(s) to see all Students which is(are) belong to that/those Town(s).

@JoinColumn(name = "town_id") - I just don't see the difference when I have this and don't understand when to use it because I saw codes without it too. I read it about it but...Probably I missed something more from relationships to understand when I have to use it. I am not sure when I need it and what exactly does it when I "get" the same with mappedBy.

Before I ask about JSON annotations let me say about the other problem.
This works, when I POST Student it creates the Student and his Town. When I create another Student with the same Town name it has the id from that Town (id = 1) but when I change some attribute in Student and want to PUT it I get some changes. A new Town is created with the same name and has id = 2, the Student which I updated now has foreign key town_id = 2. Why?

I want to have to Create, Read, and Update a Town. I don't need it deletes. But when I change a Student and if I don't touch a Town information I want to stay the same.


PUT: http://127.0.0.1:8080/students/1
accept           application/json
Content-Type application/json



To avoid infinite JSON loop I use and . But when I want to GET a Student it doesn't show my any data of his Town. I found this can help me but I am not sure is that what I need it

Because when I GET a Student Postman backs me the Student with his Town and the Town has an array of all Students who have FK of that Town.
 
Sheriff
Posts: 22041
113
Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Deyan Perov wrote:A town is the OWNER of a relationship.


Perhaps on a database level. On a JPA level, the one without mappedBy is the owner. By default with @ManyToOne / @OneToMany, that means the MANY side. It's possible to change the owner to Town, but that means that you should remove the (explicit) relationship from the Student side, basically creating a unidirectional relationship. That's a bit more advanced though, so let's skip that step.

One warning: don't just remove mappedBy. If you have both a @ManyToOne and a matching @OneToMany but the latter one does have a mappedBy, then you don't have one relationship. You have two relationships that involve the same entities. It's very error prone, so please don't do it. (The same holds for two @ManyToMany or two @OneToOne; one side always needs mappedBy.)

To tell Hibernate who is the owner we use @OneToMany(mappedBy = "townEntity").


Yes, but by specifying mappedBy you specify that the other side is the owner.

And this means in the Student table it will be created foreign key town_id, right?


Right. This will be done for any relationship in which Student needs a link to Town:
  • @ManyToOne on Student. Town doesn't even need @OneToMany to get the foreign key; it's a unidirectional many-to-one without it.
  • @OneToMany without mappedBy on Town, and no @ManyToOne on Student. This is a unidirectional one-to-many.
  • @OneToOne with mappedBy on Student. Note that the non-owner side needs mappedBy with @OneToOne.


  • @JoinColumn(name = "town_id") - I just don't see the difference when I have this and don't understand when to use it because I saw codes without it too. I read it about it but...Probably I missed something more from relationships to understand when I have to use it. I am not sure when I need it and what exactly does it when I "get" the same with mappedBy.


    By default, the JPA provider tries to guess the column name. It does this by taking the entity name and appending _id. By using @JoinColumn you can make it explicit, or override the default.

    Before I ask about JSON annotations let me say about the other problem.
    This works, when I POST Student it creates the Student and his Town. When I create another Student with the same Town name it has the id from that Town (id = 1) but when I change some attribute in Student and want to PUT it I get some changes. A new Town is created with the same name and has id = 2, the Student which I updated now has foreign key town_id = 2. Why?

    I want to have to Create, Read, and Update a Town. I don't need it deletes. But when I change a Student and if I don't touch a Town information I want to stay the same.


    Any entity without an id will be considered new, and will trigger an insert. If you update a Student, that Student has an id. If its Town does not, the cascade will cause a new one to be created. That's because CascadeType.ALL implies PERSIST. In order to update the Town as well, it needs to have an id before saving the Student.

    One interesting thing about your Town. It looks more like an Address. I understand that multiple Students can live at the same Address. But how would you support moving a Student to a different Address? If you update the existing one, all other Students at the same Address will belong to the same updated Address. It would appear that all of the Students moved instead of just the one. This is something to think about carefully.

    To avoid infinite JSON loop I use and . But when I want to GET a Student it doesn't show my any data of his Town. I found this can help me but I am not sure is that what I need it

    Because when I GET a Student Postman backs me the Student with his Town and the Town has an array of all Students who have FK of that Town.


    I hate directly exposing entities, because you have to work hard often to prevent these loops. It will give you more problems than you want. Instead, I prefer to create a DTO class for the REST interface. These don't have the bidirectional relationship, just the one you want. For creating you'd have to convert the DTO into an entity and save it; for updating you'd need to find the existing entity, update it from the DTO, and save it.
     
    Marshal
    Posts: 26128
    77
    Eclipse IDE Firefox Browser MySQL Database
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I gave you a cow for such a comprehensive answer, Rob. I also gave Deyan a cow for posting such a well-formed comprehensively-answerable question.
     
    Rancher
    Posts: 4740
    50
    • Likes 2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Deyan Perov wrote:
    I have Town which can have many Students. Student(s) can exist without Town and Town can exist without Student(s).



    Rob's covered everything, but I didn't notice whether he spotted this.

    You say a Student doesn't have to have a Town...OK, but your StudentEntity has its TownEntity marked as NotNull.
     
    Rob Spoor
    Sheriff
    Posts: 22041
    113
    Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Paul Clapham wrote:I gave you a cow for such a comprehensive answer, Rob. I also gave Deyan a cow for posting such a well-formed comprehensively-answerable question.


    Thanks

    Dave Tolls wrote:

    Deyan Perov wrote:
    I have Town which can have many Students. Student(s) can exist without Town and Town can exist without Student(s).



    Rob's covered everything, but I didn't notice whether he spotted this.

    You say a Student doesn't have to have a Town...OK, but your StudentEntity has its TownEntity marked as NotNull.


    I missed that, but you're completely right.
     
    Deyan Perov
    Greenhorn
    Posts: 6
    1
    MyEclipse IDE IntelliJ IDE Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Rob Spoor, I want to thank you for all your effort to help! But now when I scratched deeper I am still confused and now I have many more questions...

    On a JPA level, the one without mappedBy is the owner. By default with @ManyToOne / @OneToMany, that means the MANY side.



    I always think the owner is ONE because I thought MANY of something depends on that ONE.



    It's possible to change the owner to Town, but that means that you should remove the (explicit) relationship from the Student side, basically creating a unidirectional relationship.  


    I this case it will be created a new table and I will have 3 tables instead of 2. This can be prevent with @JoinColumn?


    @ManyToOne on Student. Town doesn't even need @OneToMany to get the foreign key; it's a unidirectional many-to-one without it.
    @OneToMany without mappedBy on Town, and no @ManyToOne on Student. This is a unidirectional one-to-many.
    @OneToOne with mappedBy on Student. Note that the non-owner side needs mappedBy with @OneToOne.



    This is new to me... First I have to understand bidirectional and unidirectional. Bidirectional doesn't exist in relational databases, there exist only undirectional, right? Table A has a FK from Table B, but Table B doesn't have a FK from Table A, right? And this is undirectional?
    We need to bidirectional on the level of Java because we need to have a reference for an object in a class. i.g. We have a Student which HAS-A Town, Town HAS-A student. Am I right? The student has to know for the Town, and the Town has to know for the Student.



    But if the Town doesn't have a reference of the Student is that undirectional then?


    By default, the JPA provider tries to guess the column name. It does this by taking the entity name and appending _id. By using @JoinColumn you can make it explicit, or override the default.



    Finally, I clearly understand it .


    Any entity without an id will be considered new, and will trigger an insert. If you update a Student, that Student has an id. If its Town does not, the cascade will cause a new one to be created. That's because CascadeType.ALL implies PERSIST. In order to update the Town as well, it needs to have an id before saving the Student.



    I think I got it.

    One interesting thing about your Town. It looks more like an Address. I understand that multiple Students can live at the same Address. But how would you support moving a Student to a different Address? If you update the existing one, all other Students at the same Address will belong to the same updated Address. It would appear that all of the Students moved instead of just the one. This is something to think about carefully.



    I really appreciate your attention. I am going to think once more for a better solution.


    I hate directly exposing entities, because you have to work hard often to prevent these loops. It will give you more problems than you want. Instead, I prefer to create a DTO class for the REST interface. These don't have the bidirectional relationship, just the one you want. For creating you'd have to convert the DTO into an entity and save it; for updating you'd need to find the existing entity, update it from the DTO, and save it.



    Good tips!


    ------------

    @Paul Clapham
    Thank you!

    -----------

    @Dave Tolls

    You say a Student doesn't have to have a Town...OK, but your StudentEntity has its TownEntity marked as NotNull.


    You are right. I overlook this. I need to reconsider how to change all those mistakes which I made it.
     
    Rob Spoor
    Sheriff
    Posts: 22041
    113
    Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Deyan Perov wrote:Rob Spoor, I want to thank you for all your effort to help! But now when I scratched deeper I am still confused and now I have many more questions...

    On a JPA level, the one without mappedBy is the owner. By default with @ManyToOne / @OneToMany, that means the MANY side.



    I always think the owner is ONE because I thought MANY of something depends on that ONE.


    Maybe from a logical or DB perspective, but from a JPA perspective, the MANY is the owner side of the relationship. Probably because it has the foreign key. If you add a MANY entity to the ONE without setting the ONE on the MANY, the foreign key will not be set. You should also use the same principle for MANY to MANY - always add to the owning side. Adding to the other side is optional for JPA, but good practice for your in-memory model.

    Note that if you forget the mappedBy with @ManyToMany, you get two unidirectional relationships. If you add the entities to each other, that will lead to two INSERT statements. If they use the same @JoinTable that will trigger a unique constraint violation (assuming you've added the correct constraints / pkeys). I've seen this happen myself.

    It's possible to change the owner to Town, but that means that you should remove the (explicit) relationship from the Student side, basically creating a unidirectional relationship.  


    I this case it will be created a new table and I will have 3 tables instead of 2. This can be prevent with @JoinColumn?


    There will never be a third table when using @OneToMany / @ManyToOne / @OneToOne unless you explicitly tell JPA to use one. A unidirectional @OneToMany will still use a join column in the MANY table; the MANY entity just doesn't use it itself. With @JoinColumn you tell JPA explicitly what the column looks like instead of having JPA using its defaults.

    @ManyToOne on Student. Town doesn't even need @OneToMany to get the foreign key; it's a unidirectional many-to-one without it.
    @OneToMany without mappedBy on Town, and no @ManyToOne on Student. This is a unidirectional one-to-many.
    @OneToOne with mappedBy on Student. Note that the non-owner side needs mappedBy with @OneToOne.



    This is new to me... First I have to understand bidirectional and unidirectional. Bidirectional doesn't exist in relational databases, there exist only undirectional, right? Table A has a FK from Table B, but Table B doesn't have a FK from Table A, right? And this is undirectional?
    We need to bidirectional on the level of Java because we need to have a reference for an object in a class. i.g. We have a Student which HAS-A Town, Town HAS-A student. Am I right? The student has to know for the Town, and the Town has to know for the Student.



    But if the Town doesn't have a reference of the Student is that undirectional then?


    Right on both accounts. Relational DBs indeed only know unidirectional. The MANY refers to the ONE.
    In JPA, a relationship is unidirectional if one of the sides has as reference to the other, but not the other way around. A relationship is bidirectional if both entities have references to each other. The latter is often preferred but it's allowed to create unidirectional relationships if you only need to traverse the relationship one way. That could be from MANY to ONE, but also from ONE to MANY. It all depends on your needs. I often see bidirectional relationships out of habit though.
     
    Deyan Perov
    Greenhorn
    Posts: 6
    1
    MyEclipse IDE IntelliJ IDE Java
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    There will never be a third table when using @OneToMany / @ManyToOne / @OneToOne unless you explicitly tell JPA to use one. A unidirectional @OneToMany will still use a join column in the MANY table; the MANY entity just doesn't use it itself. With @JoinColumn you tell JPA explicitly what the column looks like instead of having JPA using its defaults.



    I mean by this: https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ Unidirectional @OneToMany

    I am going to refactor code and to see did I learn enough to finish how I imagine.

    As you can see I am not conformable with JPA, Hibernate, and Relationships. Could you please suggest to me some good documentation where I can read about it? I am going to search over coderanch too, maybe I will find some interesting text which can help me not to be confused.
     
    Rob Spoor
    Sheriff
    Posts: 22041
    113
    Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Deyan Perov wrote:

    There will never be a third table when using @OneToMany / @ManyToOne / @OneToOne unless you explicitly tell JPA to use one. A unidirectional @OneToMany will still use a join column in the MANY table; the MANY entity just doesn't use it itself. With @JoinColumn you tell JPA explicitly what the column looks like instead of having JPA using its defaults.



    I mean by this: https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ Unidirectional @OneToMany


    I guess I was wrong - JPA by default does use a third table, and you have to tell it explicitly to not do this. I'm a bit surprised that Hibernate isn't smart enough to merge the INSERT and UPDATE into one though. I wonder how that will behave if you use @JoinColumn(nullable = false). Perhaps that will tell JPA that it needs to set the column during INSERT.

    There's an alternative to unidirectional @OneToMany: @ElementCollection. In this case, the MANY side isn't an entity of its own, it's completely owned by the ONE side. There is a drawback of course - because the MANY side isn't an entity, it doesn't have a primary key as far as JPA is concerned (it can and should of course have one in the database). That means that updating such records may not work as you expect it to be - it could be implemented as DELETE + INSERT for each record on the MANY side.

    As you can see I am not conformable with JPA, Hibernate, and Relationships. Could you please suggest to me some good documentation where I can read about it? I am going to search over coderanch too, maybe I will find some interesting text which can help me not to be confused.


    It's not free, but I recommend Pro JPA 2. This is one of the books I've read in preparation for my JPA exam.
     
    Deyan Perov
    Greenhorn
    Posts: 6
    1
    MyEclipse IDE IntelliJ IDE Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I believe the book is good but right now I prefer to read some shorter documentation. Because this is important to finish this in next period.
    Those blogs: vladmihalcea, thorben-janssen and baeldung are very good but I think it will be more helpful for me when I become much better at understanding JPA, Hibernate, and Relationships.
    Can you suggest some shorter documentation?

    I want to make small changes in my code. What do you think about those changes?
    I created class TownEntity because in need of this class to add a reference to StudentEntity and CompanyEntity.  
    Maybe from TownEntity to remove address and to put the separate attributes in StudentEntity and CompanyEntity?

    I want to avoid this what you wrote:

    One interesting thing about your Town. It looks more like an Address. I understand that multiple Students can live at the same Address. But how would you support moving a Student to a different Address? If you update the existing one, all other Students at the same Address will belong to the same updated Address. It would appear that all of the Students moved instead of just the one. This is something to think about carefully.



    And TownEntity will have these:






     
    Rob Spoor
    Sheriff
    Posts: 22041
    113
    Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Deyan Perov wrote:I believe the book is good but right now I prefer to read some shorter documentation. Because this is important to finish this in next period.
    Those blogs: vladmihalcea, thorben-janssen and baeldung are very good but I think it will be more helpful for me when I become much better at understanding JPA, Hibernate, and Relationships.
    Can you suggest some shorter documentation?


    I'm afraid not. I learned JPA from "EJB 3 in Action", "Pro JPA 2" and practicing.

    I want to make small changes in my code. What do you think about those changes?
    I created class TownEntity because in need of this class to add a reference to StudentEntity and CompanyEntity.  
    Maybe from TownEntity to remove address and to put the separate attributes in StudentEntity and CompanyEntity?


    Perhaps you can take a look at @Embeddable. In short: instead of making something an entity, you annotate the class with @Embeddable. You make this a property of another class. That will automatically embed the properties of the @Embeddable class into the class you put it in. It essentially gets "flattened" into the entity class.

    There are three main reasons I use this:
  • Compound primary keys
  • Sharing fields between several entities
  • For a group of fields, allowing them to all be required, or all null. The @Embeddable property is nullable, so if I don't set it, all of its database fields will be null. All properties of the @Embeddable class are @NotNull, so if the @Embeddable property is set, then all of its properties must be set
  •  
    Deyan Perov
    Greenhorn
    Posts: 6
    1
    MyEclipse IDE IntelliJ IDE Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Could you please help me to understand one more thing? I am confused about cascading.
    Let's take the example above did I do well how I understood.





    First I need to clarify something.
    Does this part of code mean:

    All these changes when happens to TownEntity will be affected on StudentEntity too? Or vice versa? I am confused because the annotation is above the list of students.

    When I remove PERSIST it still creates the Town or Student, only where I saw affect is when I put CascadeType.REMOVE. If I remove a Student it will remove his Town too. Then I created two students and one town and when I remove the student id = 1 it removed both students and town.



     
    Rob Spoor
    Sheriff
    Posts: 22041
    113
    Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Cascades bubble "down". In your example, any of the listed operations performed on Town will also be performed on all of the Students stored in the list.

    Deyan Perov wrote:When I remove PERSIST it still creates the Town or Student, only where I saw affect is when I put CascadeType.REMOVE. If I remove a Student it will remove his Town too. Then I created two students and one town and when I remove the student id = 1 it removed both students and town.


    Did you persist the Student before persisting the Town? Because otherwise the Student shouldn't have been persisted.

    Be aware with REMOVE. Only use it if your entities are really tied together. If a Student can live without a Town, removing the Town should usually leave the Student intact. If you use REMOVE (or ALL) on every relationship, you can end up removing many records by just removing one. You'd need to first break the relationship (by removing the link between the two) before removing to prevent this.
     
    Deyan Perov
    Greenhorn
    Posts: 6
    1
    MyEclipse IDE IntelliJ IDE Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Cascades bubble "down".


    Sorry, I don't get this.

    In your example, any of the listed operations performed on Town will also be performed on all of the Students stored in the list.


    Does this mean because I wrote this - @OneToMany(mappedBy = "townEntity"...) or because it this annotation is in TownEntity class?


    Did you persist the Student before persisting the Town? Because otherwise the Student shouldn't have been persisted.


    This is how I persist the Student.



    Be aware with REMOVE. Only use it if your entities are really tied together. If a Student can live without a Town, removing the Town should usually leave the Student intact. If you use REMOVE (or ALL) on every relationship, you can end up removing many records by just removing one. You'd need to first break the relationship (by removing the link between the two) before removing to prevent this.


    Thank you for suggestion.

    In my case I should left these:

    But what about the other side, above attribute TownEntity? I don't see any affect do I left CascadeType.ALL or put only any other cascade. I know if I don't have any cascade it will be CascadeType.ALL by default.


    Sorry for a lot of questions. I read a lot and watched videos and now all is mixed up and I am confused when I don't get what I expected
     
    Rob Spoor
    Sheriff
    Posts: 22041
    113
    Eclipse IDE Spring VI Editor Chrome Java Ubuntu Windows
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    Deyan Perov wrote:

    Cascades bubble "down".


    Sorry, I don't get this.

    In your example, any of the listed operations performed on Town will also be performed on all of the Students stored in the list.


    Does this mean because I wrote this - @OneToMany(mappedBy = "townEntity"...) or because it this annotation is in TownEntity class?


    By bubble down I meant, that any cascade that you define on a relationship will be applied to any entity on the other side of that relationship. If that has cascades on its relationships, it will go through those as well, etc.

    Did you persist the Student before persisting the Town? Because otherwise the Student shouldn't have been persisted.


    This is how I persist the Student.


    That's not the persisting code, that's just the source of the Student and Town. But since neither has an id, the Town should only be persisted if either you persist it manually, or if the Student cascades PERSIST to the Town.

    Be aware with REMOVE. Only use it if your entities are really tied together. If a Student can live without a Town, removing the Town should usually leave the Student intact. If you use REMOVE (or ALL) on every relationship, you can end up removing many records by just removing one. You'd need to first break the relationship (by removing the link between the two) before removing to prevent this.


    Thank you for suggestion.

    In my case I should left these:

    But what about the other side, above attribute TownEntity? I don't see any affect do I left CascadeType.ALL or put only any other cascade. I know if I don't have any cascade it will be CascadeType.ALL by default.


    By default there will be no cascading.
     
    I suggest huckleberry pie. But the only thing on the gluten free menu is this tiny ad:
    Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
    https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
    reply
      Bookmark Topic Watch Topic
    • New Topic