带自动生成主键的双表插入问题:Workout与Exercise关联插入
解决关联Workout和Exercise插入时的主键获取问题
嘿,这个问题我在项目里碰到过好多次,核心就是要拿到刚插入Workout时数据库自动生成的主键值,再把它绑定到对应的Exercise记录上。下面是几种靠谱的实现方式:
1. 利用ORM或数据库驱动的自动返回主键功能
这是最推荐的方式,大部分ORM框架和数据库驱动都支持在插入后自动填充主键到实体对象里。
比如用MyBatis的话,只需要在Insert Mapper里配置useGeneratedKeys和keyProperty:
<insert id="addWorkout" useGeneratedKeys="true" keyProperty="id"> INSERT INTO workout (name, created_time) VALUES (#{name}, #{createdTime}) </insert>
调用这个方法后,你传入的Workout对象的id字段就会被自动赋值为数据库生成的主键。接下来插入Exercise时,直接用这个id作为workout_id就行。
如果是原生JDBC,也可以通过PreparedStatement来获取:
String insertWorkoutSql = "INSERT INTO workout (name, created_time) VALUES (?, ?)"; PreparedStatement pstmt = connection.prepareStatement(insertWorkoutSql, Statement.RETURN_GENERATED_KEYS); pstmt.setString(1, workout.getName()); pstmt.setTimestamp(2, workout.getCreatedTime()); pstmt.executeUpdate(); ResultSet rs = pstmt.getGeneratedKeys(); if (rs.next()) { long workoutId = rs.getLong(1); // 用这个workoutId插入所有关联的Exercise }
2. 插入后立即查询数据库生成的主键
有些数据库支持直接查询当前会话最后生成的主键,比如:
- MySQL:
SELECT 640115; - PostgreSQL:
SELECT currval('workout_id_seq');(这里的workout_id_seq是Workout表主键对应的序列名) - SQL Server:
SELECT SCOPE_IDENTITY();
注意要保证这个查询和Workout的插入操作在同一个数据库连接/会话里执行,不然可能拿到其他会话生成的主键。比如在同一个事务里先执行插入,再执行查询,拿到主键后批量插入Exercise。
3. 用事务保证操作的原子性
不管用哪种方法,一定要把Workout插入和所有Exercise插入的操作放在同一个事务中。这样如果任何一步失败,整个操作都会回滚,避免出现“有Workout但没有对应Exercise”或者“有Exercise但没有对应Workout”的不一致情况。
比如在Spring Boot里,只需要给业务方法加上@Transactional注解:
@Transactional public void saveWorkoutWithExercises(Workout workout, List<Exercise> exercises) { workoutRepository.save(workout); exercises.forEach(exercise -> { exercise.setWorkoutId(workout.getId()); exerciseRepository.save(exercise); }); }
额外注意点
- 尽量不要自己生成主键(比如UUID)然后插入,除非有特殊业务需求。数据库自动生成的主键通常更高效,也能避免主键冲突问题。
- 如果要插入大量Exercise,建议用批量插入语句,比如MySQL的
INSERT INTO exercise (name, duration, workout_id) VALUES (...), (...), (...),这样能大幅提升插入性能。
内容的提问来源于stack exchange,提问作者Ben Lamb
相关产品推荐
相关产品推荐

