Hibernate 中对象 set 属性时自动保存入库的问题

本文探讨了Hibernate中对象的不同状态及其对数据自动保存的影响,提供了避免非预期数据更改的方法,包括使用瞬时状态对象和将托管对象转为游离状态。

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

背景

在某个项目中,通过 Heibernate 查出某个对象后,对属性进行了 set 操作,并没有进行 update 操作,但是在数据中自动保存了。

显然这并不是我想要的,我只是想 set 一下属性,在前端显示,并不想入库,但是 Heibernate 自动保存了。

// 查出对象,原来的 name 为 user1
User user = userManager.findOne(1);
// 将userName 设置为 user2 ,用于前端显示,并没有保存
user.setName("user2");
// heibernate 自动进行了保存,库中的 name 为 user2

原因

Heibernate 中对象分为以下几种状态:

瞬时状态:

实际上就是 new 了一个普通的 JavaBean 对象。

托管状态:
  • 当瞬时对象调用了管理器的 persist() 后,即可将一般的 JavaBean 做为了持久 Bean,该 Bean 的任何属性改动都会牵涉到数据库记录的改动。

  • 一旦该记录 flush 到数据库之后,并且事务提交了,那么此对象不在持久化上下文中,即:变为了游离(没人管的孩子)状态了。在游离状态的时候调用更新、刷新方法后,游离状态对象就变为了在持久化上下文的托管状态了。

  • 通过管理器的 find 方法,将实体从数据库查询出来后,该实体也就变为了托管形态。

持久化状态:

当处在托管状态的实体 Bean 被管理器 flush 了,那么就在极短暂的时间进入了持久化状态,事务提交之后,立刻变为了游离状态。您可以把持久化状态当做实实在在的数据库记录。

游离状态:

游离状态就是提交到数据库后,事务 commit 后实体的状态,因为事务已经提交了,此时实体的属性任你如何改变,也不会同步到数据库,因为游离是没人管的孩子,不在持久化上下文中。

销毁对象:

一般要删除一个持久化对象的时候都是先 find 出来,之后调用 remove 方法删之,此时这个对象就是销毁对象,实际上就是瞬时对象的另一种形态罢了。

根据上面的各种状态,可以得知。通过 find 查出某个对象后,就进入了托管状态,此时对对象的任何属性改动,都会自动保存到数据库中。

解决方法

  1. 通过 new 一个新的对象出来进行操作,这个新的对象是瞬时状态,不会自动保存。
// 查出对象
User user = userManager.findOne(1);
// new 一个新的对象
User newUser = new User();
// 将需要用到的属性赋予新的对象,对于前端显示
newUser.setAge(user.getAge());
newUser.setName("user2");
// 不要对 newUser 进行保存即可。也不要改变 user 的属性
  1. 在 find 查出的对象 set 属性前,将其状态改为游离状态。这样的话会用到 session 的几个方法:close、clear、evict。

    • close 方法:关闭 session 这样这个对象肯定是游离态了,因为 session 已经关闭了,但是往往我们实际的开发过程中,session 在后面是要用的到的,所以这个方法可行,但是不一定用得上,分清具体的情况。

    • clear 方法:将 session 中的所有的对象全部清除出缓存,这个方式有点劳师动众,不过 session 清除了全部的对象之后自然就会变为游离态了,这样做不是很好吧我感觉。

    • evict 方法:将某一个对象清除出缓存 session,这个方法是很好的实现方式,推荐使用。调用的时候是这样的,session.evict(Object obj) 这样就可以了。

    这边推荐使用 evict 方法,代码如下:

    @PersistenceContext
    private EntityManager entityManager;
    
    ......
    
    // 获取 session
    HibernateEntityManager hEntityManager = (HibernateEntityManager)entityManager;
    Session session = hEntityManager.getSession();
    // 查出对象,原来的 name 为 user1
    User user = userManager.findOne(1);
    // 将对象清除出缓存 session,这样后面对属性的改变就不会自动保存
    session.evict(user);
    // 将userName 设置为 user2 ,用于前端显示
    user.setName("user2");
    // 由于清除出了 session,heibernate 不会对这个对象进行保存,库中的 name 仍为 user1
    

总结

通过 Hibernate 查询出来的对象,改变其属性后,Hibernate 会对其自动保存入库。

如果不需要使用这个功能,可以通过 new 一个新的对象来进行操作,或者将对象改成游离态。

<think>嗯,用户的问题是关于POJO类属性没有初始值的原因,以及这样设计的意义。我需要先理解POJO是什么,然后分析为什么属性不设置默认值会有这样的效果。 首先,POJO代表普通的Java对象,通常不继承特定框架的类或实现特定接口。它们的属性通常由getter和setter方法访问。用户提到属性没有初始值是为了提醒使用者必须显式赋值,这样任何NPE问题入库检查都由使用者负责。 那为什么这样做呢?比如,如果一个String类型的属性默认初始化为null,如果使用者忘记赋值,可能在后续使用中抛出NullPointerException。而如果不初始化,开发者在使用前必须显式设置值,否则编译可能不会报错,但运行会出问题。这促使开发者更谨慎地处理这些属性,确保它们在需要有正确的值。 另外,数据库操作,如果字段不能为null,而POJO属性未初始化,存入数据可能会触发约束错误。这候使用者必须检查数据有效性,避免插入无效数据。这也是一种设计上的考虑,强制要求使用者处理这些情况,而不是依赖默认值可能带来的隐藏问题。 不过,这样的设计也可能带来问题,比如使用者可能忘记赋值,导致运行错误。所以有些情况下,开发者会给属性合理的默认值,比如空字符串或空集合,而不是null。这需要根据具体场景权衡。 总结一下,POJO不设初始值的主要目的是让使用者明确处理赋值的责任,减少因默认值导致的潜在错误。但需要权衡便利性和安全性,根据项目需求决定是否初始化属性。</think><< POJO(Plain Old Java Object)类的属性不赋予初始值的主要目的是为了将数据完整性和正确性的责任明确交给类的使用者。这种设计模式强调以下几个关键点: ### 1. **明确赋值责任** - **原因**:如果属性没有默认值,使用者在构造对象或调用方法,必须显式地为这些属性赋值。这避免了因依赖默认值而导致的潜在逻辑错误。 - **示例**: ```java public class User { private String name; // 未初始化,默认为null private int age; // 未初始化,默认为0(基本类型) // Getter & Setter } ``` 如果使用者未显式调用`setName()`,直接调用`getName()`会返回`null`,可能导致NPE。 ### 2. **强制数据校验** - **原因**:未初始化的属性会迫使使用者在数据入库或业务逻辑处理前进行显式校验,例如通过`@NotNull`注解或手动检查。 - **示例**: ```java public void saveUser(User user) { if (user.getName() == null) { throw new IllegalArgumentException("Name cannot be null"); } // 入库操作 } ``` ### 3. **避免隐式默认值的误导** - **原因**:基本类型如`int`默认值为0,引用类型如`String`默认值为`null`。这些默认值可能与业务逻辑的合法值冲突(例如年龄为0可能无效)。 - **对比**: ```java // 错误示例:隐式默认值导致逻辑漏洞 public class Product { private double price = 0.0; // 默认值可能被误认为合法值 } ``` ### 4. **框架和库的兼容性** - **原因**:许多框架(如Hibernate、Jackson)依赖无参构造器和未初始化的属性,通过反射动赋值。显式初始值可能干扰这些框架的行为。 ### 何应赋予默认值? 如果属性有明确的业务默认值(例如订单状默认为“未支付”),则应显式初始化: ```java public class Order { private String status = "UNPAID"; // 显式业务默认值 } ``` ### 总结 POJO属性不设初始值是一种防御性编程策略,要求开发者主动管理数据的生命周期,从而减少因默认值导致的隐蔽错误。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值