0

I have a REST API that will receive some customer data on the following format:

{
    "customer_Id": 50,
    "name": "name",
    "company_name": "company_name",
    "email": "email@provider.com",
    "business_phone": "(00) 1111-2222",
    "mobile_phone": "(00) 1111-2222",
    "document": "123456789",
    "state_registration_number": "ISENTO",
    "state_registration_type": "NO_CONTRIBUTOR",
    "city_registration_number": "ISENTO",
    "classification": "AUTO",
    "address": {
        "street": "STREET NAME XXX",
        "number": "NUMBER XX",
        "complement": "COMPLEMENT",
        "zip_code": "ZIP_CODE",
        "neighborhood": "NEIGHBORHOOD",
        "city": "CITY",
        "state": "STATE"
    }
}

I'd like to save this data on two tables: One table should contains the "main" customer data, and the other one should contais the customer's "address" data.

So, I defined the Customer entity as below:

@Data
@Entity(name = "X_CUSTOMERS")
public class Customer {

    @Id
    private int customer_Id;

    @NotNull
    private String name;

    private String company_name;

    private String email;

    private String business_phone;

    private String mobile_phone;

    @NotNull
    private String document;

    private String state_registration_number;

    private String state_registration_type;

    private String city_registration_number;

    @NotNull
    private String classification;

    @OneToOne(cascade = CascadeType.ALL)
    private Address address;

}

And the Address entity as

@Data
@Entity(name = "X_ADDRESS")
public class Address {

    @NotNull
    private String street;

    private String number;

    private String complement;

    private String zip_code;

    private String neighborhood;

    private String city;

    private String state;

}

But, I couldn't realize how to create a relationship between them. Should I create a customer_id attribute on the Address entity? Should I define some additional Tags on Customer's address attribute? Note that I don't have a customer on the JSON data that is posted by the REST Client and, if a Customer is Update ou Deleted, the Address data should be Updated / Deleted also.

Sorry if this is a such trivial question. I'm learning the basics of JPA/Hibernate these days and your answer will guides me to the right direction to avoid things such 'reinventing the wheel'.

Thanks a lot!

regisxp
  • 956
  • 2
  • 10
  • 31

2 Answers2

3

If we consider Address to be a Value Object rather than entity then it can be mapped as below. In your case, it probably is correct to model it as a VO: if you were building a database of addresses then it could be considered an entity. See further here:

Value vs Entity objects (Domain Driven Design)

We can then make the address class an @Embeddable rather than an entity: it will not then have any identity of its own. To have the customer and address details stored in separate tables we can also use JPAs @SecondaryTable funtionality:

https://docs.oracle.com/javaee/7/api/javax/persistence/SecondaryTable.html

We have then the model classes as below. With these mappings your JSON updates will work as expected.

Customer:

@Data
@Table(name = "customers")
@SecondaryTable(name = "customer_addresses",  pkJoinColumns={
        @PrimaryKeyJoinColumn(name="customer_id", 
            referencedColumnName="customer_id")})
public class Customer {

    protected static final String ADDRESS_TABLE_NAME = "customer_addresses";

    // other fields

    @Embedded
    private Address address;

}

Address:

@Data
@Embeddable
public class Address {

    @NotNull
    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String street;

    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String number;

    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String complement;

    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String zip_code;

    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String neighborhood;

    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String city;

    @Column(table = Customer.ADDRESS_TABLE_NAME)
    private String state;
}
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
0

This is how i do it :

@OneToOne (fetch=FetchType.EAGER, cascade = CascadeType.ALL, optional = false)
@NotNull(message = "L'addresse du domicile est requise!", groups = Seventh.class)
@Getter
@Setter
private Address homeAddress;

No need for any inverse mapping and this lets me save a customer and his address in one fell swoop!

You need an ID for your address entity as well, something like :

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
@Getter
@Setter
private Long id;
Martin
  • 1,977
  • 5
  • 30
  • 67
  • Changing the Address mapping attributes didn't work. JPA fails to create the tables with the following error: `No identifier specified for entity: br.com.customers.entity.Address`. If I add the `customer_id` attribute on `Address` entity, annotated with the tag `@Id`, the tables are created but `Address.customer_id` is always set to 0 instead of the right value that is passed to the controller... – regisxp Nov 07 '18 at 16:27
  • Martin, this way we will always get a new ID to each address. This isn’t what I need. The primary key on Address table should also be customer_id ... – regisxp Nov 07 '18 at 16:36
  • Why exactly do you have this requirement? – Martin Nov 07 '18 at 16:38
  • I need to populate the Addresses data on a specific table instead of have a unique table with a huge amount of columns... – regisxp Nov 07 '18 at 16:41
  • Having a different id than your customer_id will still achieve this result. I do not believe your address can have the same ID as your customer, nor do i believe there are any reasons to expect that to be honest. Doing it how i suggested does split the customer / address data into 2 tables and allows you to access the address data directly from a customer entity. An entity NEEDS an id for hibernate to even create the table but in this case the address_id really doesn't matter since every customer entity will have a reference to it and will be able to access all of its members. – Martin Nov 07 '18 at 16:45
  • You will see that I didn’t have a address_id on the JSON data being POST/PUT. If I update the Customer Data, a new address will always be created instead of update the old one. This is not desired. – regisxp Nov 07 '18 at 16:48
  • I am not familiar with JSON to be honest but there has to be a way for this to work. I use merge() on any updates to my customer entities and it does not create a new row when the address changes. The point of having an auto generated ID is exactly that nobody has to input it and it's generated automatically using the strategy you specify. This is therefore definitely feasible even if the JSON data doesn't contain the address ID. – Martin Nov 07 '18 at 16:52
  • That JSON is simply converted to a POJO. But, since I don’t have the addressId on the Address object, when JPA persists the Address it creates a new record because addressId is zero. I know that I can load then customer, get its addressId and then fill the new object before submit it to JPA, but this seems to be a little hacky to me. – regisxp Nov 07 '18 at 16:59
  • I understand the nature of your challenge since JSON gives you all the data in one batch. I always have a reference to the customer object when editing the address so I can just do setAddress() when the user is done building the address. It does create a new address row doing it that way but you can use orphanRemoval=true on the OneToOne annotation for Hibernate to cleanup after itself. – Martin Nov 07 '18 at 17:04