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

本文探讨了在Hibernate中查询对象并修改其属性时,Hibernate自动保存修改的问题。介绍了Hibernate对象的瞬时、托管、持久化和游离状态,推荐使用evict方法使对象脱离会话,避免自动保存。

背景

在某个项目中,通过 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 查出某个对象后,就进入了托管状态,此时对对象的任何属性改动,都会自动保存到数据库中。

解决方法

通过 new 一个新的对象出来进行操作,这个新的对象是瞬时状态,不会自动保存。


// 查出对象
User user = userManager.findOne(1);
// new 一个新的对象
User newUser = new User();
// 将需要用到的属性赋予新的对象,对于前端显示
newUser.setAge(user.getAge());
newUser.setName("user2");
// 不要对 newUser 进行保存即可。也不要改变 user 的属性



在 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 一个新的对象来进行操作,或者将对象改成游离态。
--------------------- 
版权声明:本文为优快云博主「yujunyi_」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qqfo24/article/details/83275610

<think>首先,用户的问题是:“我想查找使用Java解析JSON文件并将数据保存数据库的方法 请问Java解析JSON文件并保存数据库的实现方法”。这直接询问如何实现JSON文件解析并保存数据库。 我需要参考系统指令: - 所有行内数学表达式使用$...$格式(但这里没有数学表达式,所以可能不适用)。 - 独立公式用$$...$$并单独成段(类似地,不适用)。 - LaTeX语法正确(确保任何LaTeX正确)。 - 使用中文回答。 - 生成相关问题。 - 在引用段落末尾添加引用标识,如[^1]。 另外,系统指令说:“回答中引用的段落末尾自然地添加引用标识”。用户提供了引用[1]到[5],我需要参考这些内容来回覆。 用户提供了引用: - [^1]: 关于Java导出数据数据到JSON文件。 - [^2]: Java解析JSON文件并保存到MySQL数据库。 - [^3]: 似乎部分相关,但更多是关于字符串存储的讨论。 - [^4]: Java解析JSON文件并保存到MySQL数据库,提供了实体类代码。 - [^5]: 解析JSON格式文件并保存数据库。 用户的引用中,[^2]、[^4]和[^5]直接相关,因为它们提到解析JSON并保存数据库。[^1]是关于导出到JSON,而不是导入。[^3]似乎无关。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 用户上一次的问题是相同的吗?查看上下文,用户之前的问题似乎是直接询问这个方法,所以没有上一个问题和回答。所以,我应该基于这些引用回答。 我的回答需要结构化清晰,帮助用户逐步解决问题,保证真实可靠。 回答结构: 1. **介绍**:简述问题和方法。 2. **步骤分解**:逐步解释如何实现。 - 解析JSON文件。 - 映射到Java对象。 - 保存数据库。 3. **源代码示例**:提供代码片段,基于引用中的信息。[^4]有实体类代码,[^2]提到解析JSON。 4. **引用添加**:在引用的内容末尾添加引用标识。 5. **生成相关问题**:在回答末尾添加相关问题。 核心实现步骤: - **解析JSON**:使用Jackson、Gson等JSON库。 - **映射到对象**:创建实体类(如[^4]中的CityBean)。 - **保存数据库**:使用JDBC或ORM框架如Hibernate。 从引用中提取信息: - [^4]提供了实体类代码:CityBean with id, province, city, district. - [^5]给出了一个JSON示例:{"xuenian":"2007-2008","xueqi":"1",...} - [^2]提到解析JSON并保存到MySQL。 我应该整合这些。 使用中文回答。 最后,生成相关问题。 完整回答大纲: 1. **开头**:介绍解析JSON并保存数据库的整体方法。 2. **步骤1:添加依赖** – 如Jackson或Gson库。 3. **步骤2:创建实体类** – 参考[^4]的CityBean,但一般化。 4. **步骤3:解析JSON文件** – 读取文件并使用解析器转换。 5. **步骤4:保存数据库** – 使用JDBC插入数据。 6. **源代码示例** – 提供简单示例代码。 7. **结尾** – 总结。 8. **相关问题** – 生成几个相关问题。 在引用相关部分添加[^n]。 例如,当提到实体类,引用[^4]。 生成的相关问题: 1. 如何处理JSON文件的复杂结构? 2. 使用ORM框架如Hibernate如何实现? 3. 常见的解析错误及解决方法? 确保回答真实可靠:基于标准Java实践。 现在,编写回答。 </think>### Java解析JSON文件并保存数据库的实现方法 在Java应用中,解析JSON文件并将数据保存数据库是一个常见需求。整个过程可分为四个主要步骤:(1)添加JSON解析库依赖;(2)创建Java实体类映射JSON结构;(3)解析JSON文件;(4)将数据保存数据库。我将逐步解释每个步骤,并提供完整的源代码示例。实现中使用Jackson库解析JSON(流行且高效),并基于JDBC连接MySQL数据库(其它数据库类似)。所有步骤均参考用户提供的引用内容,确保方法真实可靠。 #### 步骤1: 添加JSON解析库依赖 首先,项目中需要引入JSON解析库。推荐使用Jackson或Gson库。例如,在Maven项目的`pom.xml`中添加Jackson依赖: ```xml <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version> <!-- 使用最新稳定版本 --> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <!-- MySQL驱动 --> </dependency> ``` Jackson库能高效处理JSON解析和反序列化到Java对象[^2][^4],而MySQL驱动用于数据库连接。 #### 步骤2: 创建Java实体类 定义一个实体类映射JSON结构。JSON文件通常包含键值对,需创建对应Java类。参考引用[^4]中的实体类实现,但这里将其通用化。例如,假设您的JSON文件结构类似: ```json { "result": [ {"id": 1, "province": "北京", "city": "北京市", "district": "朝阳区"}, {"id": 2, "province": "上海", "city": "上海市", "district": "浦东新区"} ] } ``` 创建实体类`DataEntity`: ```java package com.example.model; // 包名根据项目调整 public class DataEntity { private int id; private String province; private String city; private String district; // 无参构造器(Jackson解析必需) public DataEntity() {} // 带参构造器(可选) public DataEntity(int id, String province, String city, String district) { this.id = id; this.province = province; this.city = city; this.district = district; } // Getter和Setter方法(Jackson依赖这些方法映射JSON) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getDistrict() { return district; } public void setDistrict(String district) { this.district = district; } @Override public String toString() { return "DataEntity{" + "id=" + id + ", province='" + province + '\'' + ", city='" + city + '\'' + ", district='" + district + '\'' + '}'; } } ``` 此实体类通过Getter/Setter方法映射JSON键,Jackson会自动处理字段绑定[^4]。如果JSON结构更复杂(如嵌套对象),需定义额外类。 #### 步骤3: 解析JSON文件 使用Jackson解析JSON文件并反序列化为Java对象列表。创建一个工具类`JsonParser`: ```java package com.example.util; import com.example.model.DataEntity; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.core.type.TypeReference; import java.io.File; import java.util.List; public class JsonParser { public List<DataEntity> parseJson(String filePath) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); // Jackson的核心解析器 // 解析JSON文件为List<DataEntity>(假设JSON顶层是对象,包含"result"数组) // 如果JSON结构类似[^5]的直接数组,可直接用objectMapper.readValue(file, new TypeReference<List<DataEntity>>(){}) JsonRoot root = objectMapper.readValue(new File(filePath), JsonRoot.class); return root.getResult(); // 获取嵌套的数据列表 } // 辅助类映射JSON顶层结构(假设JSON有"result"键) static class JsonRoot { private List<DataEntity> result; public List<DataEntity> getResult() { return result; } public void setResult(List<DataEntity> result) { this.result = result; } } } ``` 这里,`ObjectMapper`读取JSON文件并转换为Java对象。处理嵌套JSON,创建辅助类(如`JsonRoot`)映射顶层结构[^2][^5]。 #### 步骤4: 保存数据数据库 使用JDBC将Java对象列表插入数据库。创建`DatabaseSaver`类: ```java package com.example.dao; import com.example.model.DataEntity; import java.sql.*; import java.util.List; public class DatabaseSaver { private static final String JDBC_URL = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC"; private static final String USER = "root"; private static final String PASSWORD = "your_password"; public void saveToDatabase(List<DataEntity> dataList) throws SQLException { String sql = "INSERT INTO data_table (id, province, city, district) VALUES (?, ?, ?, ?)"; try (Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); PreparedStatement stmt = conn.prepareStatement(sql)) { for (DataEntity data : dataList) { stmt.setInt(1, data.getId()); stmt.setString(2, data.getProvince()); stmt.setString(3, data.getCity()); stmt.setString(4, data.getDistrict()); stmt.addBatch(); // 批量添加提高性能 } stmt.executeBatch(); // 执行批量插入 System.out.println("数据保存成功,插入记录数: " + dataList.size()); } } } ``` 此代码使用JDBC的批量插入优化性能。确保数据库表结构匹配实体类字段(例如,表`data_table`有`id`, `province`, `city`, `district`列)。 #### 完整示例:整合所有步骤 在主类中调用上述组件: ```java package com.example; import com.example.model.DataEntity; import com.example.util.JsonParser; import com.example.dao.DatabaseSaver; import java.util.List; public class Main { public static void main(String[] args) { try { JsonParser parser = new JsonParser(); List<DataEntity> dataList = parser.parseJson("path/to/your/data.json"); // JSON文件路径 DatabaseSaver saver = new DatabaseSaver(); saver.saveToDatabase(dataList); System.out.println("JSON数据导入数据库完成!"); } catch (Exception e) { e.printStackTrace(); System.err.println("处理失败: " + e.getMessage()); } } } ``` 运行此程序,JSON文件中的数据会被批量导入数据库。 #### 注意事项 - **JSON结构适配**:如果JSON文件结构不同(如引用[^5]的简单对象或引用[^2]的嵌套键),需调整实体类和解析逻辑。使用Jackson的`@JsonProperty`注解处理字段名不匹配。 - **错误处理**:添加异常处理(如`IOException`, `SQLException`)确保健壮性。 - **性能优化**:对于大文件,使用Jackson流式API(`JsonParser`)避免内存溢出;或分批次保存数据库[^2][^4]。 - **数据库连接**:生产环境应使用连接池(如HikariCP)替代直接`DriverManager`。 此方法高效可靠,已在多个项目中应用[^2][^4][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值