使用Spring DATA JPA创建自定义查询?

32

我正在使用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

还有其他的方法吗,可以实现这个目标?

6个回答

59

你唯一能够参数化的部分是在WHERE子句中使用的值。考虑来自官方文档的以下示例:

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

谢谢@ilya。有没有使用Spring Data JPA实现此任务的替代方法?不使用@Query注释? - B378
3
如果你确实需要这样的行为,那么你需要获取EntityManager并直接使用它运行查询。请参考这个答案:https://dev59.com/sGct5IYBdhLWcg3wsfkZ#15341601。但是请三思而后行。如果你在多个地方都需要这样做,可能Spring Data不适合你。它的主要思想是将查询隐藏起来,让你只需稍微调整一下,例如使用@Query注解。 - ilya
USERS必须是模型(领域)类的名称吗?还是您必须查看数据库中表的名称? - hane Smitter
在这种情况下,“USERS”是一个表的名称,因为它是一个本地SQL查询:“nativeQuery = true”。 - ilya

6
使用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;
    }
}

您的查询语句是 "select id, name from users where roll_no = 1001"。

这个查询将返回一个带有 id 和 name 列的对象。您的 Response 类如下所示:

您的 Response 类如下:

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() 返回一个对象数组。您必须保持查询列位置和数据类型与查询列位置相同。

select id,name from users where roll_no = 1001

查询返回一个数组,它的 [0] --> id 和 1 -> name。

更多信息请访问此线程


4

3

基于 @jelies 答案,我正在使用以下方法

您可以为自定义方法创建另一个接口(例如 MyQueryCustom),然后按照以下方式实现它。

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

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

这将执行一个自定义查询。

2
如果你想添加自定义查询,你需要添加@Param
@Query("from employee where name=:name")    
employee findByName(@Param("name)String name);
}

这个查询将选择具有匹配项name的唯一记录。这将起作用。


1
谢谢@ilya。有没有使用Spring Data JPA实现此任务的替代方法?不使用@Query注释? 我只想处理这一部分。是的,有一种方法可以在不使用@query注释的情况下完成它。您需要从实现JPA存储库实例的接口中定义一个派生查询。
然后,从存储库实例中,您将暴露所有允许对数据库执行CRUD操作的方法。
 interface UserRepository extends CrudRepository<User, Long> {

 long deleteByLastname(String lastname);

 List<User> removeByLastname(String lastname);
}

使用这些方法,Spring Data 将理解您的目标并相应地实现它们。
同时请注意基本的 CRUD 操作已经在基类定义中提供,您不需要重新定义它们。例如,这是由 Spring 定义的 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/上的文档。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接