Создание пользовательского запроса с помощью Spring DATA JPA?

Я работаю над проектом с Spring Data JPA. У меня есть таблица в базе данных как my_query.

Я хочу создать метод, который берет строку в качестве параметра, а затем выполняет ее как запрос в базе данных.

Метод:

executeMyQuery(queryString)

В качестве примера, когда я прохожу

queryString= "SELECT * FROM my_query"

то он должен выполнить этот запрос на уровне БД.

Класс репозитория выглядит следующим образом.

public interface MyQueryRepository extends JpaRepository<MyQuery, Long>{
    public MyQuery findById(long id);

    @Modifying(clearAutomatically = true)
    @Transactional
    @Query(value = "?1", nativeQuery = true)
    public void executeMyQuery(String query);

}

Однако это не сработало, как я ожидал. Он дает следующую ошибку.

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''select * from my_query;'' at line 1

Есть ли другой способ, чтобы я мог достичь этой цели. заранее спасибо

Ответ 1

Единственная часть, которую вы можете параметризировать, это значения, используемые в предложении WHERE. Рассмотрите этот пример из официального документа:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}

Ответ 2

Специальной поддержки для этого нет. Но вы можете создать собственный метод с параметром String и в вашей реализации вы получите EntityManager и выполните его.

Возможно полезные ссылки:

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations

Как получить доступ к диспетчеру объектов с весенними загрузками и данными весны

Примечание. Я бы переосмыслил, что то, что вы пытаетесь сделать, является хорошей идеей, потому что оно исчерпывает детали реализации репозитория в остальной части приложения.

Ответ 3

Спасибо, @ilya. Существует ли альтернативный подход для достижения этой задачи с использованием Spring Data JPA? Без аннотации @Query?

Я просто хочу действовать по этому поводу. да, вы можете обойти это без использования аннотации @query. вам нужно определить производный запрос из вашего интерфейса, который реализует экземпляр репозитория JPA.

то из вашего экземпляра репозитория вы будете подвержены всем методам, которые позволяют выполнять операции CRUD в вашей базе данных, такие как

 interface UserRepository extends CrudRepository<User, Long> {

 long deleteByLastname(String lastname);

 List<User> removeByLastname(String lastname);
}

с этими методами данные весны поймут, что вы пытаетесь архивировать и внедрять их соответственно.

Также имейте в виду, что основные операции CRUD предоставляются из определения базового класса, и вам не нужно их определять. например, это класс JPARepository, определенный весной, поэтому его расширение дает вам все методы.

 public interface CrudRepository<T, ID extends Serializable>
 extends Repository<T, ID> {

 <S extends T> S save(S entity);      

 Optional<T> findById(ID primaryKey); 

 Iterable<T> findAll();               

 long count();                        

 void delete(T entity);               

 boolean existsById(ID primaryKey);   


}

Для получения дополнительной информации ознакомьтесь с документацией по адресу https://docs.spring.io/spring-data/jpa/docs/current/reference/html/.

Ответ 4

С помощью EntityManager вы можете достичь этого.

Предположим, что ваш класс сущности подобен ниже:

import javax.persistence.*;
import java.math.BigDecimal;

@Entity
@Table(name = "USER_INFO_TEST")
public class UserInfoTest {
    private int id;
    private String name;
    private String rollNo;

    public UserInfoTest() {
    }

    public UserInfoTest(int id, String name) {
    this.id = id;
    this.name = name;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false, precision = 0)
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name", nullable = true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Basic
    @Column(name = "roll_no", nullable = true)
    public String getRollNo() {
        return rollNo;
    }

    public void setRollNo(String rollNo) {
        this.rollNo = rollNo;
    }
}

И ваш запрос "выберите идентификатор, имя пользователя, где roll_no = 1001".

Здесь запрос вернет объект с идентификатором и именем столбца. Ваш класс ответа ниже:

Ваш класс ответа выглядит так:

public class UserObject{
    int id;
    String name;
    String rollNo;

    public UserObject(Object[] columns) {
        this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
        this.name = (String) columns[1];
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRollNo() {
        return rollNo;
    }

    public void setRollNo(String rollNo) {
        this.rollNo = rollNo;
    }
}

здесь конструктор UserObject получит массив объектов и установит данные вместе с объектом.

public UserObject(Object[] columns) {
            this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
            this.name = (String) columns[1];
        }

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

public UserObject getUserByRoll(EntityManager entityManager,String rollNo) {

        String queryStr = "select id,name from users where roll_no = ?1";
        try {
            Query query = entityManager.createNativeQuery(queryStr);
            query.setParameter(1, rollNo);

            return new UserObject((Object[]) query.getSingleResult());
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

Здесь вы должны импортировать следующие пакеты:

import javax.persistence.Query;
import javax.persistence.EntityManager;

Теперь ваш основной класс, вы должны вызвать эту функцию. Сначала получите EntityManager и вызовите эту функцию getUserByRoll(EntityManager entityManager,String rollNo). Процедура вызова приведена ниже:

Вот импорт

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

получить EntityManager следующим образом:

@PersistenceContext
private EntityManager entityManager;

UserObject userObject = getUserByRoll(entityManager,"1001");

Теперь у вас есть данные в этом userObject.

Примечание:

query.getSingleResult() возвращает массив объектов. Вы должны поддерживать позицию столбца и тип данных с позицией столбца запроса.

выберите идентификатор, имя пользователя, где roll_no = 1001

запрос возвращает массив и его [0] → идентификатор и 1 → имя.

Подробнее посетите эту тему.

Ответ 5

Основываясь на ответе @jelies, я использую следующий подход

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

public class MyQueryRepositoryImpl implements MyQueryRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;

    public int executeQuery(String query) {
        return entityManager.createNativeQuery(query).executeUpdate();
    }
}

Это выполнит пользовательский запрос.