游侠网云服务,免实名免备案服务器 游侠云域名,免实名免备案域名

统一声明:

1.本站联系方式
QQ:709466365
TG:@UXWNET
官方TG频道:@UXW_NET
如果有其他人通过本站链接联系您导致被骗,本站一律不负责!

2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET
3.免实名域名注册购买- 游侠云域名
4.免实名国外服务器购买- 游侠网云服务
Hibernate修改数据实例详解:超详细实战步骤+避坑指南

这篇文章把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

这里我要插个表格,帮你分清updatemerge这两个常用方法——很多人搞不清什么时候用哪个,我之前也混淆过,后来查了官方文档, 了区别:

方法名 适用场景 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了。