2015-12-25 3 views
9

Предположим, у меня есть таблица первичных пользователей, которая называется UserProfile, и на ней есть отображаемое имя.Есть ли способ использовать SecondaryTable для перехода нескольких таблиц?

У меня есть различные модули, с которыми вы можете быть участником, с UserProfile и ModuleId на нем, чтобы обозначить ваш ModuleMembership. Затем у вас может быть профиль для каждого другого модуля для хранения данных, относящихся к этому модулю, например, если вы зарегистрированы для PokerModule, вы получите PokerProfile.

Я хочу разместить отображаемое имя от UserProfile по адресу PokerProfile, но я хотел бы сделать это нормализованным образом. Я мог бы сделать это через Hibernate или через SQL, в любом случае работает. Точное соотношение было бы PokerProfile.membership.userProfile.displayName - как я могу получить это в @Column на классе PokerProfile?

+0

Должно быть, мне что-то не хватает, потому что мне кажется, что вы можете просто скопировать displayName из UserProfile в ModuleMembership и слить обновление. –

+0

Как насчет @ManyToOne? Это может быть ассоциация сущностей. – crybird

+0

Возможно, было бы полезно, если бы вы могли поделиться кодом своих сущностей. Хотя из того, что вы описали, я не знаю, почему вы не можете просто добавить метод '@ Transient' в класс' PokerProfile' (например, 'getUserName()'), который делает что-то вроде 'return this.getMembership() . .getUserProfile() GetDisplayName(); '. Предполагая, что вы правильно определили все свои отношения. Нет причин использовать SQL или HQL для чего-то подобного, если только SQL, создаваемый слоем ORM, просто слишком медленный по вашему вкусу. – aroth

ответ

9

Вы можете использовать derived property для извлечения displayName в вашем PokerProfile классе следующим образом:

@Formula(
    "(SELECT up.displayName" 
     + " FROM ModuleMembership mm" 
     + " JOIN UserProfile up" 
     + " ON (mm.userProfileId = up.userProfileId)" 
     + " WHERE mm.moduleMembershipId = membershipId)") 
String displayName; 

Обратите внимание, что полученное свойство использует SQL только, не HQL. membershipId определяется как @JoinColumn имя объекта membership. Другие имена столбцов также определены.

Однако, несмотря на то, что это не то, что вы просили точно, я обычно реализовать свои собственные свойства ярлыка в Java для таких вещей, как следует:

@Transient 
public String getDisplayName() { 
    if (membership == null) { 
     return null; 
    } 
    UserProfile userProfile = membership.getUserProfile(); 
    return userProfile == null ? null : userProfile.getDisplayName(); 
} 

Для меня, контекстные свойства легче читать и напишите, чем соответствующий SQL.

Образцы образцов были протестированы для работы с использованием Hibernate 5.0.6. Final против версии базы данных H2 1.4.190.

+0

Если 'getDisplayName()' предназначено для размещения внутри определения сущности, вы не хотите бросать перед ним '@ Transient', чтобы Hibernate не стал жаловаться, когда видит, что нет поля displayName в таблице поддержки (или, что еще хуже, попробуйте _create_ это поле, если вы включили обновления автоматической схемы)? – aroth

+0

@aroth Возможно, вы правы, но я не уверен, что это необходимо в Hibernate 5. У меня нет предупреждений, когда я запускаю свою программу, нет никакого дополнительного столбца, и я не вижу ничего явного в Hibernate 5 документов. – heenenee

+0

@aroth Я добавил это, потому что я чувствовал, что это было более ясно. Я все еще не знаю, требуется ли это или нет. Это может стоить другого вопроса о стеке. Спасибо за предложение! – heenenee

1

Hibernate Formula s являются изящным решением для этого, однако им может быть трудно получить право, если они сложны (Hibernate имеет ограниченную способность анализировать собственный SQL). Кроме того, даже если вы заставите их работать правильно, производительность может пострадать, потому что формулы могут включать подзапросы, которые всегда вставляются в окончательный SQL.

Подход, который я часто использую, состоит в том, чтобы создать представление базы данных, которое делает необходимые соединения для меня, а затем я сопоставляю свою сущность с представлением. База данных способна оптимизировать представление гораздо лучше, чем вложенные подзапросы, а код Java немного чист, потому что в нем нет встроенного SQL.

Итак, вы можете создать db-представление Membership_Profile и отображаемое на нем объект многократного использования, скажем MembershipProfile.

@Entity 
@Table(name = Membership_Profile) 
class MembershipProfile { 
    Long moduleMembershipId; 
    String displayName; 
    // Other properties if needed 
} 

Затем вы можете связать этот объект с определенными профилями, например:

@Entity 
class PokerProfile { 
    @OneToOne(fetch=FetchType.LAZY) 
    @JoinColumn(name = "membershipId") 
    MembershipProfile membershipProfile; 
    ... 
} 

Преимущество этого подхода заключается в том, что вы загружаете displayName (и любые другие MembershipProfile свойства) лениво, когда вам нужно их (таким образом, избегая затрат на выполнение представления или подзапросов).

Альтернативой созданию представлений базы данных являются Hibernate Subselect s, которые представляют собой вид Hibernate (только для чтения), но я ожидаю, что реальные представления db будут работать намного лучше.

+0

Это могло бы быть очень низким уровнем - Hibernate не знал бы разницы, и я мог бы использовать полную оптимизацию на стороне SQL для любого используемого мной вкуса. Фактически, мне даже не нужно размещать его в профиле членства - он может оставаться в профиле пользователя, как сейчас, иметь профиль в покер без отображаемого имени и иметь представление, которое обертывает эти два. – corsiKa

+0

@corsiKa Правда, вы можете обернуть в поле зрения все, что лучше всего подходит для ваших случаев использования и ваших требований к производительности. Недостатком определения представления на PokerProfile является то, что он всегда будет выполняться при загрузке экземпляров PokerProfile и что вам нужно будет определить новое представление для использования в других случаях использования * * Profile (например, «BlackjackProfile» , и т.д). –

2

Если мы думаем о данной области с точки зрения объектно-ориентированного/реляционного образом, мы можем заключить следующее соотношение:

PokerProfileнаследуетсяUserProfile

Вы можете достичь того же отношения с JPA и СУБД с помощью «One Table Per Subclass Inheritance» и отношения внешних ключей соответственно.

Таким образом, вы можете определить что-то вроде ниже:

UserProfile.java

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.Inheritance; 
import javax.persistence.InheritanceType; 
import javax.persistence.Table; 


@Entity 
@Table(name = "TBL_USER_PROFILE") 
@Inheritance(strategy=InheritanceType.JOINED) 
public class UserProfile { 

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

    @Column(name = "DISPLAY_NAME") 
    private String displayName; 

    public Person() { 

    } 

    public Person(String displayName) { 
     this.displayName = displayName; 
    } 

    // Getter and Setter methods 
} 

PokerProfile.java

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.PrimaryKeyJoinColumn; 
import javax.persistence.Table; 

@Entity 
@Table(name="TBL_POKER_PROFILE") 
@PrimaryKeyJoinColumn(name="ID") 
public class PokerProfile extends UserProfile { 

    // Properties & Getters & Setters here 

} 

Преимущество этой модели заключается в том, что он четко выражает требуемую функциональность и, конечно, четко синхронизирован с родительским профилем. Нет необходимости в обходном пути или настройке. И под капотом, он использует отношения внешнего ключа между PokerProfile & UserProfile и, следовательно, избыточность данных.

+0

Профиль покера никоим образом не наследуется от профиля пользователя. Если бы он наследовал, я бы не смог получить пользовательский файл, который хотел бы играть в покер и блэкджек, не имея другого идентификатора. Мне нравится внешнее мышление, но, к сожалению, я не думаю, что это применимо здесь. – corsiKa

+0

Итак, я начал думать об этом, и я понял, что мне нужно, чтобы PokerProfile наследовал что-то, но не UserProfile. Этот класс существует как глобальное представление реального человека, в то время как их WhateverProfiles представляет их, предпочитая присоединиться к определенному модулю. Не существует общего поведения между PokerProfile и UserProfile, но между PokerProfile и BlackjackProfile существует. Поэтому я сделал AbstractProfile (как @MappedSuperclass) для покера и блэкджека, чтобы унаследовать его, и разместил в нем аннотацию. Опять же, мне нравится думать здесь, это просто не подходит для меня. – corsiKa

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