简体   繁体   中英

How do I reverse key value map types on one side of @ManyToMany JPA relationship

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.

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);
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM