getHibernateTemplate().execute(new HibernateCallback())方法

本文介绍如何在Spring中利用HibernateCallback接口在同一个事务中执行多个持久化操作,通过回调机制实现连环操作,提升代码的灵活性和效率。

1. HibernateCallback是一个接口,该接口只有一个方法doInHibernate(org.hibernate.Session session),该方法

 

只有一个参数 Session。

 

2. 方法 doInHibernate 的方法体就是 Spring执行的持久化操作。

 

3. 使用HibernateTemplate执行execute(new HibernateCallback())方法,从HibernateCallback中得到session,

 

在此session中做多个操作,并希望这些操作位于同一个事务中。

 

4. 回调实际就是一种事件触发模式,就象连环地雷一样,一旦触发一个,这个再触发另外一个,你在find这个方法被执行

 

时,希望同时执行其他方法,就需要回调。

 

例如:

          return (List<E>) getHibernateTemplate().execute(
                new HibernateCallback() {
                        public Object doInHibernate(Session session)
                              throws HibernateException {
                                          Criteria criteria = session.createCriteria(entityClass);
                                          criteria.setFirstResult(pageSize * (pageIndex));
                                          criteria.setMaxResults(pageSize);
                                          return criteria.list();
                               }
    }); 

 

 

如例子代码中,他希望在执行execute()时也执行HibernateCallback 中的一些方法。

 

5. HibernateTemplate提供这个HibernateCallback,
          就是为了满足这种使用了HibernateTemplate的情况下,
          仍然需要直接访问Session的需求而来的
          它提供了在HibernateTemplate里面直接访问Session的能力
          这个就是我们要使用HibernateCallback的原因.

 

6  需要在hibernate中写连接多个表的sql语句

 

    final  String  sql = "select  textvalue  from  a";
  List   list = (List)this.getHibernateTemplate().execute(new org.springframework.orm.hibernate.HibernateCallback(){
   public Object doInHibernate(Session session) throws HibernateException, SQLException {
               Connection con = session.connection();  
                 PreparedStatement ps = con.prepareStatement(sql);  
                 ResultSet rs = ps.executeQuery();  
                 List all = new ArrayList();
                 while(rs.next()){  
                   String  result = (String)rs.getString("textvalue"); 
                   all.add(result);
                 }  
                 rs.close();  
                 ps.close();  
                 session.flush();  
                 session.close();  
                 return all;  
   }
     });

<think>我们正在处理Hibernate中的NonUniqueObjectException异常。根据引用内容,该异常通常发生在Hibernate的Session中已经存在一个具有相同标识符(ID)的对象实例,而尝试将另一个具有相同ID但不同状态的对象关联到同一个Session时。以下是解决此问题的步骤和原因分析。 ### 原因分析 1. **重复对象冲突**:在同一个Hibernate Session中,不能有两个具有相同ID的实体对象实例。如果尝试将一个已加载到Session中的对象与另一个具有相同ID的新对象(或从其他Session中分离的对象)关联,就会抛出此异常[^2][^3][^4]。 2. **常见场景**: - 从不同来源(如不同Session或不同查询)获取了同一个持久化对象的两个不同实例。 - 在更新操作前,已经有一个相同ID的对象存在于Session中,然后又尝试保存或更新另一个相同ID的对象[^4]。 3. **级联操作问题**:在级联更新时,如果父子对象关系处理不当,可能导致同一个对象被多次关联到Session[^3]。 ### 解决方法 根据引用内容,以下是几种有效的解决方案: 1. **清除Session缓存**: - 在更新操作前,调用`session.clear()`方法清除整个Session缓存,确保没有重复对象[^4]。但要注意,这会清除所有未保存的更改,因此需谨慎使用。 - 示例代码: ```java session.clear(); // 清除整个Session缓存 session.update(object); // 然后更新对象 ``` 2. **使用`merge()`代替`update()`**: - `merge()`方法会检查Session中是否已存在相同ID的对象。如果存在,它会将传入对象的状态复制到已存在的对象上,并返回已存在的对象;如果不存在,则保存传入对象[^2]。 - 示例代码: ```java Object mergedObj = session.merge(object); // 使用merge合并对象状态 ``` 3. **确保操作唯一对象**: - 避免在同一个Session中多次加载同一个对象。例如,在更新前,先检查Session中是否已存在该对象(通过`session.get()`或`session.load()`),然后直接修改已存在的对象,而不是新建一个对象[^2][^5]。 - 示例代码: ```java // 先查询Session中是否已有该对象 MyEntity existingObj = session.get(MyEntity.class, id); if (existingObj != null) { // 直接修改existingObj的属性 existingObj.setProperty(newValue); } else { // 处理对象不存在的情况 } ``` 4. **正确管理对象状态**: - 在SSH(Struts+Spring+Hibernate)框架中,确保在更新操作前,对象是从当前Session中获取的,而不是从其他Session或缓存中获取的[^4]。 5. **使用`saveOrUpdate()`的注意事项**: - 如果使用`saveOrUpdate()`,确保传入的对象在Session中不存在相同ID的对象。否则,也会触发此异常。 ### 总结 `NonUniqueObjectException`的核心是Session中出现了相同ID的多个对象实例。解决思路是确保在同一个Session中,每个持久化对象的ID唯一。优先推荐使用`merge()`方法,或直接操作Session中已存在的对象。如果问题仍存在,可考虑清除Session缓存(但需注意副作用)。 §§相关问题§§ 1. Hibernate中`merge()`和`update()`方法有什么区别? 2. 如何避免Hibernate Session中出现重复对象? 3. Hibernate一级缓存的工作原理是什么? 4. 在Spring Data JPA中如何处理类似异常?</think>### Hibernate NonUniqueObjectException 解决方法 `NonUniqueObjectException` 是 Hibernate 中常见的异常,错误信息 `a different object with the same identifier value was already associated with the session` 表示**同一个 Hibernate Session 中存在两个具有相同 ID 但不同状态的对象实例**。以下是原因分析和解决方案: #### 原因分析 1. **重复对象冲突** 当 Session 缓存中已存在 ID 为 X 的对象 A,又尝试将另一个 ID 为 X 的对象 B 关联到同一个 Session 时触发异常[^2][^4]。 例如: ```java User user1 = session.get(User.class, 1); // 对象A已加载到Session User user2 = new User(1, "NewName"); // 新建对象B(相同ID) session.saveOrUpdate(user2); // 触发异常! ``` 2. **常见场景** - 从不同来源(如不同 Session 或 JSON 反序列化)获取相同 ID 的对象 - 级联操作时父子对象引用不一致[^3] - 手动实例化持久化对象而非通过 Hibernate 查询获取 3. **一级缓存机制** Hibernate Session 的一级缓存(Identity Map)要求每个 Session 中**同一持久化类每个 ID 只能有一个对象实例**[^4]。 #### 解决方案 1. **优先使用 `merge()` 替代 `update()`** `merge()` 会检查 Session 中是否存在相同 ID 的对象: ```java // 正确方式 User mergedUser = session.merge(user2); // 将user2状态合并到缓存对象 ``` - 若缓存存在对象:复制属性到缓存对象并返回它 - 若不存在:保存新对象并返回 2. **清除 Session 缓存** 在更新前清除整个 Session 缓存(慎用,会丢失所有未提交更改)[^4]: ```java session.clear(); // 清除所有缓存对象 session.update(user2); // 重新关联 ``` 3. **查询替换实例化** 避免手动 `new` 持久化对象,改为从当前 Session 查询: ```java // 错误方式 User user = new User(1, "Name"); session.update(user); // 正确方式 User user = session.get(User.class, 1); // 从缓存或数据库获取 user.setName("NewName"); // 直接修改 ``` 4. **级联操作检查** 确保关联对象来自同一个 Session: ```java Order order = session.get(Order.class, 100); // 错误:从外部获取关联对象 order.setUser(externalUser); // 正确:从当前Session获取关联对象 order.setUser(session.get(User.class, externalUser.getId())); ``` 5. **使用 `saveOrUpdate()` 的注意事项** 确保对象状态明确: ```java if (session.contains(user)) { // 检查对象是否在Session中 session.update(user); } else { session.saveOrUpdate(user); } ``` #### 最佳实践 1. **统一对象来源** 始终通过 `session.get()` 或 HQL 查询获取对象,避免混用 `new` 和持久化操作。 2. **事务边界控制** 在事务内保持 Session 操作原子性,避免跨事务传递持久化对象。 3. **DTO 转换策略** 接收前端数据时,使用 DTO 接收参数,再通过 ID 查询出持久化对象进行赋值: ```java UserDTO dto = ... // 前端传入的数据 User persistentUser = session.get(User.class, dto.getId()); persistentUser.setName(dto.getName()); // 仅更新属性 ``` > **关键点**:Hibernate 通过对象引用跟踪状态,任何相同 ID 的对象在 Session 中必须指向同一内存地址[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值