JPA查询--使用原生sql 并且把查询结果转为实体对象

本文详细介绍了如何在Oracle数据库环境中,使用JPA的原生SQL查询并将结果转化为实体对象。文章涵盖了精确查询、模糊查询和分页查询的示例,并列举了在操作过程中可能出现的错误及解决方法,如字段名与数据库关键字冲突、实体类缺少@Entity和@Id注解等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章主要记录 使用原生sql查询 并且把查询结果转为实体对象, 注意:这儿使用的数据库 是oracle数据库

这儿记录了三种查询:精确查询,模糊查询,分页查询。

1.把原生sql查询的结果转为实体类对象用的是 Query query = em.createNativeQuery(sql, Student.class);

 2.实体类 要使用@Entity 和 @Id 注解,否则会报错

新建实体类

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Student {
	@Id
	private Long classNo;
	private Long studentNo;
	private String remark;
	//省略了getter setter方法
	@Override
	public String toString() {
		return "RiskUnit [classNo=" + classNo + ", studentNo=" + studentNo + ", remark=" + remark + "]";
	}	 
}

1. 精确查询   where t.riskunit_id = :riskunit_id

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
 
public class JpaDemoTest {
	@PersistenceContext
	private EntityManager em;

 	public void testJpaNativeQuery() {

		// sql
		String sql = "select t.class_no  classNo ,t.student_no studentNo ,t.remark remark  from t_student t where t.class_no = :class_no ";
		// 执行查询 并把结果专为实体类
		Query query = em.createNativeQuery(sql, Student.class);
		// 参数值
		query.setParameter("class_no", 400002);
		// 获取查询结果
		List<Student> students = query.getResultList();
		System.out.println("students===" + students.toString());
	}
}

2. 模糊查询    where t.riskunit_id like '%'||:riskunit_id||'%' 

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
 
public class JpaDemoTest {
	@PersistenceContext
	private EntityManager em;

 	public void testJpaNativeQuery() {

		// sql
		String sql = "select t.class_no  classNo ,t.student_no studentNo ,t.remark remark  from t_student t where t.class_no like '%'||:class_no||'%' ";
		// 执行查询 并把结果专为实体类
		Query query = em.createNativeQuery(sql, Student.class);
		// 参数值
		query.setParameter("class_no", 400002);
		// 获取查询结果
		List<Student> students = query.getResultList();
		System.out.println("students===" + students.toString());
	}
}

3. 原生sql查询并分页  这里参考oracle sql 3.oracle sql 分页实现

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

public class JpaDemoTest {
	@PersistenceContext
	private EntityManager em;

	public void testJpaNativeQuery() {
		Integer pageNo = 2;
		Integer pageSize = 10;
		String sql = "select t.class_no  classNo ,t.student_no studentNo ,t.remark remark  from t_student t where t.class_no like '%' || :class_no || '%' order by t.class_no ";
		sql = " select * from ( select a.*, rownum as a_rownum from (" + sql + ") a where rownum <= "
				+ (pageNo + 1) * pageSize + ") b where b.a_rownum > " + pageNo * pageSize;
		Query query = em.createNativeQuery(sql, Student.class);
		query.setParameter("class_no", 40000);
		List<Student> students = query.getResultList();
 		System.out.println("student===" + students.toString()); 
	}
}

4. 有几种可能hibernate或者sql的报错:

1.Caused by: java.sql.SQLSyntaxErrorException: ORA-01747: invalid user.table.column, table.column, or column specification

 查询sql的字段名与数据库关键字冲突了,仔细查看sql 是因为where t.like 写错了

2.javax.persistence.PersistenceException: org.hibernate.MappingException: Unknown entity: com.mx.JpaDemo.entity.RiskUnit
需要在实体类RiskUnit上加上@Entity注解 

3.Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.mx.JpaDemo.entity.RiskUnit

需要在实体类加上@Id注解   

<think>我们正在讨论JPA使用原生SQL查询数据的方法。根据引用资料,我们可以总结出几种主要方式:1.使用@Query注解,并设置nativeQuery=true,在value属性中编写SQL查询语句(引用[2][4])2.使用EntityManager创建原生SQL查询(引用[3])3.使用SqlResultSetMapping进行复杂结果映射(引用[3])具体步骤:1.使用@Query注解(简单查询):在Repository接口中定义方法,使用@Query注解,设置nativeQuery=true,并在value中写SQL语句。例如:@Query(nativeQuery=true,value="SELECT*FROMcustomer_courseWHERERelated_Code=1ANDCourse_ID=?1")List<CustomerCourse>findByCourseId(Integerid);注意:参数可以使用位置参数(如?1)或者命名参数(需配合@Param注解)。2.使用EntityManager(更灵活):注入EntityManager,然后使用createNativeQuery方法创建查询。例如:@PersistenceContextprivateEntityManagerentityManager;publicList<CustomerCourse>findCourses(Integerid){Stringsql="SELECT*FROMcustomer_courseWHERERelated_Code=1ANDCourse_ID=?1";Queryquery=entityManager.createNativeQuery(sql,CustomerCourse.class);query.setParameter(1,id);returnquery.getResultList();}注意:这里第二个参数指定了返回的实体类型,这样JPA会尝试将结果映射到该实体。3.使用SqlResultSetMapping(复杂结果):当查询结果不能直接映射到单个实体,或者需要返回多个实体和标量值时,可以使用@SqlResultSetMapping注解在实体类上定义结果映射。例如:@SqlResultSetMapping(name="CustomerCourseMapping",entities=@EntityResult(entityClass=CustomerCourse.class))然后在查询时指定映射名称:Queryquery=entityManager.createNativeQuery(sql,"CustomerCourseMapping");4.分页查询(引用[2]):在@Query注解中,除了value写查询数据的SQL,还可以指定countQuery来写查询总条数的SQL,然后方法参数传入Pageable,返回Page类型。例如:@Query(nativeQuery=true,value="select*fromt_merchant",countQuery="selectcount(1)fromt_merchant")Page<Merchant>findMerchantPage(Pageablepageable);5.动态条件拼接(引用[5]):如果查询条件动态变化,可以使用Specification(非原生SQL)或者使用字符串拼接(需注意SQL注入风险)或者使用CriteriaAPI。注意事项:-使用原生SQL查询时,表名和列名是数据库中的实际名称,而不是实体类的属性名。-当返回实体时,确保查询结果的列名与实体类的字段映射一致(或者使用@FieldResult进行映射)。-动态拼接SQL时要注意防止SQL注入,建议使用参数绑定。示例代码:假设有一个实体类CustomerCourse,对应数据库表customer_course。方式1:Repository中使用@QuerypublicinterfaceCustomerCourseRepositoryextendsJpaRepository<CustomerCourse,Long>{@Query(nativeQuery=true,value="SELECT*FROMcustomer_courseWHERERelated_Code=1ANDCourse_ID=:courseId")List<CustomerCourse>findByCourseId(@Param("courseId")IntegercourseId);}方式2:使用EntityManager@RepositorypublicclassCustomerCourseDao{@PersistenceContextprivateEntityManagerentityManager;publicList<CustomerCourse>findCourses(IntegercourseId){Stringsql="SELECT*FROMcustomer_courseWHERERelated_Code=1ANDCourse_ID=:courseId";Queryquery=entityManager.createNativeQuery(sql,CustomerCourse.class);query.setParameter("courseId",courseId);returnquery.getResultList();}}方式3:分页查询publicinterfaceCustomerCourseRepositoryextendsJpaRepository<CustomerCourse,Long>{@Query(nativeQuery=true,value="SELECT*FROMcustomer_courseWHERERelated_Code=1",countQuery="SELECTCOUNT(*)FROMcustomer_courseWHERERelated_Code=1")Page<CustomerCourse>findCustomerCourses(Pageablepageable);}总结:JPA提供了多种使用原生SQL查询的方式,可以根据需求选择合适的方法。简单查询可以直接在Repository中使用@Query注解;复杂查询或需要动态拼接时可以使用EntityManager;分页查询需要提供countQuery;对于结果集映射复杂的场景,可以使用SqlResultSetMapping。相关问题:1.JPA使用原生SQL查询时如何防止SQL注入?2.如何使用JPA原生SQL查询返回非实体对象(如DTO)?3.JPA原生SQL查询如何实现动态条件拼接?</think>### JPA 使用原生 SQL 查询数据的方法 JPA 支持通过原生 SQL 直接操作数据库,以下是具体实现方式及步骤: --- #### **1. 使用 `@Query` 注解(推荐)** 在 Repository 接口中直接编写 SQL 语句,通过 `nativeQuery = true` 声明使用原生 SQL: ```java public interface UserRepository extends JpaRepository<User, Long> { // 基础查询 @Query(value = "SELECT * FROM users WHERE age > :minAge", nativeQuery = true) List<User> findUsersByAge(@Param("minAge") int minAge); // 分页查询(需指定 countQuery) @Query( value = "SELECT * FROM users WHERE status = 1", countQuery = "SELECT COUNT(*) FROM users WHERE status = 1", nativeQuery = true ) Page<User> findActiveUsers(Pageable pageable); } ``` - **参数绑定**:使用 `:paramName` 语法 + `@Param` 注解 - **分页**:通过 `Pageable` 参数自动处理分页逻辑,`countQuery` 定义总数查询[^2] --- #### **2. 使用 `EntityManager` 动态构建** 注入 `EntityManager` 灵活执行复杂 SQL: ```java @PersistenceContext private EntityManager em; public List<User> executeCustomSQL(String name) { String sql = "SELECT * FROM users WHERE name = :nameParam"; Query query = em.createNativeQuery(sql, User.class); // 映射到实体类 query.setParameter("nameParam", name); return query.getResultList(); } ``` - **结果映射**:`createNativeQuery(sql, Entity.class)` 自动将结果转为实体对象 - **动态 SQL**:可拼接条件(注意防 SQL 注入)[^5] --- #### **3. 复杂结果映射(`@SqlResultSetMapping`)** 当查询结果无法直接映射到实体时,通过注解自定义映射: ```java // 在实体类上定义映射规则 @SqlResultSetMapping( name = "UserSummaryMapping", classes = @ConstructorResult( targetClass = UserDTO.class, columns = { @ColumnResult(name = "id", type = Long.class), @ColumnResult(name = "name") } ) ) // 使用映射 public List<UserDTO> getUserSummary() { String sql = "SELECT id, name FROM users"; Query query = em.createNativeQuery(sql, "UserSummaryMapping"); return query.getResultList(); } ``` - **适用场景**:返回 DTO 或聚合结果(如 COUNT/SUM)[^3] --- #### **4. 动态条件拼接(Specification 或字符串)** - **安全方式(JPA Specification)**: 通过 `CriteriaBuilder` 构建动态查询(非原生 SQL)。 - **原生 SQL 拼接(需谨慎)**: 使用 `StringBuilder` 拼接条件,但必须参数化防注入[^5]: ```java StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1"); if (name != null) { sql.append(" AND name = :name"); // 后续用 setParameter 绑定值 } ``` --- ### **关键注意事项** 1. **表名与列名** 原生 SQL使用**数据库实际表名和列名**(非实体类属性名),例如 `user_table` 而非 `User`。 2. **结果映射** - 直接返回实体需确保 SELECT 字段与实体属性匹配 - 复杂结构使用 `@SqlResultSetMapping` 显式映射[^3] 3. **性能优化** - 分页查询必须提供 `countQuery` 避免全表扫描[^2] - 大数据量查询建议分批处理(`LIMIT` + 游标) 4. **防 SQL 注入** **禁止**直接拼接字符串(如 `"WHERE name = '" + name + "'"`),**必须**使用参数绑定(`setParameter` 或 `@Param`)。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值