Introduction
When working with relational databases in Spring JPA, you’ll often need to insert data into multiple related tables—for example, creating a new User
along with a corresponding Transaction
(like a default “SEND MONEY” record).
If you’ve been manually setting foreign keys or saving entities separately, this guide will help you do it the Spring JPA way using proper entity relationships, cascade operations, and best practices for transactional integrity.
Why Your Current Approach Might Be Failing
If you’re trying to set user_id
manually in the Transaction
table without a defined JPA relationship, JPA won’t know how to manage the foreign key or ensure consistency. This leads to:
- Foreign key errors
- Redundant save operations
- Data integrity issues
Step 1: Define the Entity Relationship
To make Spring JPA handle inserts across tables automatically, model a bidirectional one-to-many relationship between User
and Transaction
.
User Entity:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Other fields like name, email, etc.
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<Transaction> transactions = new HashSet<>();
public void addTransaction(Transaction transaction) {
transactions.add(transaction);
transaction.setUser(this); // Set reverse link
}
// Getters and setters
}
Transaction Entity:
@Entity
@Table(name = "transaction")
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idTrans;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
private String type;
private Timestamp transactionDate;
private int transMoney;
// Getters and setters
}
Important Notes:
@OneToMany
withcascade = CascadeType.ALL
ensures all child transactions are saved with the parent.- The
addTransaction()
method ensures bidirectional integrity.
Step 2: Create the Repositories
Use Spring Data JPA interfaces:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface TransactionRepository extends JpaRepository<Transaction, Long> {}
Step 3: Build the Service Layer
Create both entities in one go inside a service method. No need to call transactionRepository.save()
separately.
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public User createUserWithTransaction(UserRequest userRequest) {
User user = new User();
user.setName(userRequest.getName());
user.setEmail(userRequest.getEmail());
Transaction transaction = new Transaction();
transaction.setType("SEND MONEY");
transaction.setTransactionDate(new Timestamp(System.currentTimeMillis()));
transaction.setTransMoney(userRequest.getInitialMoney());
user.addTransaction(transaction); // Associate transaction
return userRepository.save(user); // Persist both entities
}
}
Step 4: Create the REST Controller
Create a simple endpoint to accept user data and trigger the creation process:
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody UserRequest request) {
User savedUser = userService.createUserWithTransaction(request);
return ResponseEntity.ok(savedUser);
}
}
Common Pitfalls & How to Fix Them
LazyInitializationException
:
Wrap your service methods with@Transactional
to ensure lazy-loaded fields are accessible.- Cascading Doesn’t Work:
EnsureCascadeType.ALL
is specified on the@OneToMany
mapping. - Missing Reverse Mapping:
Always update both sides of the relationship. Usinguser.addTransaction()
handles this. - JSON Infinite Recursion:
Use@JsonIgnore
on theuser
field inTransaction
or manage with@JsonManagedReference
/@JsonBackReference
.
Best Practices
✅ Use DTOs (like UserRequest
) to decouple entity logic from API inputs/outputs
✅ Annotate service methods with @Transactional
for atomic operations
✅ Initialize collections (like Set<Transaction>
) to avoid NullPointerException
✅ Avoid manual foreign key assignments—let JPA manage it
✅ Use meaningful names and relationships to reflect your domain logic
Keywords:
Spring Data JPA insert multiple tables, one-to-many relationship, JPA cascade persist, save parent and child entity, bidirectional mapping JPA, Spring Boot transactional insert, JPA entity association
Conclusion
Inserting data into multiple tables in Spring JPA doesn’t need to be messy. With well-defined entity relationships, cascading, and proper service layering, you can persist parent-child records like User
and Transaction
in one smooth operation—cleanly and reliably.
Want to explore more? Check out the official Spring Data JPA documentation or our REST API design guide for additional insights. 🚀
Let me know if you’d like to turn this into a downloadable PDF, add diagram illustrations, or generate a sample project template!