2016-02-09 4 views
0

Я надеюсь, что кто-то будет достаточно любезен, чтобы дать несколько советов относительно того, как наилучшим образом подойти к следующей проблеме.Полиморфное сопоставление с аннотациями гибернации

Я пытаюсь использовать 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 ... 
} 

Мне нравится этот подход, б/с Я был в состоянии повторно использовать все общие отображения, и я не должен повторяться в любом месте.

Теперь о проблеме. Предположим, что я хочу создать еще один общий компонент, который добавляет что-то к 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 подход не очень удобен больше, б/с становится трудно контролировать, какие конкретные подклассы загружаются во время выполнения. Он по-прежнему работает в принципе, если есть ТОЧНО один подкласс с по пути к классам, но это непросто сделать во время тестирования, где мы можем протестировать оба подкласса: и CommonUserLogon.

Итак, в этот момент я хотел остановиться и убедиться, что я нахожусь на правильном пути, или, может быть, есть лучший способ сделать то, что я описал?

Любые советы были бы весьма полезными.

ответ

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.

Смежные вопросы