EntityManager阻塞数据库触发器,JPA锁新记录导致触发器未激活如何解决
JPA新增记录时数据库触发器无法触发问题解决方案
问题根因说明
你遇到的触发器未正常执行、customerCode无法赋值的核心原因有3点:
- 实体字段配置错误:customerCode字段未标注数据库生成属性,JPA会将int类型的默认值0写入insert语句,覆盖触发器赋值逻辑
- 代码逻辑错误:新增Customer时ModelMapper映射的源对象写反,导致插入的Customer字段全部为默认值
- 持久上下文缓存问题:saveAndFlush后直接调用findById会命中JPA一级缓存,返回未同步触发器更新值的旧实体
修复方案
1. 修正Customer实体配置
给触发器维护的字段添加对应的JPA注解,明确告知JPA该字段的维护逻辑:
@Entity public class Customer { @Id // 如果主键是数据库自增,补充生成策略注解 @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private int id; @Column private String name; @Column private String address; @Column private int personRef; // 标注该字段insert、update时不由JPA维护,插入后自动回查数据库值 @Generated(GenerationTime.INSERT) @Column(insertable = false, updatable = false) private int customerCode; //db trigger updated }
2. 修正Service层逻辑
- 修正ModelMapper映射的源对象错误
- 先保存Person拿到id再关联Customer,减少刷库次数
- 插入后主动刷新实体获取触发器更新的值
@Service public class CustomerService{ @Autowired private final CustomerRepository customerRepository; @Autowired private final PersonService personService; // 注入EntityManager用于刷新实体 @Autowired private EntityManager entityManager; @Transactional(rollbackFor = {Exception.class}) public int control(CustomerDTO customerDTO){ Customer customer = customerRepository.findById(customerDTO.getId()).orElse(null); if(customer == null){ ModelMapper mapper = new ModelMapper(); // 修正映射源对象,从DTO转成实体 customer = mapper.map(customerDTO,Customer.class); // 先保存Person拿到主键 Person person = mapper.map(customerDTO.getPerson(),Person.class); personService.saveAndFlush(person); // 给Customer赋值personRef customer.setPersonRef(person.getId()); // 保存Customer并刷库,触发数据库insert触发器 customerRepository.saveAndFlush(customer); // 主动刷新实体,同步触发器更新的customerCode字段 entityManager.refresh(customer); return customer.getCustomerCode(); } return customer.getCustomerCode(); } }
3. 特殊场景兼容(如果确实存在行锁阻塞触发器)
部分数据库的行锁机制会导致同事务内触发器无法操作当前插入的行,可调整事务传播策略:
- 将Customer的保存逻辑单独拆分到一个新的事务方法,标注
@Transactional(propagation = Propagation.REQUIRES_NEW) - 子事务提交后释放行锁,再在主事务中执行后续操作
内容的提问来源于stack exchange,提问作者rasit
相关产品推荐
相关产品推荐

