

统一声明:
1.本站联系方式QQ:709466365 TG:@UXWNET 官方TG频道:@UXW_NET 如果有其他人通过本站链接联系您导致被骗,本站一律不负责! 2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET 3.免实名域名注册购买- 游侠云域名 4.免实名国外服务器购买- 游侠网云服务
这篇文章把Hibernate修改数据的实战逻辑扒得明明白白:从实体类映射配置、Session获取,到update与merge方法的区别(到底什么时候用哪个?),再到“查询-修改-提交”的完整流程(附可直接复用的代码示例),每一步都讲得够细。更关键的是,我们把新手常踩的“忘记提交事务”“缓存未同步”“游离对象处理”等8个坑点单独拎出来,每个坑都说明“为什么会踩”“怎么快速解决”。
不管你是刚学Hibernate的小白,还是遇到修改问题卡壳的开发者,跟着走一遍实操步骤,再避开这些高频坑,就能轻松搞定数据修改,不用再翻零散文档试错。
你有没有过这种情况?用Hibernate写修改数据的代码,控制台显示没报错,但数据库里的数据就是没变?或者明明对象都拿到了,一调用update就抛“ObjectNotFoundException”?我去年帮刚入行的小周调过这个问题——他写了一堆代码,结果忘了开事务,白折腾3小时。今天我把自己踩过的雷、 的实操步骤全抖出来,你跟着做,保证能避开90%的坑,顺顺利利修改数据。
Hibernate修改数据的实战步骤:我自己用了3年的流程
我从做第一个Hibernate项目开始,就一直用这套流程,没翻过车——不管是改用户信息、订单状态还是商品库存,都能顺顺利利。你跟着一步一步来,绝对不会错。
第一步,先把实体类和映射配置好。这是基础中的基础,要是映射错了,后面全白搭。比如我要改用户表的数据,先写个User实体类:用@Entity注解标注重实体,@Id标注重键,@Column标注字段对应数据库的列名。举个例子:
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name") // 对应数据库的user_name字段
private String name;
private Integer age;
// getter和setter省略
}
要是你用xml映射文件,就写个user.hbm.xml
,里面配置标签对应表,对应主键,对应字段。我之前试过用xml,后来觉得注解更方便,但不管用哪种,一定要保证实体类的字段和数据库表对应上——我之前漏了@Column(name = "user_name")
,结果修改name字段时,数据库里的user_name
字段没变化,查了半天才发现是映射名错了。
第二步,获取Session并开启事务。Hibernate的Session就像你和数据库之间的“桥梁”,所有操作都得通过它。获取Session的方式很简单:先建SessionFactory(一般是单例,因为创建成本高),然后用SessionFactory.openSession()
拿到Session。接下来一定要开事务——Hibernate官方文档明确说过:“所有写操作(插入、修改、删除)都需要活跃的事务”。我之前没开事务,改了数据根本没提交到数据库,白忙一场。代码大概是这样的:
// 创建SessionFactory(单例,一般在项目启动时初始化)
SessionFactory sessionFactory = new Configuration()
.configure("hibernate.cfg.xml") // 加载配置文件
.buildSessionFactory();
// 获取Session
Session session = sessionFactory.openSession();
// 开启事务(关键!没这步改了也白改)
Transaction tx = session.beginTransaction();
第三步,查询要修改的对象。你得先拿到要改的对象,才能修改它的属性。我 用session.get()
方法——比如要改id为1的用户,就写User user = session.get(User.class, 1L)
。为什么不用session.load()
?因为get是“立即加载”,不管对象在不在,都会立马去数据库查,返回null或者对象;而load是“延迟加载”,只有用到对象的属性时才会查数据库,要是对象不存在,直接抛“ObjectNotFoundException”。我之前用load()踩过坑:要改的用户id不存在,结果代码直接报错,换成get()就返回null,我可以提前判断if (user != null)
,避免报错。
第四步,修改对象的属性。拿到对象后,直接改它的属性就行——比如user.setName("张三")
、user.setAge(25)
。这一步很简单,但要注意:只要对象还在Session的缓存里(也就是还没close),Hibernate会自动跟踪这些修改。我之前以为要调用什么方法通知Hibernate“我改了属性”,后来才知道,根本不用——Hibernate的“一级缓存”(Session缓存)会盯着对象的变化,等你提交事务时,自动把修改同步到数据库。
第五步,提交事务并关闭Session。改完属性后,一定要提交事务:tx.commit()
。要是忘了这一步,就算你改了属性,数据也不会写到数据库里——小周就是因为这个,白折腾3小时。提交完事务,别忘了关闭Session:session.close()
,不然会占用数据库连接资源。完整的代码流程大概是这样的:
// 省略SessionFactory和Session获取、事务开启
User user = session.get(User.class, 1L);
if (user != null) {
user.setName("新名字"); // 修改属性
user.setAge(30); // 修改属性
}
tx.commit(); // 提交事务(关键!)
session.close(); // 关闭Session
这里我要插个表格,帮你分清update
和merge
这两个常用方法——很多人搞不清什么时候用哪个,我之前也混淆过,后来查了官方文档, 了区别:
方法名 | 适用场景 | Session关联要求 | 注意事项 |
---|---|---|---|
update | 修改游离态对象(脱离Session管理的对象) | Session中不能有相同id的对象 | 若Session已有相同对象,抛NonUniqueObjectException |
merge | 修改游离态对象,或Session中已有相同对象 | 无要求,自动合并属性 | 返回合并后的对象,原对象不会被修改 |
举个例子:如果你有个游离态的User对象(比如从前端传过来的,只有id和name),想修改它的属性,用merge()
更稳妥——比如:
User detachedUser = new User();
detachedUser.setId(1L); // 已有id
detachedUser.setName("新名字");
session.merge(detachedUser); // 合并属性到Session中的对象
要是用update()
,如果Session中已经有id为1的User对象,就会报错;而merge()
会自动把游离对象的属性复制到Session中的对象里,不会报错。我之前用update踩过这个坑,换成merge就好了。
8个高频坑点避坑:我踩过的雷帮你绕开
光会步骤还不够,我把自己踩过的8个坑点整理出来,每个坑都告诉你“为什么会踩”“怎么解决”,帮你绕开。
第一个坑:忘记开启或提交事务。我之前写demo的时候,光顾着写修改代码,没加Transaction tx = session.beginTransaction()
,也没加tx.commit()
,结果数据库里的数据根本没变化。为什么?因为Hibernate默认不自动提交事务,所有写操作都需要在事务里进行。解决办法很简单:不管做什么修改,先开事务,改完立马提交。
第二个坑:Session缓存未同步。有时候你修改了对象属性,但因为缓存没刷新,数据库里的数据没更新。比如你改了user.setName()
,但还没提交事务,这时候用session.flush()
可以强制Hibernate把缓存里的修改同步到数据库。我之前遇到过,修改后想立刻查数据库看结果,结果没刷新缓存,查出来还是旧数据,调用flush()
就好了。
第三个坑:用load()
方法查询对象导致异常。我之前用session.load(User.class, 1L)
查对象,结果id为1的用户不存在,直接抛“ObjectNotFoundException”。而用session.get()
的话,会返回null,我可以提前判断if (user != null)
再修改,避免报错。所以 优先用get()
方法。
第四个坑:修改主键字段。Hibernate不允许修改实体类的主键——比如你想user.setId(2L)
,然后提交事务,肯定会报错。为什么?因为主键是唯一标识对象的,修改主键相当于创建了一个新对象,而不是修改旧对象。解决办法:永远不要修改主键字段,要是真的要改,就删了旧对象,插入新对象。
第五个坑:游离对象调用update()
导致异常。游离对象就是已经脱离Session管理的对象(比如Session已经close了,或者对象是new出来的,没关联到Session)。要是你对游离对象调用update()
,而Session中已经有相同id的对象,就会抛“NonUniqueObjectException”。解决办法:用merge()
方法代替update()
,merge会合并属性,不会报错。我之前遇到过这个问题,把update改成merge就好了。
第六个坑:字段未映射。我之前给User实体类加了个email
字段,忘了在注解里加@Column
,结果修改email的时候,数据库里的email
字段没变化。为什么?因为Hibernate不知道这个字段对应数据库的哪个列,所以不会同步修改。解决办法:不管用注解还是xml,一定要把实体类的所有字段都映射到数据库表。
第七个坑:事务回滚没处理。要是修改过程中抛了异常,比如NullPointerException
,你得在catch块里调用tx.rollback()
,不然事务会一直挂着,占用数据库资源。我之前没加回滚,结果数据库连接池满了,其他请求都进不来,查了半天才发现是事务没回滚。代码大概是这样的:
try {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = session.get(User.class, 1L);
if (user != null) {
user.setName("新名字");
}
tx.commit();
} catch (Exception e) {
if (tx != null && tx.isActive()) {
tx.rollback(); // 回滚事务
}
e.printStackTrace();
} finally {
if (session != null && session.isOpen()) {
session.close(); // 关闭Session
}
}
第八个坑:使用detached对象直接修改。detached对象就是已经脱离Session的对象(比如Session已经close了)。要是你直接修改这个对象的属性,再调用update()
,就会抛异常。解决办法:要么重新关联Session(用session.update()
),要么用merge()
方法。我之前有个detached对象,直接修改后调用update,结果报错,换成merge就好了。
要是你按这些方法试了,或者遇到其他问题,欢迎回来告诉我效果!我当初学Hibernate的时候,也是踩了一堆坑才摸透这些规律,现在把经验传给你,希望你能少走点弯路。
用Hibernate改了数据,控制台没报错但数据库没变化,这是怎么回事?
大概率是没开事务或者没提交事务!Hibernate默认不自动提交事务,所有写操作(修改、插入、删除)都得在事务里进行。你得先调用session.beginTransaction()开启事务,改完数据后再调用tx.commit()提交事务,不然再对的代码也不会把修改同步到数据库。我之前帮小周调bug时,他就是漏了提交事务,白折腾3小时。
还有种可能是映射配置错了,比如实体类字段没对应数据库列(比如漏加@Column注解),这时候Hibernate根本不知道要改哪个字段,也会导致数据库没变化,得检查实体类和映射文件的对应关系。
Hibernate里update和merge方法有啥区别?什么时候用哪个?
简单说,两者都是改数据,但适用场景不一样:update适合修改“游离态对象”(比如从前端传过来的、只有id和要改属性的对象),但得保证当前Session里没有相同id的对象——要是Session里已经有个一样id的对象,调用update就会报错;merge更稳妥,不管Session里有没有相同对象,都会自动把游离对象的属性合并到Session中的对象里,还不会报错,返回的是合并后的对象,原对象不会被修改。
我自己的习惯是,除非很确定Session里没有相同对象,否则优先用merge(),毕竟不容易出错。比如你有个从前端传过来的User对象(只有id和新名字),直接调用session.merge(user)就行,不用纠结Session里有没有相同的User。
用load()查对象修改时抛ObjectNotFoundException,怎么解决?
这是因为load()是“延迟加载”——它不会立刻去数据库查对象,只有用到对象属性时才会查,要是对象不存在(比如id错了),就会直接抛ObjectNotFoundException。想解决的话,换成session.get()方法就行,get()是“立即加载”,会立刻去数据库查,对象不存在会返回null,你可以提前判断if(user != null)再修改,就不会报错了。
我之前用load()踩过这个坑,改数据时突然抛异常,查了半天才发现是id对应的用户不存在,换成get()后,提前判断null,直接给用户返回“数据不存在”的提示,比报错友好多了。
修改脱离Session的detached对象(比如Session关了的对象)时报错,该怎么办?
detached对象就是已经脱离Session管理的对象——比如你从Session里查出来一个User对象,然后调用session.close()关了Session,这时候这个User就是detached对象。要是直接调用session.update(detachedUser),如果当前Session里已经有个相同id的User对象,就会报错。
解决办法有两个:一是用merge()方法,它会自动把detached对象的属性合并到Session中的对象里,不用管Session里有没有相同对象;二是确保当前Session里没有相同id的对象,再调用update()重新关联Session。我自己更爱用merge(),因为不用记那么多条件,稳妥。
修改数据后立刻查数据库还是旧数据,这是怎么回事?
这是Hibernate的“一级缓存”(Session缓存)在搞鬼!你修改了对象属性后,Hibernate会先把修改存在缓存里,没立刻同步到数据库。要是你想立刻看数据库结果,得调用session.flush()强制把缓存里的修改同步到数据库——或者直接提交事务(tx.commit()会自动触发flush)。
我之前改完用户名字,立刻去数据库查,结果还是旧名字,以为代码错了,后来才知道是没flush缓存,调用session.flush()后再查,就显示新名字了。记住,commit()会自动flush,所以要是你改完数据已经提交了事务,就不用再手动flush了。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
站长QQ:709466365 站长邮箱:709466365@qq.com