简体   繁体   English

具有Hibernate批注的多态映射

[英]Polymorphic mapping with Hibernate annotations

I am hoping that someone will be kind enough to provide some advice as to how to best approach the following problem. 我希望有人能提供一些有关如何最好地解决以下问题的建议。

I am trying to use Hibernate with annotations to create a common library of mapped superclasses, which can be used from client applications by extending the common entities. 我正在尝试使用带有注释的Hibernate创建一个映射的超类的公共库,可以通过扩展公共实体从客户端应用程序使用该库。 The names of database tables used by all client applications are the same, although application-specific columns might be present in addition to the common columns mapped in the common mappings. 所有客户端应用程序使用的数据库表的名称是相同的,尽管除了公共映射中映射的公共列之外,还可能存在特定于应用程序的列。

Here's an example. 这是一个例子。 5 different applications all share common UserLogon code. 5个不同的应用程序都共享通用的UserLogon代码。 So, in the common library the class might look like this: 因此,在公共库中,该类可能如下所示:

@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 ...
}

This approach works very well. 这种方法效果很好。 All I have to do in my client applications is something like: 我在客户端应用程序中要做的就是:

@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 ...
}

I like this approach b/c I was able to re-use all common mapping, and I did not have to repeat myself anywhere. 我喜欢这种方法,因为b / c我可以重用所有通用映射,而不必在任何地方重复。

Now for the problem. 现在解决问题。 Let's say that I want to create another common component, which adds something to CommonUserLogon . 假设我要创建另一个通用组件,该组件向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 ...

}

Now that we have more than one level of subclasses, the DiscriminatorFormula approach is not so convenient anymore, b/c it gets difficult to control which specific subclasses get loaded at runtime. 既然我们有多个级别的子类, DiscriminatorFormula方法就不再那么方便了,因为b / c,很难控制在运行时加载哪些特定的子类。 It still works in principle if there is EXACTLY one subclass with @DiscriminatorValue("666") on the classpath, but that is not easy to do during testing, where we might want to test both, CommonUserLogonWithDigitalSignature and CommonUserLogon subclasses. 如果类路径上确实有一个带有@DiscriminatorValue("666")子类, @DiscriminatorValue("666")原理上讲仍然可以工作,但这在测试期间不容易做到,因为我们可能要同时测试CommonUserLogonWithDigitalSignatureCommonUserLogon子类。

So at this point I wanted to stop and make sure that I am actually on the right track, or maybe there is a better way to do what I described? 因此,在这一点上,我想停下来并确保自己确实处在正确的轨道上,或者也许有更好的方法来完成我所描述的工作?

Any advice would be greatly appreciated. 任何建议将不胜感激。

Decided to continue with the demonstrated paradigm, and it worked out really well. 决定继续演示的范例,并且效果很好。

The only thing I had to do was change the DiscriminatorFormula values from hard-coded numbers to database-configured values. 我唯一要做的就是将DiscriminatorFormula值从硬编码数字更改为数据库配置的值。 The idea is that client applications will have to tell hibernate which common mappings should be used by storing a value in a dedicated single-column table. 这个想法是,客户端应用程序必须通过将值存储在专用的单列表中来告诉休眠状态,应使用哪些公共映射。

This turned out to be a good approach for testing too, b/c tests can update that table before running, thus getting access to any of the available configurations. 事实证明,这也是一种很好的测试方法,b / c测试可以在运行之前更新该表,从而可以访问任何可用配置。

So the hierarchy looks like this now: 因此,层次结构现在看起来像这样:

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

}

All the client applications need to do now is subclass the correct common superclass, and set the corresponding value in DRY_HIBERNATE_USER_MAP_MODE.MAP_MODE column. 现在,所有客户端应用程序需要做的事情是正确的公共超类的子类,并在DRY_HIBERNATE_USER_MAP_MODE.MAP_MODE列中设置相应的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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