3

I am using Spring Data REST in my project and I have a problem with relationships when I edit the main entity. This is a simple version of my model:

@MappedSuperclass
public abstract class AbstractEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    // Getters and setters
}

@Entity
public class Player extends AbstractEntity {

    private String category;

    private String fnuCode;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinTable(name = "player_contact", joinColumns = @JoinColumn(name = "id_player"), inverseJoinColumns = @JoinColumn(name = "id_contact"))
    private List<Contact> contacts;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_person")
    private Person person;

    // Getters and setters
}

@Entity
public class Contact extends AbstractEntity {

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_person")
    private Person person;

    private String relationship;

    // Getters and setters
}

@Entity
public class Person extends AbstractEntity {

    private String name;

    private String lastname;

    private String idCard;

    // Getters and setters
}

Then, I have my PlayerRepository:

@RepositoryRestResource(path = "players", excerptProjection = PlayerList.class)
@CrossOrigin(origins = "http://localhost:8081")
public interface PlayerRepository extends PagingAndSortingRepository<Player, Long> {

}

PlayerDetail is the projection I use when see and edit a player:

@Projection(name = "playerdetail", types = { Player.class })
public interface PlayerDetail {

    @Value("#{target.person.name} #{target.person.lastname}")
    String getFullName();

    @Value("#{target.person.idCard}")
    String getIdCard();

    @Value("#{target.category}")
    String getCategory();

    @Value("#{target.fnuCode}")
    String getFnuCode();

    List<Contact> getContacts();
}

I don't have any repository for contacts since they are created and modified into the player context.

This is a sample json response I get when I want to edit or see the details of a player (the object is generated from playerdetail projection):

{
 "idCard" : "12365478",
 "fnuCode" : "1111",
  "contacts" : [ {
    "person" : {
      "name" : "Roberto",
      "lastname" : "Perez",
      "idCard" : null
    },
    "relationship" : "Father"
  }, {
    "person" : {
      "name" : "Lucia",
      "lastname" : "Perez",
      "idCard" : null
    },
    "relationship" : "Sister"
  } ],
  "fullName" : "Juancito Perez",
  "category" : "U19",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/players/171"
    },
    "jugador" : {
      "href" : "http://localhost:8080/players/171{?projection}",
      "templated" : true
    }
  }
}

The problem occurs when I edit the player and remove the first contact of the list, in this case, "Roberto Perez". As a result, in the database the row of Lucia Perez is removed and its data is merged in Roberto Perez row. Before deleting the first contact, I have the following data in DB:

Table: contact
id  |id_person  |relationship   |
175 |176        |Father         |  --> First contact (Roberto)
178 |179        |Sister         |  --> Second contact (Lucia)

Table: person
id  |id_card    |lastname   |name
176 |null       |Perez      |Roberto
179 |null       |Perez      |Lucia

After deleting first contact (Roberto), I have this:

Table: contact
id  |id_person  |relationship
175 |176        |Sister

Table: person
id  |id_card    |lastname   |name
176 |null       |Perez      |Lucia

Note that the ID for the second contact has changed. It should be 178 instead of 175.

EXPECTED RESULT:

Table: contact
id  |id_person  |relationship
178 |179        |Sister 

Table: person
id  |id_card    |lastname   |name
179 |null       |Perez      |Lucia

The second contact should keep its ID.

I suppose Spring Data REST merges contacts information because there is no univocal way to identify every contact since the id is not exposed in the projection.

I need a way of prevent contacts merging. Is there any other way of managing collections and relationships in Spring Data REST?

  • i see a wrong declaration in Player.You are using @JoinTable which is for mapping 2 table ids in one table. https://coderanch.com/t/217548/databases/JoinColumn-JoinTable – Sarkhan Aug 08 '17 at 20:39
  • I omited AbstractEntity class when I simplified the model for posting it here. I've just corrected it. All of my entities extend from AbstractEntity which has the id attribute. Thank you @Sarkhan! – Florchis VD Aug 08 '17 at 20:55
  • It's a correct behavior for managed objects. In your model Player is an [aggregate root](https://stackoverflow.com/a/1958722) and Contacts are managed by it. You take access to contacts only via Player, so their ids are unnecessary... – Cepr0 Aug 08 '17 at 20:59
  • I wanted to say that their ids don't matter... – Cepr0 Aug 08 '17 at 21:11
  • Hi @Cepr0, thank you for your answer! I got your point but I don't understand how to update child entities (Contacts) without merging the data (caused by the lack of id). In this example, before editing the player and removing the first conctact, the second contact has id = 178 but after the edition, the id changes to 175. This could cause serious problems in other relationships that use the id. – Florchis VD Aug 08 '17 at 21:15
  • Sorry @Sarkhan, I misunderstood your comment. I saw your link but I cannot understand how it is related to my problem. I decided to map the relationship with an intermediate table and it's working fine when saving. – Florchis VD Aug 08 '17 at 21:19
  • 1
    If there are entities related with contacts then you have to change your model, for example to transform Contact to aggregate root and work with contacts independently from Player. – Cepr0 Aug 08 '17 at 21:48
  • @Cepr0 that could be a solution for this case, but I have the same problem with another entity related to Person: Gender. The relationship is ManyToOne and Gender has the attributes id, code and description. The gender is selected from a combo in frontend. When I create a player, I select the gender and it is correctly saved in the DB. When I edit the player and change the gender, the gender object is modified instead of the 'gender id' in person. For example I create a player with gender "Male", then edit the player and change the gender to "Female"; as a result, I have two gender "Female". – Florchis VD Aug 08 '17 at 22:56
  • 1
    It seems that `Gender` doesn't have its own repo and `Player` has 'all-cascaded' association with `Gender`. Just create repo for `Gender` and remove cascade to it. By the way, I'd recommend to transform `Gender` to [enum](https://stackoverflow.com/q/44800475)... – Cepr0 Aug 09 '17 at 05:35

0 Answers0