Save() Vs Saveandflush()
These 2 are basically the same, the only difference is when it comes when it's being applied in a @Transactional
block.
This is because save()
is marked as @Transactional
. And by default, jpa will automatically flush after a @Transactional
block. Therefore when calling save()
on its own it's also flush()
When in a @Transactional
block
Difference really comes when we have a @Transactional
in our code for example
With save()
@Transactional
public void myTransactionFunction() {
Customer customer = new Customer();
customer.setName("Austin")
customerRepository.save(customer);
String sqlQuery = "SELECT * FROM customer WHERE customer.id = ?";
List<Customer> customers = jdbcTemplate.query(sqlQuery, new Object[]{customer.getId()}, new CustomerRowMapper());
if (!customers.isEmpty()) {
System.out.println("Customer found with id: " + customers.get(0).getId());
} else {
System.out.println("Customer not found with id: " + customer.getId());
}
}
class CustomerRowMapper implements RowMapper<Customer> {
@Override
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setId(rs.getLong("id"));
customer.setName(rs.getString("name"));
// Map other columns as needed
return customer;
}
}
In this example, we do a database query using select, the result would be Customer not found with id:
. This is because since we annotated with @Transactional
, the transaction now applied at the outer level outside of save()
and hence, save()
would not auto-flush anymore.
[!important]
In here if we callcustomerRepository.findById(customer.id)
it would still return the valid customer. This is becausecustomerRepository
is cached and not represent the actual state of the database. However theselect * from customer
actually query the database and return the actual state of the database.
With saveAndFlush()
However the same example if we use saveAndFlush()
we would be able to see the customer:
@Transactional
public void myTransactionFunction() {
Customer customer = new Customer();
customer.setName("Austin")
customerRepository.saveAndFlush(customer);
String sqlQuery = "SELECT * FROM customer WHERE customer.id = ?";
List<Customer> customers = jdbcTemplate.query(sqlQuery, new Object[]{customer.getId()}, new CustomerRowMapper());
if (!customers.isEmpty()) {
System.out.println("Customer found with id: " + customers.get(0).getId());
} else {
System.out.println("Customer not found with id: " + customer.getId());
}
}
class CustomerRowMapper implements RowMapper<Customer> {
@Override
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setId(rs.getLong("id"));
customer.setName(rs.getString("name"));
// Map other columns as needed
return customer;
}
}
As a result, we have Customer found with id:
this is because saveAndFlush()
actually write into the database. Since this is a @Transactional
block, auto-flush has not been started in this block but and saveAndFlush()
will flush it immediately.
[!important]
Since this changes has not been commited. Only this transactional when query the database can see the changes. When another thread query the database (or another application talk to the database in the same time), the changes are not visible.
When NOT in a @Transactional
block
When not in a transactional block. Since by default, save()
is transactional on its own and we have auto-flush from JPA after a transactional. save()
is equivalent to saveAndFlush()
@Transactional
public void myTransactionFunction() {
Customer customer = new Customer();
customer.setName("Austin")
customerRepository.save(customer);
String sqlQuery = "SELECT * FROM customer WHERE customer.id = ?";
List<Customer> customers = jdbcTemplate.query(sqlQuery, new Object[]{customer.getId()}, new CustomerRowMapper());
if (!customers.isEmpty()) {
System.out.println("Customer found with id: " + customers.get(0).getId());
} else {
System.out.println("Customer not found with id: " + customer.getId());
}
}
class CustomerRowMapper implements RowMapper<Customer> {
@Override
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setId(rs.getLong("id"));
customer.setName(rs.getString("name"));
// Map other columns as needed
return customer;
}
}
Will also return Customer found with id:
here.