使用Hibernate注释进行多态映射

时间:2016-02-09 05:37:33

标签: hibernate jpa

我希望有人能够就如何最好地解决以下问题提供一些建议。

我正在尝试使用带有注释的Hibernate来创建映射超类的公共库,可以通过扩展公共实体从客户端应用程序中使用它。所有客户端应用程序使用的数据库表的名称都相同,但除了公共映射中映射的公共列之外,可能还存在特定于应用程序的列。

这是一个例子。 5个不同的应用程序都共享通用的UserLogon代码。因此,在公共库中,类可能如下所示:

@Entity
@Table(name="DRY_USER_LOGON")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("666") // A trick - any subclass mapped with @DiscriminatorValue("666") found on the classpath will be used!
public abstract class CommonUserLogon  {
    @Id
    @Column(name = "USER_LOGON_OID")
    Long oid;

    @Column(name = "USER_LOGON_NAME")
    String userLogonName;

    @OneToOne(mappedBy="userLogon")
    CommonUserProfile userProfile;

    ... etc ...
}

@Entity
@Table(name="DRY_USER_PROFILE")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("666") // A trick - any subclass mapped with @DiscriminatorValue("666") found on the classpath will be used!
public abstract class CommonUserProfile {
    @Id
    @Column(name = "USER_PROFILE_OID")
    Long oid;

    // Hibernate will use the subclass that matches the discriminator at runtime, which is great
    @ManyToOne
    @JoinColumn(name = "USER_LOGON_OID")
    CommonUserLogon userLogon;

    ... etc ...
}

这种方法非常有效。我在客户端应用程序中所要做的就是:

@Entity
@DiscriminatorValue("666") // Has to match @DiscriminatorFormula in superclass
public class UserLogon extends CommonUserLogon {

    @OneToMany(mappedBy="userLogon")
    Set <ClientSpecificStuff> clientExtensions;

    .. etc ..

}

@Entity
@DiscriminatorValue("666") // Has to match @DiscriminatorFormula in superclass
public class UserProfile extends CommonUserProfile {

    /**
     * @return Cast to program-specific UserLogon
     */
    public UserLogon getUserLogon() {
        (UserLogon)super.getUserLogon();
    }

    @Column(name = "APP_SPECIFIC_COL1")
    String appSpecificThing;

    ... etc ...
}

我喜欢这种方法b / c我能够重复使用所有常见的映射,而且我不必在任何地方重复。

现在出现问题。让我们说我想创建另一个通用组件,它会为CommonUserLogon添加一些内容。

// ????? - How to map this?
public abstract class CommonUserLogonWithDigitalSignature extends CommonUserLogon {

    @OneToMany(mappedBy="userLogon")
    List<UserKeyPair> userKeyPairs;

    ... etc ...
}

@Entity
@Table(name="DRY_USER_SECURITY_KEY")
public class UserKeyPair {
    @Id
    @Column(name = "USER_SECURITY_KEY_OID")
    Long oid;

    @ManyToOne
    @JoinColumn(name = "USER_LOGON_OID", nullable = false)
    CommonUserLogonWithDigitalSignature userLogon;

    ... etc ...

}

现在我们有多个子类级别,DiscriminatorFormula方法不再那么方便了,b / c很难控制在运行时加载哪些特定的子类。如果类路径上只有一个@DiscriminatorValue("666")的子类,那么原则上仍然有效,但在测试过程中这并不容易,我们可能想要同时测试CommonUserLogonWithDigitalSignatureCommonUserLogon子类。

所以在这一点上我想停下来确保我实际上是在正确的轨道上,或者有更好的方法来做我描述的事情?

非常感谢任何建议。

1 个答案:

答案 0 :(得分:0)

决定继续展示范式,并且效果非常好。

我唯一要做的就是将DiscriminatorFormula值从硬编码数字更改为数据库配置值。我们的想法是客户端应用程序必须通过在专用的单列表中存储值来告诉hibernate应该使用哪些常见映射。

这也是一种很好的测试方法,b / c测试可以在运行之前更新该表,从而可以访问任何可用的配置。

所以层次结构现在看起来像这样:

@Entity
@DiscriminatorValue("USER_LOGON_WITH_SIGNATURES")
public class Client1UserLogon extends CommonUserLogonWithDigitalKeys {

    @OneToMany(mappedBy="userLogon")
    Set <ClientSpecificStuff> clientExtensions1;

}

@Entity
@DiscriminatorValue("USER_LOGON_BASIC")
public class Client2UserLogon extends CommonUserLogon {

    @OneToMany(mappedBy="userLogon")
    Set <ClientSpecificStuff> clientExtensions2;

}

@Entity
public abstract class CommonUserLogonWithDigitalKeys extends CommonUserLogon {

    @OneToMany(mappedBy="userLogon")
    List<UserKeyPair> userKeyPairs = [];

    ... etc ...
}

@Entity
@Table(name="DRY_USER_LOGON")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("(select humm.MAP_MODE from DRY_HIBERNATE_USER_MAP_MODE humm)) 
public abstract class CommonUserLogon  {
    @Id
    @Column(name = "USER_LOGON_OID")
    Long oid;

    @Column(name = "USER_LOGON_NAME")
    String userLogonName;

    @OneToOne(mappedBy="userLogon")
    CommonUserProfile userProfile;

    ... etc ...
}

@Entity
@Table(name="DRY_USER_PROFILE")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("(select humm.MAP_MODE from DRY_HIBERNATE_USER_MAP_MODE humm))
public abstract class CommonUserProfile {
    @Id
    @Column(name = "USER_PROFILE_OID")
    Long oid;

    // Hibernate will use the subclass that matches the discriminator at runtime, which is great
    @ManyToOne
    @JoinColumn(name = "USER_LOGON_OID")
    CommonUserLogon userLogon;

    ... etc ...
}

@Entity
@DiscriminatorValue("USER_LOGON_WITH_SIGNATURES")
public class UserProfileWithDigitalKeys extends CommonUserProfile {

    public UserLogonWithSignature getUserLogon() {
        (UserLogonWithSignature)super.getUserLogon();
    }

    public UserProfileWithDigitalKeys() {
        userLogon = new UserLogonWithSignature();
    }

}

@Entity
@DiscriminatorValue("USER_LOGON_BASIC")
public class UserProfile extends CommonUserProfile {

    public UserLogon getUserLogon() {
        (UserLogon)super.getUserLogon();
    }

    public UserProfile() {
        userLogon = new UserLogon();
    }

}

现在需要做的所有客户端应用程序都是子类正确的公共超类,并在DRY_HIBERNATE_USER_MAP_MODE.MAP_MODE列中设置相应的值。

相关问题