Почему TypedQuery.getResultList () разрешает каждую связь ManyToOne с отдельным SELECT? - PullRequest
6 голосов
/ 11 ноября 2010

Учитывая следующую простую ассоциацию сущностей: (EntityA) * -1 (EntityB), созданный в базе данных с внешним ключом в EntityA (entityB_id).

Объекты JPA отображают эту связь однонаправленно:

@Entity
EntityA {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable=false,length=250)
    private String name;

    @ManyToOne(optional=false)
    private EntityB entityB;

    ... getter/setter ...
}

@Entity
EntityB {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable=false,length=250)
    private String name;

    ... getter/setter ...
}

Если сделан простой запрос:

EntityManager em = ...;
TypedQuery<EntityA> tq = em.createQuery("from EntityA a", EntityA.class);
tq.getResultList();

В выводе отладки SQL Hibernate я вижу, что запрос EntityB выполняется для каждой строки EntityA:

Hibernate: 
    select
        entitya0_.id as id8_,
        entitya0_.entityB_id as entityB3_8_,
        entitya0_.name as name8_ 
    from
        EntityA entitya0_
Hibernate: 
    select
        entityb0_.id as id4_0_,
        entityb0_.name as name4_0_ 
    from
        EntityB entityb0_ 
    where
        entityb0_.id=?

Даже если стратегией выборки по умолчанию является EAGER (что, похоже, имеет место), EntityB следует извлекать через соединение implizit, не так ли?Что не так?

Но это становится еще более странным - если загружен только один объект EntityA:

EntityA a = em.find(EntityA.class, new Long(1));

Тогда Hibernate, кажется, понимает работу:

Hibernate: 
    select
        entitya0_.id as id1_1_,
        entitya0_.entityB_id as entityB3_1_1_,
        entitya0_.name as name1_1_,
        entityb1_.id as id12_0_,
        entityb1_.name as name12_0_ 
    from
        EntityA entitya0_ 
    inner join
        EntityB entityb1_ 
        on entitya0_.entityB_id=entityb1_.id 
    where
        entitya0_.id=?

Вышеупомянутые тесты были сделаны с Hibernate 3.5 и JPA 2.0.

Ответы [ 3 ]

5 голосов
/ 11 ноября 2010

Даже если стратегией выборки по умолчанию является EAGER (что, похоже, имеет место), EntityB следует извлекать через неявное соединение, не так ли? Что не так?

Действительно, по умолчанию FetchType для ManyToOne равно EAGER. Но это просто говорит о том, что сторона One должна загружаться при загрузке сторона Many, а не как . Как оставлено на усмотрение провайдера персистентности (а JPA не позволяет настроить стратегию).

Hibernate имеет специальные Fetch аннотации, позволяющие настроить режим извлечения. Из документации:

2.4.5.1. Ленивые опции и режимы извлечения

JPA поставляется с опцией извлечения определить ленивую загрузку и выборку режимы, однако Hibernate имеет много в этой области установлено больше опций. В порядке настроить ленивую загрузку и выборку стратегии, некоторые дополнительные введены аннотации:

  • [...]

  • @Fetch: определяет стратегию выборки, используемую для загрузки ассоциации. FetchMode может быть SELECT (выбор срабатывает, когда ассоциация нуждается быть загруженным), SUBSELECT (только доступны для коллекций, используйте стратегия выбора - пожалуйста, обратитесь к справочная документация Hibernate для получения дополнительной информации) или JOIN (используйте SQL JOIN для загрузки ассоциации при загрузка объекта-владельца). JOIN переопределяет любой ленивый атрибут ассоциация загружается через JOIN стратегия не может быть ленивой).

Возможно, вы захотите попробовать следующее (если не возражаете против использования аннотаций конкретного поставщика):

@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;
4 голосов
/ 16 ноября 2010

Решение, которое работало для текущего варианта использования, состоит в том, чтобы включить выборочное соединение в оператор:

select a from entityA left join fetch a.entityB

Это извлечет все связанные EntityBs (и переопределит FetchType.LAZY).

0 голосов
/ 11 ноября 2010

Прежде всего, почему вы вообще об этом беспокоитесь? Смысл использования библиотеки ORM в том, чтобы вам не приходилось иметь дело с такими деталями. Если вы видите, что какая-то операция выполняется медленно, , тогда вы начинаете думать об оптимизации запросов.

Я думаю, что стратегия Hibernate имеет смысл, так как отношения - ManyToOne. Это означает, что число EntityB, возвращаемых в запросе, никогда не будет больше количества EntityAs и обычно будет меньше. Поэтому попытка извлечь данные из EntityB через соединение, вероятно, будет означать отправку избыточных копий тех же данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...