spring-framework
spring-framework copied to clipboard
Add `updateReturning`, `batchUpdateReturning` and `batchUpdateReturningStream` methods to `NamedParameterJdbcTemplate`
Many databases now support UPDATE/INSERT/DELETE ... RETURNING *
which enables a single database request to apply and return in a single database call, this allows the caller to see the values for generated fields such as ID's, default values and potentially data which is populated via triggers BEFORE INSERT OR UPDATE ...
or in my case I have a batch insert that uses ON CONFLICT DO NOTHING RETURNING *
which allows me to not fail the entire batch when some rows conflict and manage the conflicts manually.
Currently I see a lot of implementations which use an insert and then a select separately where the implementation needs to take care to use @Transactional
.
The functionality of updateReturning
can be achieved using the query
methods however I would argue that this is counter intuitive.
Both batchUpdateReturning
and batchUpdateReturningStream
are a little more complicated to workaround with the currently available query methods but they are possible (see: this Stack Overflow answer) but the solution has some drawbacks.
Example of the workaround I've been using
public Set<ExternalId> markSeen(Collection<ExternalId> externalIds) {
List<Object[]> values = externalIds.stream()
// Object[] must be in order of the columns defined for the insert
.map(externalId -> new Object[] {externalId.getStringId(), externalId.getDomain()})
.toList();
// insert batch returning everything that didn't conflict
Set<ExternalId> unseenIds = namedParameterJdbcTemplate.query("""
insert into seen_documents (external_id, domain)
values :idAndDomain
on conflict do nothing
returning external_id, domain
""",
new MapSqlParameterSource("idAndDomain", values),
resultExtractor);
// return values that didn't get inserted
return externalIds.stream()
.filter(not(unseenIds::contains))
.collect(Collectors.toUnmodifiableSet());
}
I'm happy to add the functionality to the APIs if others think this is worth doing.