0

I need advice regarding a many-to-many relationship between KeyStoreEntity and it's 3 child entities (KeyPairEntity, SecretKeyEntity, CertificateEntity). I will use CertificateEntity for this example as other 2 follow the same pattern/issue. My extended class BaseRdbmsEntity is a @MappedSuperclass with id, name and description attributes.

I had no problems with @ManyToMany on both sides and using Map<String, CertificateEntity> on KeystoreEntity side to get certificates by their alias along with Map<String, KeyStoreEntity> on CertificateEntity to get the aliases and associated keystores.

Then I realized I'd blown it as a certificate could easily have the SAME alias in different keystores...thus CertificateEntity map keys can clash and one value will wipe out subsequent value stored therein during a repository query.

I need to reverse the key/value types for the map attribute in CertificateEntity to Map<KeyStoreEntity, String>. It's unclear to me how I do a @MapKeyValue (I realize this doesn't exist) to get alias as value in map...and not as key.

I've attempted to fix the problem by using @JoinTable annotations but am not seeing an equivalent of @MapKeyColumn for value-side of map. What I want is KeyStoreEntity as key and String alias as the value.

@Entity(name = "keystores")
@Table(name = "pki_keystores")
public class KeyStoreEntity extends BaseRdbmsSecurityEntity
{
    @MapKeyColumn(name="alias")
    @ManyToMany
    (
        fetch = FetchType.LAZY,
        cascade = {CascadeType.PERSIST, CascadeType.MERGE}
    )
    private Map<String, CertificateEntity> certificates;
    ...snip...
}

@Entity(name = "certificates")
@Table(name = "pki_certificates", uniqueConstraints = {
    @UniqueConstraint(columnNames = {"issuerDn", "serialNumber"})
})
public class CertificateEntity extends BaseRdbmsSecurityEntity
{
    @MapKeyColumn(name="alias")
    @ManyToMany(
        fetch = FetchType.LAZY,
        cascade = {CascadeType.PERSIST, CascadeType.MERGE},
        mappedBy = "certificates",
        targetEntity = KeyStoreEntity.class
    )
    private Map<String, KeyStoreEntity> keystores;
    // Map type for above needs to be <KeyStoreEntity, String>
    ...snip...
}

I'm expecting a lazy fetch to work with k/v reversed on the CertificateEntity.keystores side of the relationship. My expectation is I only need to tweak/add an annotation to CertificateEntity for keystores attribute.

Mike Klein
  • 11
  • 1
  • 4

1 Answers1

0

Here's an example of uni/bidirectional relationship Many-to-Many. I hope you would easily adapt it to your code) It's just trivial example with List. Check out this question on SO, too

@ManyToMany

unidirectional:

User.java

@Entity
public class User {

    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private long id;

    ...

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private List<Role> roles = new ArrayList<>();

    public void addRoles(Role role) {
        roles.add(role);
    }

    public void removeRoles(Role role) {
        roles.remove(role);
    }
}

Role.java

@Entity
public class Role {

    @Id
    @GeneratedValue
    @Column(name = "role_id")
    private int id;

    @Column(name = "role")
    private String role;
}



bidirectional:

Trader.java:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@ToString(exclude = "stockmarkets")
@Table(name = "trader")
public class Trader {

    @Id
    @GeneratedValue
    @Column(name = "trader_id")
    private Long id;

    @Column(name = "trader_name")
    private String traderName;

    @ManyToMany(fetch = FetchType.LAZY,
            cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    })
    @JoinTable(name = "TRADER_STOCKMARKET",
            joinColumns = { @JoinColumn(name = "trader_id") },
            inverseJoinColumns = { @JoinColumn(name = "stockmarket_id") })
    private List<Stockmarket> stockmarkets = new ArrayList<>();


    /*
    We need to add methods below to make everything work correctly
     */

    public void addStockmarket(Stockmarket stockmarket) {
        stockmarkets.add(stockmarket);
        stockmarket.getTraders().add(this);
    }

    public void removeStockmarket(Stockmarket stockmarket) {
        stockmarkets.remove(stockmarket);
        stockmarket.getTraders().remove(this);
    }

}

Stockmarket.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@ToString(exclude = "traders")
@Table(name = "stockmarket")
public class Stockmarket{

    @Id
    @GeneratedValue
    @Column(name = "stockmarket_id")
    private Long id;

    @Column(name = "stockmarket_name")
    private String stockmarketName;

    @ManyToMany(mappedBy="stockmarkets")
    private List<Trader> traders = new ArrayList<>();

    /*
    We need to add methods below to make everything work correctly
     */

    public void addTrader(Trader trader) {
        traders.add(trader);
        trader.getStockmarkets().add(this);
    }

    public void removeTrader(Trader trader) {
        traders.remove(trader);
        trader.getStockmarkets().remove(this);
    }

}

Community
  • 1
  • 1
Antonio112009
  • 415
  • 2
  • 7
  • 21
  • Hello. Thanks for info but I don't see how this helps me with my case requiring a Map.Entry using an Entity as key and alias as value. There are annotations which exist letting you specify the map key but am unclear on how to get String:alias for the value portion of map.entry tuple. – Mike Klein Sep 07 '19 at 01:46