Tworzenie relacji OneToOne

W tej aplikacji, którą rozwijamy, zauważyliśmy, że widok był szczególnie powolny. Sprofilowałem widok i zauważyłem, że było jedno zapytanie wykonywane przez hibernate, które zajęło 10 sekund, nawet jeśli w bazie danych były tylko dwa obiekty do pobrania. Wszystkie relacje OneToMany i ManyToMany były leniwe, więc to nie był problem. Podczas sprawdzania rzeczywistego wykonywanego SQL zauważyłem, że w zapytaniu było ponad 80 połączeń.

[[7]}dalej sprawdzając problem, zauważyłem, że problem był spowodowane głęboką hierarchią OneToOne i ManyToOne relacji pomiędzy klasami encji. Więc pomyślałem, że zrobię z nich leniwych, to powinno rozwiązać problem. Ale adnotacja albo @OneToOne(fetch=FetchType.LAZY) albo @ManyToOne(fetch=FetchType.LAZY) nie wydaje się działać. Albo dostaję wyjątek, albo nie są one faktycznie zastępowane przez obiekt proxy, a tym samym są leniwe. Jakieś pomysły, jak to uruchomić? Zauważ, że nie używam persistence.xml do definiowania relacji lub szczegółów konfiguracji, wszystko odbywa się w kodzie java.
Author: skaffman, 2009-09-18

6 answers

Po pierwsze, kilka wyjaśnień doKLE 's odpowiedź:

  1. Unstrained (nullable) Asocjacja jeden do jednego jest jedyną, której nie można proxyować bez oprzyrządowania kodu bajtowego. Powodem tego jest to, że podmiot właściciel musi wiedzieć, czy właściwość asocjacyjna powinna zawierać obiekt proxy czy NULL i nie może tego stwierdzić, patrząc na kolumny swojej tabeli bazowej z powodu tego, że jeden do jednego jest zwykle mapowany za pomocą współdzielonego PK, więc i tak musi być chętnie pobierana. proxy bez sensu. Oto bardziej szczegółowe Wyjaśnienie.

  2. Stowarzyszenia wielu do jednego (i oczywiście jeden do wielu) nie cierpią z powodu tego problemu. Właściciel może łatwo sprawdzić własne FK (a w przypadku jednego do wielu, puste proxy kolekcji są tworzone początkowo i wypełniane na żądanie), więc skojarzenie może być leniwe.

  3. Zastąpienie jednego do jednego jednym do wielu nie jest dobrym pomysłem. Można go zastąpić unikalnym multi-To-one, ale są inne (ewentualnie lepsze) opcje.

Rob H. ma ważny punkt, jednak możesz nie być w stanie go zaimplementować w zależności od Twojego modelu (np. jeśli Twoje powiązanie jeden do jednego jest nullable).

Teraz, jeśli chodzi o pierwotne pytanie:

A) @ManyToOne(fetch=FetchType.LAZY) powinno działać dobrze. Jesteś pewien, że to nie jest nadpisane w samym zapytaniu? Można określić join fetch w HQL i / lub jawnie ustawić tryb pobierania za pomocą API kryteriów, który miałby pierwszeństwo nad adnotacją klasy. Jeśli tak nie jest i nadal masz problemy, opublikuj swoje klasy, zapytanie i wynikowy SQL, aby uzyskać więcej konwersacji do punktu.

B) @OneToOne jest trudniejsze. Jeśli to zdecydowanie nie jest nullable, skorzystaj z sugestii Roba H. i określ ją jako taką: {]}

@OneToOne(optional = false, fetch = FetchType.LAZY)

W przeciwnym razie, jeśli możesz zmienić swoją bazę danych (dodać kolumnę klucza obcego do tabeli właściciela), zrób to i Mapuj ją jako "joined":

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="other_entity_fk")
public OtherEntity getOther()

I in.:

@OneToOne(mappedBy = "other")
public OwnerEntity getOwner()

If you can ' t do that (i nie można żyć z eager fetching) oprzyrządowanie kodu bajtowego jest jedyną opcją. Muszę się jednak zgodzić z CPerkins - Jeśli masz 80!!! dołącza ze względu na chętne skojarzenia OneToOne, masz większe problemy niż to: -)

 186
Author: ChssPly76,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-22 17:21:04

Aby uzyskać leniwe Ładowanie pracujące na nullable mappingach jeden do jednego, musisz pozwolić hibernate wykonaćkompilację oprzyrządowania czasowego i dodać @LazyToOne(value = LazyToOneOption.NO_PROXY) do relacji jeden do jednego.

Przykładowe Odwzorowanie:

@OneToOne(fetch = FetchType.LAZY)  
@JoinColumn(name="other_entity_fk")
@LazyToOne(value = LazyToOneOption.NO_PROXY)
public OtherEntity getOther()

Przykładowe rozszerzenie pliku Ant Build (do wykonywania oprzyrządowania czasu kompilacji Hibernate):

<property name="src" value="/your/src/directory"/><!-- path of the source files --> 
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries --> 
<property name="destination" value="/your/build/directory"/><!-- path of your build directory --> 

<fileset id="applibs" dir="${libs}"> 
  <include name="hibernate3.jar" /> 
  <!-- include any other libraries you'll need here --> 
</fileset> 

<target name="compile"> 
  <javac srcdir="${src}" destdir="${destination}" debug="yes"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </javac> 
</target> 

<target name="instrument" depends="compile"> 
  <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </taskdef> 

  <instrument verbose="true"> 
    <fileset dir="${destination}"> 
      <!-- substitute the package where you keep your domain objs --> 
      <include name="/com/mycompany/domainobjects/*.class"/> 
    </fileset> 
  </instrument> 
</target>
 17
Author: Kdeveloper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-28 14:36:10

Podstawową ideą xtonów w Hibernate jest to, że w większości przypadków nie są leniwi.

Jednym z powodów jest to, że gdy Hibernate musi zdecydować się na umieszczenie proxy (z id) lub null,
aby dołączyć, musi zajrzeć do drugiej tabeli. Koszt dostępu do drugiej tabeli w bazie danych jest znaczny, więc równie dobrze może ona pobrać dane dla tej tabeli w tym momencie (zachowanie nie leniwe), zamiast pobierać je w późniejszym żądaniu, które wymagałoby drugiego dostępu do tego samego stołu.

Edytowane: szczegółowe informacje można znaleźć w odpowiedzi ChssPly76 . Ten jest mniej dokładny i szczegółowy, nie ma nic do zaoferowania. Dzięki ChssPly76.

 10
Author: KLE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-20 05:53:55

Oto coś, co dla mnie działa (bez oprzyrządowania):

Zamiast używać @OneToOne po obu stronach, używam @OneToMany w odwrotnej części relacji(tej z mappedBy). To sprawia, że właściwość jest kolekcją (List W poniższym przykładzie), ale tłumaczę ją na element w getterze, czyniąc ją przejrzystą dla klientów.

Ta konfiguracja działa leniwie, czyli selekcje są wykonywane tylko wtedy, gdy wywołane są getPrevious() lub getNext() - i tylko jeden select dla każdy telefon.

Struktura tabeli:

CREATE TABLE `TB_ISSUE` (
    `ID`            INT(9) NOT NULL AUTO_INCREMENT,
    `NAME`          VARCHAR(255) NULL,
    `PREVIOUS`      DECIMAL(9,2) NULL
    CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
                 FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);

Klasa:

@Entity
@Table(name = "TB_ISSUE") 
public class Issue {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @Column
    private String name;

    @OneToOne(fetch=FetchType.LAZY)  // one to one, as expected
    @JoinColumn(name="previous")
    private Issue previous;

    // use @OneToMany instead of @OneToOne to "fake" the lazy loading
    @OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
    // notice the type isnt Issue, but a collection (that will have 0 or 1 items)
    private List<Issue> next;

    public Integer getId() { return id; }
    public String getName() { return name; }

    public Issue getPrevious() { return previous; }
    // in the getter, transform the collection into an Issue for the clients
    public Issue getNext() { return next.isEmpty() ? null : next.get(0); }

}
 7
Author: acdcjunior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-09 22:48:09

W natywnych mapowaniach Hibernate XML można to osiągnąć, deklarując mapowanie jeden do jednego z atrybutem ograniczonym ustawionym na true. Nie jestem pewien, co to jest odpowiednik adnotacji Hibernate/JPA, A Szybkie wyszukiwanie doc nie dostarczyło odpowiedzi, ale mam nadzieję, że to da ci trop do kontynuowania.

 5
Author: Rob H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-18 13:42:32

Jak już doskonale wyjaśnił ChssPly76, proxy Hibernate nie pomagają w nieskrępowanych (nullable) asocjacjach jeden do jednego, ale jest pewna sztuczka tutaj, aby uniknąć konfigurowania oprzyrządowania. Chodzi o to, aby oszukać Hibernate, że Klasa encji, której chcemy użyć, została już instrumentowana: instrumentujesz ją ręcznie w kodzie źródłowym. To proste! Zaimplementowałem go z CGLib jako dostawca kodu bajtowego i działa (upewnij się, że skonfigurujesz Lazy= "no-proxy" i fetch="select", a nie "join", w HBM).

Myślę, że jest to dobra alternatywa dla prawdziwej (mam na myśli automatyczną) instrumentacji, gdy masz tylko jeden do jednego zerową relację, którą chcesz zrobić leniwym. Główną wadą jest to, że rozwiązanie zależy od dostawcy kodu bajtowego, którego używasz, więc skomentuj swoją klasę dokładnie, ponieważ możesz w przyszłości zmienić dostawcę kodu bajtowego; oczywiście modyfikujesz również model bean z przyczyn technicznych i jest to Nie w porządku.

 3
Author: Pino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-14 13:35:58