How do I dynamically generate JPA Id column based on parent?

Viewed 172

I have a JSON file that looks like this...

{
  "accountId": "123",
  "parties":[
    {
      "name": "John Doe"
    }
  ]
}

I can parse this fine using something like

public class Account{
  private String id;
  @JsonProperty("parties")
  private List<Party> parties;
}

public class Party{
  private String name;
}

However, the SQL table PARTY contains a compound key as the ID. This is a compound key of the name and the parent's account id. Is there a way I can do this without separate classes? I tried this...

@JsonBackReference
private Account parent;

public class GetIdConverter implements AttributeConverter<String, String> {
    @Override
    public String convertToDatabaseColumn(String b) {
        if(parent == null){
            logger.error("couldn't find the parent");
        }
        return parent.getId();
    }

    @Override
    public String convertToEntityAttribute(String s) {
        return s;
    }
}

But when I try to insert I get...

IdentifierGenerationException: ids for this class must be manually assigned before calling save()

2 Answers

Unfortunately, there are only two options for defining composite keys in JPA: @IdClass and @EmbeddedId annotations, and in both cases we have to create a separate class. The basic idea behind creating a class for a composite class is to understand that our key contains multiple primary key fields and treat this object as atomic. The idea of a composite key as a class makes it easy to work with an entity key in any structure that implements JPA.

Like option you can unite two classes into single entity:

@Entity
@Table
@IdClass(PartyEntity.PartyId.class)
public class PartyEntity {

    public static class PartyId implements Serializable {
        private String accountId;

        private String name;

        public PartyId() {
        }

        public PartyId(String accountId, String name) {
            this.accountId = accountId;
            this.name = name;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof PartyId)) return false;
            PartyId partyId = (PartyId) o;
            return Objects.equals(accountId, partyId.accountId) &&
                    Objects.equals(name, partyId.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(accountId, name);
        }
    }

    @Id
    private String accountId;

    @Id
    private String name;

    //getters and setters
}

A slight improvement to saver's answer - you could try the following mapping:

public class Account{
  private String id;

  @JsonProperty("parties")
  @JsonManagedReference
  private List<Party> parties;
}

@IdClass(PartyEntity.PartyId.class)
public class Party {

    @JsonBackReference
    @Id
    @ManyToOne
    private Account account;


    @Id
    private String name;
}

See the first example in the 'Examples of Derived Identifiers' section of the spec

Related