Home > Software design >  Java: Repeated column in mapping for collection
Java: Repeated column in mapping for collection

Time:10-28

I am trying to create a OneToMany mapping with a LinkedHashMap for my main entity Bundle, that contains the entities VirtualCurrency and Price, but I am getting the following error:

Repeated column in mapping for collection: com.test.model.Bundle.pricing column: bundle_name

I could be wrong, but I believe that it has something to do with the @JoinColumns or @MapKeyJoinColumn annotation, as I have not done anything like this before, so I am quite sure that I am doing this part incorrectly.

My goal is that I should be able to provide the three fields:

bundle_name physical_currency and coin_id e.g. the VirtualCurrency/VirtualCurrencyId

in order to get the amount and discount_amount e.g. Price.

Also, if there is a better way to structure things, then I am all ears, as I personally do not really like how I have set up my tables tbh (would be nice if I could just have the bundle and bundle_pricing tables, where the bundle_pricing could just have all five fields (key and value) from the pricing map).

Here is my main entity

@Setter
@Getter
@Entity
@Table(name = "bundle")
public class Bundle implements Serializable {

  @Id
  @Column(nullable = false)
  private String name;

  @OneToMany(cascade = CascadeType.PERSIST)
  @JoinTable(
          name = "bundle_pricing",
          joinColumns = @JoinColumn(name = "bundle_name", referencedColumnName = "name"))
  @MapKeyJoinColumns({
          @MapKeyJoinColumn(name = "bundle_name"),
          @MapKeyJoinColumn(name = "physical_currency"),
          @MapKeyJoinColumn(name = "coin_id")
  })
  private Map<VirtualCurrency, Price> pricing = new LinkedHashMap<>();

  ...
}

The Key to the map

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "virtual_currency")
public class VirtualCurrency implements Serializable {

  @EmbeddedId private VirtualCurrencyId virtualCurrencyId;
}

The key's PK/Composite Key

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class VirtualCurrencyId implements Serializable {

  @Column(name = "bundle_name")
  private String bundleName;

  @Column(name = "physical_currency")
  private SomeEnum physicalCurrency;

  @Column(name = "coin_id")
  private String coinId;
}

The value for the map

@Setter
@Getter
@NoArgsConstructor
@Embeddable
@Entity
@Table(name = "price")
public class Price implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private long id;

  @Column(name = "amount")
  private BigDecimal amount;

  @Column(name = "discount_amount")
  private BigDecimal discountAmount;
}

DB tables

CREATE TABLE bundle
(
    name                    VARCHAR(100) NOT NULL PRIMARY KEY
    ...
);

CREATE TABLE virtual_currency
(
    bundle_name         VARCHAR(100) NOT NULL,
    physical_currency   TEXT         NOT NULL,
    coin_id             VARCHAR(50)  NOT NULL,

    FOREIGN KEY (bundle_name) REFERENCES bundle (name) ON DELETE CASCADE,
    PRIMARY KEY (bundle_name, physical_currency, coin_id)
);

CREATE TABLE price
(
    id                  BIGSERIAL    NOT NULL PRIMARY KEY,
    amount              NUMERIC,
    discount_amount     NUMERIC      DEFAULT 0.00
);

CREATE TABLE bundle_pricing
(
    bundle_name         VARCHAR(100) NOT NULL,
    physical_currency   TEXT         NOT NULL,
    coin_id             VARCHAR(50)  NOT NULL,
    price_id            BIGSERIAL    NOT NULL,

    FOREIGN KEY (bundle_name, physical_currency, coin_id) REFERENCES virtual_currency (bundle_name, physical_currency, coin_id) ON DELETE CASCADE,
    FOREIGN KEY (price_id) REFERENCES price (id) ON DELETE CASCADE,
    PRIMARY KEY (bundle_name, physical_currency, coin_id)
);

CodePudding user response:

To prevent repeated mapping error, you just have to specify which join should update the column like such:

@OneToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "bundle_pricing",
  joinColumns = @JoinColumn(name = "bundle_name",
    referencedColumnName = "name"))
@MapKeyJoinColumns({
  @MapKeyJoinColumn(name = "bundle_name", insertable = false, updatable = false),
  @MapKeyJoinColumn(name = "physical_currency"), @MapKeyJoinColumn(name = "coin_id")})
private Map<VirtualCurrency, Price> pricing = new LinkedHashMap<>();

NOTE the insertable = false, updatable = false for bundle_name in the MapKeyJoinColumn

CodePudding user response:

remove the @MapKeyJoinColumn(name = "bundle_name") from MapKeyJoinColumns, because when we are creating the JoinColumn in JoinTable it will create the column we don't need to mention it again.

@JoinTable(
          name = "bundle_pricing",
          joinColumns = @JoinColumn(name = "bundle_name", referencedColumnName = "name"))
  @MapKeyJoinColumns({
          @MapKeyJoinColumn(name = "physical_currency"),
          @MapKeyJoinColumn(name = "coin_id")
  })
  • Related