Hibernate 先get 一个实体数据,在修改数据,为新的数据,再add到数据库,需要注意什么?

本文讨论了在使用Spring框架时,从数据库获取对象后直接修改并尝试保存可能导致的意外数据库更新问题。通过实例演示了如何通过克隆对象来解决这一问题,并总结了避免此类问题的方法。

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

1、今天在做一个很简单的save操作的是,定位我一个小时,特在此废话下,现在从数据库中通过接口get到数据,代码如下

 // 获取当前流程数据
        TaskRunData taskRunDataOriginal = this.taskRunDataService.get(new Long(runTaskId));

然后对taskRunDataOriginal 进行修改些字段值,并且把主键设置为null了,这样就新增一条数据了。以为几秒的事,然后兴高采烈的调用save,

但是save的时候却出现,主键不能设置为空的异常,Ok我又重新new了一个TaskRunData 对象,把taskRunDataOriginal 中的数据重新构建了一下,这样我想估计Ok了吧。结果是OK了,新增了一条数据,但是多了一条SQL,就是执行update,把一开始获取到的数据更新了,也就是数据库中出现俩条一模一样的数据了(只是主键不一样)。

我这个郁闷啊。赶紧的google了下,没有发现啥,后来一想估计是一开始的 old对象是在session中取得的,所以这个对象已经和数据库同步了,或者说相关联了,那么修改,在spring事务提交,也会同时提交这个数据吧。ok,我就为TaskRunData这个JavaBean写了个clone方法,我先clone一个,然后再修改clone过来的数据,测试了一下,终于OK了。哎哎。教训啊

总结:从 getHibernateTemplate().get(this.persistType, id);中get到的数据,不要直接修改它的值,这样spring事务提交的时候,会update初始的数据。

如果要修改,请修改clone过来的数据


<think>我们讨论的是在添加数据时,如何动态判断数据库中是否已存在该数据。这通常发生在需要避免重复数据的场景,比如插入新用户时检查用户名或邮箱是否已存在。由于是动态的,我们可能不知道具体要判断哪些字段,所以需要根据实体类型和提供的字段动态生成查询。假设我们使用关系型数据库(如MySQL,PostgreSQL)和ORM(如Hibernate,SQLAlchemy)或直接使用SQL。这里我将给出两种常见情况的解决方案:1.使用ORM(以SQLAlchemy为例,Python)2.使用原始SQL(以Python的SQLite为例,但原理通用)核心思路:-我们需要知道实体的类型(即表名)和要检查的字段(一个或多个)以及对应的值。-然后构造一个查询,检查数据库中是否存在具有相同字段值的记录。注意:对于复合唯一性检查,可能需要同时判断多个字段。方案一:使用SQLAlchemy(ORM)假设我们有一个模型类`User`,我们想要在插入新用户之前检查`email`是否已经存在。但是,问题要求是动态的,即我们可能传入任意实体类型和任意字段。因此,我们需要一个通用函数。步骤:1.获取模型类(实体类型)对应的表。2.根据传入的字段和值构建查询条件。3.执行查询,判断是否存在。示例代码(SQLAlchemy):</think>### 动态数据实体类型判断数据库重复数据的解决方案 在添加数据时动态判断数据库是否存在重复数据,核心在于**动态构建查询条件**和**处理实体类型不确定性**。以下是两种通用解决方案: --- #### 方案一:使用 JPA 动态条件查询 (Java/Spring Boot) ```java import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; import javax.persistence.criteria.Predicate; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @Service public class EntityService<T, ID> { private final JpaRepository<T, ID> repository; public EntityService(JpaRepository<T, ID> repository) { this.repository = repository; } // 动态检查实体是否存在 public boolean exists(T entity) throws IllegalAccessException { // 1. 获取实体类型 Class<?> entityClass = entity.getClass(); // 2. 动态构建查询条件 return repository.exists((root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); // 3. 遍历实体所有字段 for (Field field : entityClass.getDeclaredFields()) { field.setAccessible(true); Object value = field.get(entity); // 4. 仅处理非空字段 if (value != null) { predicates.add(cb.equal(root.get(field.getName()), value)); } } // 5. 组合所有条件用 AND 连接 return cb.and(predicates.toArray(new Predicate[0])); }); } // 保存前检查重复 public T saveWithCheck(T entity) throws IllegalAccessException { if (exists(entity)) { throw new RuntimeException("数据已存在"); } return repository.save(entity); } } ``` **代码解释:** 1. **动态获取实体类型**:通过 `entity.getClass()` 获取运行时类型 2. **反射遍历字段**:扫描实体所有字段获取字段名和值 3. **条件动态构建**:对每个非空字段生成 `field = value` 查询条件 4. **AND 组合条件**:所有字段同时匹配才视为重复 5. **安全保存**:`saveWithCheck` 方法在保存前进行存在性检查 **使用示例:** ```java // 用户实体 @Entity public class User { @Id private Long id; private String email; private String phone; } // 使用服务 User newUser = new User("test@example.com", "13800138000"); entityService.saveWithCheck(newUser); // 自动检查email+phone组合 ``` --- #### 方案二:通用 SQL 动态构建 (Python/SQLAlchemy) ```python from sqlalchemy import create_engine, Table, MetaData from sqlalchemy.sql import select import pandas as pd def check_entity_exists(engine, table_name, entity_dict): """ :param engine: SQLAlchemy 引擎 :param table_name: 数据库表名 :param entity_dict: 实体字段字典 {字段: 值} """ metadata = MetaData() # 1. 动态反射数据库表 table = Table(table_name, metadata, autoload_with=engine) # 2. 构建动态WHERE条件 conditions = [] for column_name, value in entity_dict.items(): if value is not None: # 3. 添加非空字段条件 conditions.append(table.c[column_name] == value) # 4. 组合查询条件 if not conditions: return False query = select(table).where(*conditions) # 5. 执行查询并检查 with engine.connect() as conn: result = conn.execute(query) return result.fetchone() is not None # 使用示例 engine = create_engine("sqlite:///mydb.db") user_data = {"email": "test@example.com", "phone": "13800138000"} if check_entity_exists(engine, "users", user_data): print("用户已存在") else: print("可以安全创建") ``` **代码关键点:** 1. **表反射**:`autoload_with` 动态加载表结构 2. **条件生成**:遍历实体字典构建 `column=value` 条件 3. **智能过滤**:跳过 `None` 值字段避免无效条件 4. **存在性验证**:通过 `fetchone()` 检测匹配记录 --- ### 关键处理逻辑 1. **动态字段处理** - 使用反射获取实体字段 - 只处理非空字段(避免 `null=null` 无效查询) - 支持未知实体类型 2. **组合条件策略** ```mermaid graph LR A[获取实体字段] --> B{值是否非空?} B -->|是| C[生成查询条件] B -->|否| D[跳过字段] C --> E[组合所有条件] E --> F[执行AND查询] ``` 3. **边界情况处理** - 所有字段为空时直接返回不存在 - 处理数据库大小写敏感问题(可添加字段级配置) - 避免 ID 字段参与重复检查 --- ### 性能优化建议 1. **索引配置**:为常用查重字段添加数据库索引 2. **字段白名单**:指定参与查重的字段(避免全字段扫描) ```java // 在方法中添加字段过滤 List<String> checkFields = Arrays.asList("email", "phone"); if (checkFields.contains(field.getName())) { // 处理字段... } ``` 3. **批处理优化**:批量插入时使用 `WHERE NOT EXISTS` 语句 --- ### 备选方案:数据库唯一约束 对于固定业务规则,更推荐数据库层解决方案: ```sql -- 创建联合唯一约束 ALTER TABLE users ADD CONSTRAINT unique_user UNIQUE (email, phone); ``` **优点**:绝对数据一致性,高并发安全 **缺点**:无法动态调整检查规则 ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值