JPA EntityManager.createNativeQuery()查询的结果与数据库中的不一致

一、问题描述

@PersistenceContext
private EntityManager entityManager;
 
public static Page getPageDate(String selectSql, String countSql, String whereSql, int page, int size,Class clazz){
        //1、校验参数
        if (StringUtils.isBlank(selectSql) || StringUtils.isBlank(countSql)) {
            log.info("参数校验失败,参数{}{}为必填项", "selectSql", "countSql");
            return null;
        }
 
        if (StringUtils.isNotBlank(whereSql)) {
            countSql = countSql + whereSql;
            log.info("执行sql >>>>>> {},获取数据总数", countSql);
            selectSql = selectSql + whereSql;
            log.info("执行sql >>>>>> {},获取数据", selectSql);
            log.info(selectSql);
        }
 
        //2、数据查询逻辑
        try{
            
 
            //获取数据总数
            Query countQuery = entityManagerPrimary.createNativeQuery(countSql);
            BigInteger totalCount = (BigInteger)countQuery.getSingleResult();
 
            //查询数据
            Query query = entityManagerPrimary.createNativeQuery(selectSql, clazz);
            
            //设置分页信息
            Pageable pageable = new PageRequest(page - 1, size);
            query.setFirstResult(pageable.getOffset());
            query.setMaxResults(pageable.getPageSize());
            List resultList = query.getResultList();
 
            //查询结果封装
            Page data = new PageImpl<>(resultList, pageable, totalCount.longValue());
            return data;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

alterFlag属性与数据库中的值不一致。

二、原因查找

当使用`EntityManager.createNativeQuery()`方法执行原生SQL查询时,结果与数据库中的数据不一致可能是由于以下几个原因导致的:

1. 缓存的问题:JPA实现通常会使用缓存来提高性能,如果之前执行过相同的查询,并且缓存中存在数据,则可能会返回缓存中的数据而不是最新的数据库数据。你可以尝试在查询之前使用`EntityManager.clear()`方法清除缓存,或者使用`EntityManager.setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache)`提示来禁用缓存。

2. 事务问题:如果你在查询之前没有正确提交或回滚之前的事务,可能会导致查询结果不一致。你可以确保在执行查询之前提交或回滚任何未完成的事务,或者使用适当的事务隔离级别来解决并发读取的一致性问题。

3. 数据库同步问题:如果你的查询结果与数据库中的数据不一致,可能是因为数据库存在未及时同步的情况。你可以尝试手动执行`FLUSH`命令来将所有未保存的更改立即刷新到数据库中,以确保读取到最新的数据。

4. 缓存配置问题:某些情况下,JPA缓存的配置可能导致数据不一致。你可以检查缓存配置是否正确,并且缓存的过期策略是否满足你的需求。

5. 其他并发问题:如果在查询过程中存在其他并发操作修改了数据库中的数据,那么查询结果可能与数据库中的数据不一致。这种情况下,你可以考虑使用事务隔离级别来解决并发读取问题,或者使用乐观锁来处理并发修改。

综上所述,查询结果与数据库中的数据不一致可能是由于缓存、事务、数据库同步、缓存配置或其他并发问题引起的。你可以根据具体情况检查和调整相关配置,并考虑使用事务隔离级别或乐观锁来解决这个问题。

三、解决办法

@PersistenceContext
private EntityManager entityManager;
 
public static Page getPageDate(String selectSql, String countSql, String whereSql, int page, int size,Class clazz){
        //1、校验参数
        if (StringUtils.isBlank(selectSql) || StringUtils.isBlank(countSql)) {
            log.info("参数校验失败,参数{}{}为必填项", "selectSql", "countSql");
            return null;
        }
 
        if (StringUtils.isNotBlank(whereSql)) {
            countSql = countSql + whereSql;
            log.info("执行sql >>>>>> {},获取数据总数", countSql);
            selectSql = selectSql + whereSql;
            log.info("执行sql >>>>>> {},获取数据", selectSql);
            log.info(selectSql);
        }
 
        //2、数据查询逻辑
        try{
            
 
            //清除缓存,防止缓存与数据库数据不一致问题
            entityManagerPrimary.clear();
 
            //获取数据总数
            Query countQuery = entityManagerPrimary.createNativeQuery(countSql);
            BigInteger totalCount = (BigInteger)countQuery.getSingleResult();
 
            //查询数据
            Query query = entityManagerPrimary.createNativeQuery(selectSql, clazz);
            
            //设置分页信息
            Pageable pageable = new PageRequest(page - 1, size);
            query.setFirstResult(pageable.getOffset());
            query.setMaxResults(pageable.getPageSize());
            List resultList = query.getResultList();
 
            //查询结果封装
            Page data = new PageImpl<>(resultList, pageable, totalCount.longValue());
            return data;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

使用 entityManager.clear()方法清楚缓存,问题解决。

### 使用 `entityManager.createNativeQuery` 的指南 `entityManager.createNativeQuery` 是 JPA 中用于执行原生 SQL 查询的方法,允许开发者直接编写 SQL 语句以实现复杂查询[^1]。以下是关于该方法的详细说明和使用示例。 #### 方法签名 `createNativeQuery` 提供了多种重载形式,以下是最常用的两种: 1. `Query createNativeQuery(String sqlString)`:仅指定 SQL 字符串。 2. `Query createNativeQuery(String sqlString, Class<T> resultClass)`:指定 SQL 字符串以及结果映射的实体类。 #### 示例 1:执行简单查询并返回列表 以下代码展示了如何通过 `createNativeQuery` 执行一个简单的 SELECT 查询,并将结果作为对象数组返回。 ```java String sql = "SELECT id, name FROM users WHERE age > :age"; Query query = entityManager.createNativeQuery(sql); query.setParameter("age", 18); // 设置参数 List<Object[]> results = query.getResultList(); // 获取结果集 ``` 上述代码中,`results` 是一个对象数组列表,每个数组元素对应一行查询结果[^1]。 #### 示例 2:将结果映射到实体类 如果希望将查询结果直接映射到实体类,则可以使用带 `resultClass` 参数的重载方法。 ```java String sql = "SELECT * FROM users WHERE age > :age"; Query query = entityManager.createNativeQuery(sql, User.class); // 指定实体类 query.setParameter("age", 18); List<User> users = query.getResultList(); // 获取结果集 ``` 此代码片段会将查询结果自动映射到 `User` 实体类实例中[^1]。 #### 示例 3:处理多表连接查询 当需要执行复杂的多表连接查询时,可以通过 `createNativeQuery` 并结合手动映射结果来实现。 ```java String sql = "SELECT u.id, u.name, d.department_name " + "FROM users u " + "JOIN departments d ON u.department_id = d.id"; Query query = entityManager.createNativeQuery(sql); List<Object[]> results = query.getResultList(); for (Object[] row : results) { Long userId = (Long) row[0]; String userName = (String) row[1]; String departmentName = (String) row[2]; System.out.println("User: " + userName + ", Department: " + departmentName); } ``` 上述代码展示了如何处理多表连接查询结果[^5]。 #### 示例 4:执行更新或删除操作 `createNativeQuery` 还支持执行 DML(数据操作语言)语句,如 INSERT、UPDATE 和 DELETE。 ```java String sql = "UPDATE users SET status = :status WHERE id = :id"; Query query = entityManager.createNativeQuery(sql); query.setParameter("status", "ACTIVE"); query.setParameter("id", 1); int rowsUpdated = query.executeUpdate(); // 返回受影响的行数 ``` 此代码片段演示了如何通过原生 SQL 更新数据库中的记录[^1]。 #### 常见问题解决方法 - **错误:Unknown entity** 如果在调用 `createNativeQuery` 时遇到 `org.hibernate.MappingException: Unknown entity` 错误,通常是因为实体类未被正确配置为 JPA 管理的实体。确保实体类上已标注 `@Entity` 注解,并且其包路径已被扫描[^4]。 - **性能优化** 在执行复杂查询时,建议尽量减少必要的字段选择,避免全表扫描。此外,合理使用索引能够显著提升查询效率[^5]。 --- ### 注意事项 - 使用 `createNativeQuery` 时需注意 SQL 注入风险,务必通过 `setParameter` 方法绑定参数[^2]。 - 对于简单的 CRUD 操作,推荐优先使用 JPQL 或 Spring Data JPA 提供的内置方法,以保持代码的可移植性[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值