0

I have a Spring-Boot app with

  • spring-boot-starter-data-rest
  • spring-boot-starter-data-jpa
  • h2

I have entities with one-to-one relationship like this:

@Entity
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long key;

    @OneToOne(cascade = ALL)
    @JoinColumn(name = "party")
    private Party party;

    @Column
    private String street;

    ....
}
@Entity
public class Party {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long key;

    @Column
    private String name;

    @OneToOne(cascade = ALL)
    @JoinColumn(name  = "address")
    private Address address;

    ...

}

And repositories for each entity:

@RepositoryRestResource
public interface AddressRepository extends JpaRepository<Address, Long> { }
@RepositoryRestResource
public interface PartyRepository extends JpaRepository<Party, Long> { }

I create an instance of each entity:

curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{ "name": "John Smith" }' \
     http://localhost:8080/parties
curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{ "street": "456 main st" }' \
     http://localhost:8080/addresses

On the address instance, I create a HATEOAS association to party:

curl -X PUT \
     -H 'Content-Type: text/uri-list' \
     -d http://localhost:8080/parties/1 \
     http://localhost:8080/addresses/1/party

When I check the associations of address:

curl -X GET http://localhost:8080/addresses/1/party -i

I see the correct party:

HTTP/1.1 200 
{
  "key" : 1,
  "name" : "John Smith",
  ....
}

But, when I check the associations of address:

curl -X GET http://localhost:8080/parties/1/address -i

It does not exist:

HTTP/1.1 404 

How do I create both associations from a single call using Spring-Data-Rest?

Zack
  • 3,819
  • 3
  • 27
  • 48
  • can you show us java code where you are saving in party – Shailesh Chandra Nov 10 '19 at 06:00
  • It's there above, the save is proxied by JpaRepository and exposed as a POST. – Zack Nov 10 '19 at 06:05
  • It's not there, actually, I want to see where you are calling repository.save, I doubt address is not populated when you are saving the party, ideally when you save party address should also be saved – Shailesh Chandra Nov 10 '19 at 06:14
  • Other than @SpringBootApplication class with a main method, this is the entire application (minus getter/setter). There is no manual calling of repository.save -- this is proxied to REST endpoints automatically by spring-data-rest. – Zack Nov 10 '19 at 07:19
  • 1
    ok, just realized you are using **@RepositoryRestResource**, thought that you are using **@Repository** – Shailesh Chandra Nov 10 '19 at 07:21
  • posted the answer, works for me – Shailesh Chandra Nov 10 '19 at 08:30

2 Answers2

0

you need to execute your curl like below

curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{ "name": "John Smith" , "address":{ "street": "456 main st" }}' \
     http://localhost:8080/parties

There are some minor changes I did in Entity

@Entity
public class Party implements Serializable{


    private static final long serialVersionUID = 1735292011581348691L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long key;

    @Column
    @JsonProperty("name")
    private String name;

    @JsonProperty("address")
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name  = "address")
    private Address address;


}

@Entity
public class Address implements Serializable {
private static final long serialVersionUID = -7468012139122259209L;


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long key;


    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "party")
    private Party party;

    @JsonProperty("street")
    @Column
    private String street;

}

Works fine, when I tested. it's saving in both table

curl to get

curl -X  http://localhost:8080/parties/1
Shailesh Chandra
  • 2,164
  • 2
  • 17
  • 25
  • I don't want to create an association on create. I want to create the entities, then apply an association later on which should apply to FK of both entities. Whether I apply the association to address from party or party from address, I would expect both FK to be updated, but I observed only one update being applied. – Zack Nov 10 '19 at 18:07
  • 1
    well, I think, you can keep it only one side. Just assume if you have to add data using sql, you will end up writing two insert and two updates – Shailesh Chandra Nov 10 '19 at 18:10
  • I found a solution for this, see my answer – Zack Nov 25 '19 at 04:53
  • 1
    great just missed thinking of this, though I was using the same for maintaining last updated entities. – Shailesh Chandra Nov 25 '19 at 05:07
0

I found a solution for this using @PrePersist and @PreUpdate on my Party class:

@Entity
public class Party {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long key;

    @Column
    private String name;

    @OneToOne(cascade = ALL)
    @JoinColumn(name  = "address")
    private Address address;

    @PrePersist
    @PreUpdate
    public void updateAddressAssociation() {
        if (address != null)
            address.setParty(this);
    }

}

Here is my app flow:

  1. Create the address:
curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{ "street": "456 main st" }' \
     http://localhost:8080/addresses
  1. Create the party
curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{ "name": "John Smith" }' \
     http://localhost:8080/parties
  1. Associate address with party
curl -X PUT \
     -H 'Content-Type: text/uri-list' \
     -d http://localhost:8080/parties/1 \
     http://localhost:8080/addresses/1/party
  1. Confirm party has address association
curl http://localhost:8080/parties/1/address
  1. Confirm address has party association
curl http://localhost:8080/addresses/1/party
Zack
  • 3,819
  • 3
  • 27
  • 48