Создание ленивого Непосредственного отношения

Вот пример использования контроллера, введенного Guice.

/**
 * Loads a FXML file and injects its controller from the given Guice {@code Provider}
 */
public abstract class GuiceFxmlLoader {

   public GuiceFxmlLoader(Stage stage, Provider<?> provider) {
      mStage = Objects.requireNonNull(stage);
      mProvider = Objects.requireNonNull(provider);
   }

   /**
    * @return the FXML file name
    */
   public abstract String getFileName();

   /**
    * Load FXML, set its controller with given {@code Provider}, and add it to {@code Stage}.
    */
   public void loadView() {
      try {
         FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource(getFileName()));
         loader.setControllerFactory(p -> mProvider.get());
         Node view = loader.load();
         setViewInStage(view);
      }
      catch (IOException ex) {
         LOGGER.error("Failed to load FXML: " + getFileName(), ex);
      }
   }

   private void setViewInStage(Node view) {
      BorderPane pane = (BorderPane)mStage.getScene().getRoot();
      pane.setCenter(view);
   }

   private static final Logger LOGGER = Logger.getLogger(GuiceFxmlLoader.class);

   private final Stage mStage;
   private final Provider<?> mProvider;
}

Вот конкретная реализация загрузчика:

public class ConcreteViewLoader extends GuiceFxmlLoader {

   @Inject
   public ConcreteViewLoader(Stage stage, Provider<MyController> provider) {
      super(stage, provider);
   }

   @Override
   public String getFileName() {
      return "my_view.fxml";
   }
}

Обратите внимание, что этот пример загружает представление в центр BoarderPane, который является корнем сцены в рабочей области. Это не относится к примеру (деталь реализации моего конкретного варианта использования), но решил оставить его, поскольку некоторые из них могут оказаться полезными.

203
задан skaffman 18 September 2009 в 12:43
поделиться

5 ответов

Во-первых, некоторые пояснения к ответу KLE :

  1. Неограниченная (допускающая значение NULL) однозначная ассоциация - единственная, которая не может быть проксирована без инструментарий байт-кода. Причина этого в том, что объект-владелец ДОЛЖЕН знать, должно ли свойство ассоциации содержать прокси-объект или NULL, и он не может определить это, просматривая столбцы своей базовой таблицы из-за взаимно однозначного сопоставления через общий PK, поэтому он в любом случае нужно с нетерпением ждать, что делает прокси бессмысленным. Вот более подробное объяснение .

  2. ассоциации многие-к-одному (и, очевидно, один-ко-многим) не страдают от этой проблемы. Сущность-владелец может легко проверить свой собственный FK (а в случае «один ко многим» пустой прокси-сервер коллекции создается изначально и заполняется по запросу), поэтому ассоциация может быть ленивой.

  3. Замена «один к одному» на «один ко многим» - плохая идея. Вы можете заменить его уникальным «многие к одному», но есть и другие (возможно, лучшие) варианты.

Роб Х. имеет допустимую точку, однако вы не сможете реализовать ее в зависимости от вашей модели (например, если ваша однозначная ассоциация имеет значение NULL).

Теперь, что касается исходного вопроса:

A) @ManyToOne (fetch = FetchType.LAZY) должен работать нормально. Вы уверены, что он не перезаписывается в самом запросе? Можно указать объединенную выборку в HQL и / или явно установить режим выборки через API критериев, который будет иметь приоритет над аннотацией класса. Если это не так, и у вас все еще есть проблемы, опубликуйте свои классы, запрос и полученный SQL для более предметного обсуждения.

B) @OneToOne сложнее. Если он определенно не допускает значения NULL, воспользуйтесь предложением Роба Х. и укажите его как таковое:

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

В противном случае, если вы можете изменить свою базу данных (добавить столбец внешнего ключа в таблицу владельцев), сделайте это и сопоставьте его как «объединенный ":

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

и в OtherEntity:

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

Если вы не можете этого сделать (и не можете жить с нетерпеливой выборкой), инструментирование байт-кода - ваш единственный вариант. Однако я должен согласиться с CPerkins - если у вас есть 80 !!! присоединений из-за активных ассоциаций OneToOne, у вас есть более серьезные проблемы, чем это: -)

206
ответ дан 23 November 2019 в 04:57
поделиться

Как я объяснил в эта статья , если Вы не используете Улучшение Байт-кода , Вы не можете выбрать лениво родительскую сторону @OneToOne ассоциация.

Однако чаще всего, Вам даже не нужна ассоциация родительской стороны, если Вы используете @MapsId на стороне клиента:

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Post post;

    public PostDetails() {}

    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }

    //Getters and setters omitted for brevity
}

С @MapsId, id свойство в дочерней таблице служит и Первичным ключом и Внешним ключом к родительской таблице Первичный ключ.

Так, если у Вас есть ссылка на родителя Post объект, можно легко выбрать дочерний объект с помощью родительского идентификатора объекта:

PostDetails details = entityManager.find(
    PostDetails.class,
    post.getId()
);

Таким образом, Вы не будете иметь проблемы запроса N+1 , который мог быть вызван mappedBy @OneToOne ассоциация на родительской стороне.

2
ответ дан 23 November 2019 в 04:57
поделиться

Основная идея XToOnes в Hibernate заключается в том, что они в большинстве случаев не ленивы.

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

Отредактировано: подробности см. в ответе ChssPly76 . Этот менее точный и подробный, ему нечего предложить. Спасибо ChssPly76.

10
ответ дан 23 November 2019 в 04:57
поделиться

In native Hibernate XML mappings, you can accomplish this by declaring a one-to-one mapping with the constrained attribute set to true. I am not sure what the Hibernate/JPA annotation equivalent of that is, and a quick search of the doc provided no answer, but hopefully that gives you a lead to go on.

5
ответ дан 23 November 2019 в 04:57
поделиться

Чтобы заставить ленивую загрузку работать с однозначными сопоставлениями, допускающими значение NULL, вам нужно разрешить hibernate выполнять инструментирование времени компиляции и добавить @LazyToOne(value = LazyToOneOption .NO_PROXY) к отношению один к одному.

Пример сопоставления:

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

Пример расширения файла Ant Build (для инструментирования времени компиляции 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>
19
ответ дан 23 November 2019 в 04:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: