Как получить только некоторые поля объекта, имеющего ассоциации, с помощью запроса гибернации?

У меня есть объект с несколькими полями, некоторые из которых являются ассоциациями. Предположим, что структура сущности аналогична следующей:

@Entity
@Table(name="foos")
public class Foo {

  public Foo() {}
  @Id
  private Long id;

  @Column
  private String name;

  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "bar_id")
  private Bar bar;

  @ManyToOne
  @JoinColumn(name = "bar1_id")
  private Bar1 bar1;

  @ManyToOne
  @JoinColumn(name = "bar2_id")
  private Bar2 bar2;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "foo_id")
  private List<Bar3> bar3;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "foo_id")
  private List<Bar4> bar4;

  //getters and setters
}

Мой вопрос: как я могу загрузить только некоторые поля, а не весь объект с помощью запроса Hibernate?
Я хочу получить только идентификатор, панель, панель1 и панель3 и не хочу получать остальные поля (имя, панель2, bar4) в созданном объекте Foo.
Я новичок в Hibernate, поэтому буду признателен за любой совет.


person Keshav sardana    schedule 14.03.2018    source источник


Ответы (3)


Самый простой способ — создать репозиторий, который будет получать объекты по выбранным критериям, а затем извлекать из списка объектов только те поля, которые вам нужны, если вы используете Spring Data: https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

Только для спящего режима это должно выглядеть так:

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();

    Foo foo = entityManager.find(Foo.class,id);

    entityManager.getTransaction().commit();

Или с помощью пользовательского SQL-запроса только для выбранных полей:

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();

    Query query = entityManager.createQuery("SELECT e FROM Foo e");
    List<Foo> workerList = query.getResultList();
person GoodJobException    schedule 14.03.2018
comment
Спасибо за ответ. Но я думаю, что приведенный выше запрос извлечет все поля объекта foo из базы данных. Я хочу загрузить только указанные четыре поля в объект foo из db, т. е. результирующий объект foo не должен получать никаких значений для bar2, bar4 из db. - person Keshav sardana; 15.03.2018

Мне удалось решить проблему с помощью следующей ссылка. Я создал новую сущность FooShort с тем же именем таблицы "foos" и содержит только поля, необходимые для Foo.

@Entity
@Table(name="foos")
public class FooShort {

  public FooShort() {}
  @Id
  private Long id;

  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "bar_id")
  private Bar bar;

  @ManyToOne
  @JoinColumn(name = "bar1_id")
  private Bar1 bar1;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "foo_id")
  private List<Bar3> bar3;

  //getters and setters
}
person Keshav sardana    schedule 16.03.2018

Спящий режим 5, Весна 5

У вас есть несколько вариантов, но самый простой вариант — создать простой @Query с нужными вам столбцами/полями, см. пример кода из одного из моих проектов.

Прежде всего, вам не нужно создавать DTO с подмножеством полей, я покажу варианты как для гибридной версии.

Вариант первый: объект DTO с подмножеством столбцов/полей.

Код репозитория DTO:

@Query( "SELECT new com.your.package.name.customers.CompanyDTOIdName(c.id, c.name) " +
        "FROM Company c " +
        "WHERE c.enabled = 1 " +
        "ORDER BY c.name ASC"           )
List<CompanyDTOIdName> compDtoNameWhereEnabledTrue();

Объект подмножества DTO:

public class CompanyDTOIdName {

    private long id;
    private String name;

    public CompanyDTOIdName(long id, String name) {
        this.id = id;
        this.name = name;
    }

    // accessors/mutators methods
}

Спящий код выполнения SQL:

SELECT
    new com.your.package.name.customers.CompanyDTOIdName(c.id, c.name) 
FROM
    Company c 
WHERE
    c.enabled = 1 
ORDER BY
    c.name ASC

select
    company0_.companyId as col_0_0_,
    company0_.name as col_1_0_ 
from
    ome_company company0_ 
where
    company0_.enabled=1 
order by
    company0_.name ASC

Основным недостатком является то, что вам потребуется отдельный объект DTO для каждой комбинации столбцов.


Вариант второй: использование оригинального @Entity

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

Код репозитория:

@Query( "SELECT new com.your.package.name.customers.Company(c.id, c.name) " +
        "FROM Company c " +
        "WHERE c.enabled = 1 " +
        "ORDER BY c.name ASC"
)
List<Company> compNameWhereEnabledTrue();

Где Company — ваш исходный объект @Entity.

Ваш объект Entity должен содержать пустые конструкторы, а также конструкторы, соответствующие вашему оператору @Query в этом случае:

public void Company() {}
public void Company(Long id, String name) { /* code omitted */ }

Спящий код выполнения SQL:

SELECT
    new com.your.package.name.customers.Company(c.id, c.name) 
FROM
    Company c 
WHERE
    c.enabled = 1 
ORDER BY
    c.name ASC

select
    company0_.companyId as col_0_0_,
    company0_.name as col_1_0_ 
from
    ome_company company0_ 
where
    company0_.enabled=1 
order by
    company0_.name ASC

Предостережение: я уверен, что вы видите проблему с этим подходом. Загружаются только указанные столбцы/поля. Вы получите исключения, если попытаетесь получить доступ к неинициированным полям.


Гибридная версия с использованием интерфейсов (лучшее из обоих миров)

Создайте интерфейс с обязательными полями доступа.

public interface CompanyDTOIdNameInterface {

    public Long getId();
    public String getName();
}

Реализовать интерфейс для объекта @Entity.

public class Company implements Serializable,
                                CompanyDTOIdNameInterface
{ /* omitted code */ }

Код репозитория:

@Query( "SELECT new com.csfactorynw2.site.customers.Company(c.id, c.name) " +
            "FROM Company c " +
            "WHERE c.enabled = 1 " +
            "ORDER BY c.name ASC"
    )
    List<CompanyDTOIdNameInterface> compNameWhereEnabledTrue();

Это так просто. Удачи :)

person Vlad Palnik    schedule 17.04.2019