Spring JPA выбирает конкретные столбцы


146

Я использую Spring JPA для выполнения всех операций с базой данных. Однако я не знаю, как выбрать конкретные столбцы из таблицы в Spring JPA?

Например:
SELECT projectId, projectName FROM projects



Идея, лежащая в основе JPA - не искать конкретные поля, заключается в том, что затраты (с точки зрения эффективности) одинаковы для извлечения одного столбца или всех столбцов из одной строки таблицы.
Desorder

7
@ Desorder - стоимость не всегда одинакова. Это, вероятно, не имеет большого значения для более простых, примитивных типов данных, но причина, по которой я оказался на этой странице, заключается в том, что я заметил, что простой запрос «список документов» выполняется медленно. У этого объекта есть столбец BLOB (он нужен для загрузки / хранения файлов), и я подозреваю, что он медленный, потому что он загружает BLOB-объекты в память, даже если они не требуются для перечисления документов.
jm0

@ jm0 Насколько вы помните, сколько таблиц было BLOB-столбцов?
Desorder

1
@Dororder это была только одна таблица, но я выполнял функцию «list» (multirow - вывести список всех документов, созданных по заданному идентификатору). Единственная причина, по которой я заметил эту проблему, заключалась в том, что этот простой запрос к списку занимал несколько секунд, тогда как более сложные запросы к другим таблицам выполнялись практически мгновенно. Как только я понял, я знал, что это будет страдать все больше и больше по мере добавления строк, потому что Spring JPA загружает каждый большой двоичный объект в память, даже если они не используются. Я нашел достойное решение для данных Spring (опубликовано ниже), но я думаю, что у меня есть еще лучшее решение - аннотация JPA, я опубликую tmrw, если оно будет работать
jm0

Ответы:


75

Вы можете установить nativeQuery = trueв @Queryаннотации из Repositoryкласса, как это:

public static final String FIND_PROJECTS = "SELECT projectId, projectName FROM projects";

@Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findProjects();

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

public List<Project> findAll()

Вероятно, стоит посмотреть и документацию Spring .


5
нет необходимости в собственных запросах. Вам следует избегать их использования, поскольку они разрушают преимущества JPQL. см. ответ Аталса.
phil294

1
Для меня я должен был квалифицировать первый атрибут (выше FIND_PROJECTS) с valueименем атрибута (следовательно, если бы это был мой код, я должен был бы написать его как @Query(value = FIND_PROJECTS, nativeQuery = true)и т. Д.
smeeb

173

Вы можете использовать прогнозы из Spring Data JPA (doc) . В вашем случае создайте интерфейс:

interface ProjectIdAndName{
    String getId();
    String getName();
}

и добавьте следующий метод в ваш репозиторий

List<ProjectIdAndName> findAll();

11
Это чистое решение. он может иметь шаблон котла, но интерфейс может быть внутренним классом сущности. Делать это довольно чисто.
Iceman

1
офигенно, просто помните, что не нужно реализовывать интерфейс на вашей сущности, иначе он не будет работать
alizelzele

1
Куда идет проектируемый интерфейс? в своем собственном файле или он может быть включен в открытый интерфейс, который возвращает полные свойства объекта?
Мишо Ризо

8
Это решение не работает при расширении JpaRepository, кто-нибудь знает обходной путь?
немецкий

4
Вы не можете использовать findAll (); как это будет конфликтовать с методом JPARepositorys. Вам нужно использовать что-то вроде List <ProjectIdAndName> findAllBy ();
Code_Mode

137

Мне не очень нравится синтаксис (он выглядит немного хакерским ...), но это самое элегантное решение, которое мне удалось найти (оно использует собственный запрос JPQL в классе репозитория JPA):

@Query("select new com.foo.bar.entity.Document(d.docId, d.filename) from Document d where d.filterCol = ?1")
List<Document> findDocumentsForListing(String filterValue);

Тогда, конечно, вам просто нужно предоставить конструктор, Documentкоторый принимает docId& filenameкак аргументы конструктора.


9
(и, между прочим, я подтвердил, что вам не нужно указывать полное имя класса, если импортируется «Document» - просто так было, потому что так было в единственном примере, который мне удалось найти)
jm0,

это должен быть принятый ответ. Он отлично работает и действительно выбирает только необходимые поля.
Йонатан Вилкоф

1
Также включены ненужные поля, но со значением 'null', занимают ли эти поля память?
Игрок

да, но настолько минимальный, что в подавляющем большинстве случаев было бы действительно нелепо пытаться обойти это - stackoverflow.com/questions/2430655/… вам придется создавать специализированные легкие объекты без этих полей и делать так, чтобы они указывали на одно и то же стол? какой IMO нежелателен при использовании ORM и использовании их для их отношений ... гипер-оптимизация может быть больше в области простого использования некоторого облегченного DSL запроса и сопоставления непосредственно с DTO, и даже тогда я думаю, что избыточность не рекомендуется
jm0

2
jm0 он не работал для меня без полного имени класса, хотя он был импортирован. Это скомпилировано успешно, хотя.
Гейзенберг

20

В моей ситуации мне нужен только результат json, и это работает для меня:

public interface SchoolRepository extends JpaRepository<School,Integer> {
    @Query("select s.id, s.name from School s")
    List<Object> getSchoolIdAndName();
}

в контроллере:

@Autowired
private SchoolRepository schoolRepository;

@ResponseBody
@RequestMapping("getschoolidandname.do")
public List<Object> getSchool() {
    List<Object> schools = schoolRepository.getSchoolIdAndName();
    return schools;
}

2
Вы должны заменить Objectпользовательским интерфейсом, как описано в mpr. работает безупречно
phil294

14

В моем случае я создал отдельный класс сущностей без полей, которые не являются обязательными (только с полями, которые являются обязательными).

Сопоставьте сущность с той же таблицей. Теперь, когда требуются все столбцы, я использую старую сущность, когда требуются только некоторые столбцы, я использую облегченную сущность.

например

@Entity
@Table(name = "user")
Class User{
         @Column(name = "id", unique=true, nullable=false)
         int id;
         @Column(name = "name", nullable=false)
         String name;
         @Column(name = "address", nullable=false)
         Address address;
}

Вы можете создать что-то вроде:

@Entity
@Table(name = "user")
Class UserLite{
         @Column(name = "id", unique=true, nullable=false)
         int id;
         @Column(name = "name", nullable=false)
         String name;
}

Это работает, когда вы знаете столбцы для извлечения (и это не изменится).

не будет работать, если вам нужно динамически выбирать столбцы.


Привет sachin, у меня есть одно сомнение, если я создам сущность, как ты упоминал выше. когда JPA запустится и попытается создать таблицу с именем пользователя. какой объект будет использовать.
user3364549

3
никогда не создавайте таблицы с помощью JPA, создавайте таблицы вручную в БД, используйте JPA для отображения реляционного мира на объектный мир.
Сачин Шарма

Почему вы не можете использовать наследование здесь?
Deadbug

8

Я предполагаю, что самый простой способ - использовать QueryDSL , который поставляется с Spring-Data.

Используя на ваш вопрос ответ может быть

JPAQuery query = new JPAQuery(entityManager);
List<Tuple> result = query.from(projects).list(project.projectId, project.projectName);
for (Tuple row : result) {
 System.out.println("project ID " + row.get(project.projectId));
 System.out.println("project Name " + row.get(project.projectName)); 
}}

Менеджер сущностей может быть Autowired, и вы всегда будете работать с объектом и предложениями без использования языка * QL.

Как видно из ссылки, последний вариант кажется мне более элегантным, то есть с помощью DTO для сохранения результата. Примените к вашему примеру, который будет:

JPAQuery query = new JPAQuery(entityManager);
QProject project = QProject.project;
List<ProjectDTO> dtos = query.from(project).list(new QProjectDTO(project.projectId, project.projectName));

Определение ProjectDTO как:

class ProjectDTO {

 private long id;
 private String name;
 @QueryProjection
 public ProjectDTO(long projectId, String projectName){
   this.id = projectId;
   this.name = projectName;
 }
 public String getProjectId(){ ... }
 public String getProjectName(){....}
}

5

С более новыми версиями Spring можно сделать следующее:

Если не использовать собственный запрос, это можно сделать, как показано ниже:

public interface ProjectMini {
    String getProjectId();
    String getProjectName();
}

public interface ProjectRepository extends JpaRepository<Project, String> { 
    @Query("SELECT p FROM Project p")
    List<ProjectMini> findAllProjectsMini();
}

Используя собственный запрос, можно сделать то же самое, как показано ниже:

public interface ProjectRepository extends JpaRepository<Project, String> { 
    @Query(value = "SELECT projectId, projectName FROM project", nativeQuery = true)
    List<ProjectMini> findAllProjectsMini();
}

Для получения подробной информации проверьте документы


4

На мой взгляд, это отличное решение:

interface PersonRepository extends Repository<Person, UUID> {

    <T> Collection<T> findByLastname(String lastname, Class<T> type);
}

и используя это так

void someMethod(PersonRepository people) {

  Collection<Person> aggregates =
    people.findByLastname("Matthews", Person.class);

  Collection<NamesOnly> aggregates =
    people.findByLastname("Matthews", NamesOnly.class);
}

Почему бы не вернуть List <T> вместо коллекции ?!
Абдулла Хан

@AbdullahKhan, потому что результат не всегда может иметь порядок.
Рави Санвал

4

Используя Spring Data JPA, существует возможность выбора определенных столбцов из базы данных.

---- в DAOImpl ----

@Override
    @Transactional
    public List<Employee> getAllEmployee() throws Exception {
    LOGGER.info("Inside getAllEmployee");
    List<Employee> empList = empRepo.getNameAndCityOnly();
    return empList;
    }

---- в репо ----

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
    @Query("select e.name, e.city from Employee e" )
    List<Employee> getNameAndCityOnly();
}

Это сработало на 100% в моем случае. Спасибо.


2

Вы можете использовать JPQL:

TypedQuery <Object[]> query = em.createQuery(
  "SELECT p.projectId, p.projectName FROM projects AS p", Object[].class);

List<Object[]> results = query.getResultList();

или вы можете использовать собственный SQL-запрос.

Query query = em.createNativeQuery("sql statement");
List<Object[]> results = query.getResultList();

2

Можно указать nullкак значение поля в нативном sql.

@Query(value = "select p.id, p.uid, p.title, null as documentation, p.ptype " +
            " from projects p " +
            "where p.uid = (:uid)" +
            "  and p.ptype = 'P'", nativeQuery = true)
Project findInfoByUid(@Param("uid") String uid);

2

Вы можете применить приведенный ниже код в своем классе интерфейса репозитория.

entityname означает имя вашей таблицы базы данных как проекты. И Список означает, что Проект является классом Entity в ваших Проектах.

@Query(value="select p from #{#entityName} p where p.id=:projectId and p.projectName=:projectName")

List<Project> findAll(@Param("projectId") int projectId, @Param("projectName") String projectName);

0

Использование собственного запроса:

Query query = entityManager.createNativeQuery("SELECT projectId, projectName FROM projects");
List result = query.getResultList();

0

Вы можете использовать ответ, предложенный @jombie, и:

  • поместите интерфейс в отдельный файл вне класса сущности;
  • использовать нативный запрос или нет (выбор зависел от ваших потребностей);
  • не переопределяйте findAll()метод для этой цели, но используйте имя по вашему выбору;
  • не забудьте вернуть Listпараметризованный с вашим новым интерфейсом (например List<SmallProject>).
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.