Skip to content

Commit a2c0e31

Browse files
author
mhyeon-lee
committed
DATAJDBC-493 Add DbAction AcquireLockRoot, AcquireLockAllRoot for delete execution.
1 parent 64bf8af commit a2c0e31

File tree

14 files changed

+348
-99
lines changed

14 files changed

+348
-99
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/AggregateChangeExecutor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* Executes an {@link MutableAggregateChange}.
2828
*
2929
* @author Jens Schauder
30+
* @author Myeonghyeon Lee
3031
* @since 2.0
3132
*/
3233
class AggregateChangeExecutor {
@@ -77,6 +78,10 @@ private void execute(DbAction<?> action, JdbcAggregateChangeExecutionContext exe
7778
executionContext.executeDeleteRoot((DbAction.DeleteRoot<?>) action);
7879
} else if (action instanceof DbAction.DeleteAllRoot) {
7980
executionContext.executeDeleteAllRoot((DbAction.DeleteAllRoot<?>) action);
81+
} else if (action instanceof DbAction.AcquireLockRoot) {
82+
executionContext.executeAcquireLock((DbAction.AcquireLockRoot<?>) action);
83+
} else if (action instanceof DbAction.AcquireLockAllRoot) {
84+
executionContext.executeAcquireLockAllRoot((DbAction.AcquireLockAllRoot<?>) action);
8085
} else {
8186
throw new RuntimeException("unexpected action");
8287
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@
4242
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
4343
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4444
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
45+
import org.springframework.data.relational.core.sql.LockMode;
4546
import org.springframework.data.util.Pair;
4647
import org.springframework.lang.Nullable;
4748
import org.springframework.util.Assert;
4849

4950
/**
5051
* @author Jens Schauder
5152
* @author Umut Erturk
53+
* @author Myeonghyeon Lee
5254
*/
5355
class JdbcAggregateChangeExecutionContext {
5456

@@ -164,6 +166,14 @@ <T> void executeMerge(DbAction.Merge<T> merge) {
164166
}
165167
}
166168

169+
<T> void executeAcquireLock(DbAction.AcquireLockRoot<T> acquireLock) {
170+
accessStrategy.acquireLockById(acquireLock.getId(), LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
171+
}
172+
173+
<T> void executeAcquireLockAllRoot(DbAction.AcquireLockAllRoot<T> acquireLock) {
174+
accessStrategy.acquireLockAll(LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
175+
}
176+
167177
private void add(DbActionExecutionResult result) {
168178
results.put(result.getAction(), result);
169179
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@
3939
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
4040
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4141
import org.springframework.data.relational.core.mapping.event.*;
42-
import org.springframework.data.relational.core.sql.LockMode;
4342
import org.springframework.lang.Nullable;
44-
import org.springframework.transaction.support.TransactionSynchronizationManager;
4543
import org.springframework.util.Assert;
4644

4745
/**
@@ -355,9 +353,6 @@ private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType)
355353
entity = triggerBeforeDelete(entity, id, change);
356354
change.setEntity(entity);
357355

358-
// [DATAJDBC-493] Acquire Lock to avoid DeadLock
359-
this.acquireLockIfActualTransactionActive(id, domainType);
360-
361356
executor.execute(change);
362357

363358
triggerAfterDelete(entity, id, change);
@@ -447,10 +442,4 @@ private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, MutableA
447442

448443
return null;
449444
}
450-
451-
private <T> void acquireLockIfActualTransactionActive(Object id, Class<T> domainType) {
452-
if (TransactionSynchronizationManager.isActualTransactionActive()) {
453-
this.accessStrategy.findByIdWithLock(id, LockMode.PESSIMISTIC_WRITE, domainType);
454-
}
455-
}
456445
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,29 +119,38 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
119119

120120
/*
121121
* (non-Javadoc)
122-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class)
122+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#acquireLockById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
123123
*/
124124
@Override
125-
public long count(Class<?> domainType) {
126-
return collect(das -> das.count(domainType));
125+
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
126+
collectVoid(das -> das.acquireLockById(id, lockMode, domainType));
127127
}
128128

129129
/*
130130
* (non-Javadoc)
131-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class)
131+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#acquireLockAll(org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
132132
*/
133133
@Override
134-
public <T> T findById(Object id, Class<T> domainType) {
135-
return collect(das -> das.findById(id, domainType));
134+
public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
135+
collectVoid(das -> das.acquireLockAll(lockMode, domainType));
136+
}
137+
138+
/*
139+
* (non-Javadoc)
140+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class)
141+
*/
142+
@Override
143+
public long count(Class<?> domainType) {
144+
return collect(das -> das.count(domainType));
136145
}
137146

138147
/*
139148
* (non-Javadoc)
140-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
149+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class)
141150
*/
142151
@Override
143-
public <T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType) {
144-
return collect(das -> das.findByIdWithLock(id, lockMode, domainType));
152+
public <T> T findById(Object id, Class<T> domainType) {
153+
return collect(das -> das.findById(id, domainType));
145154
}
146155

147156
/*

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,23 @@ public interface DataAccessStrategy extends RelationResolver {
131131
*/
132132
void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath);
133133

134+
/**
135+
* Acquire Lock
136+
*
137+
* @param id the id of the entity to load. Must not be {@code null}.
138+
* @param lockMode the lock mode for select. Must not be {@code null}.
139+
* @param domainType the domain type of the entity. Must not be {@code null}.
140+
*/
141+
<T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType);
142+
143+
/**
144+
* Acquire Lock entities of the given domain type.
145+
*
146+
* @param lockMode the lock mode for select. Must not be {@code null}.
147+
* @param domainType the domain type of the entity. Must not be {@code null}.
148+
*/
149+
<T> void acquireLockAll(LockMode lockMode, Class<T> domainType);
150+
134151
/**
135152
* Counts the rows in the table representing the given domain type.
136153
*
@@ -150,18 +167,6 @@ public interface DataAccessStrategy extends RelationResolver {
150167
@Nullable
151168
<T> T findById(Object id, Class<T> domainType);
152169

153-
/**
154-
* Loads a single entity identified by type and id with lock.
155-
*
156-
* @param id the id of the entity to load. Must not be {@code null}.
157-
* @param lockMode the lock mode for select. Must not be {@code null}.
158-
* @param domainType the domain type of the entity. Must not be {@code null}.
159-
* @param <T> the type of the entity.
160-
* @return Might return {@code null}.
161-
*/
162-
@Nullable
163-
<T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType);
164-
165170
/**
166171
* Loads all entities of the given type.
167172
*

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,16 @@
1818
import static org.springframework.data.jdbc.core.convert.SqlGenerator.*;
1919

2020
import java.sql.JDBCType;
21+
import java.sql.ResultSet;
22+
import java.sql.SQLException;
2123
import java.util.ArrayList;
2224
import java.util.Collections;
2325
import java.util.HashSet;
2426
import java.util.List;
2527
import java.util.Map;
2628
import java.util.function.Predicate;
2729

28-
import org.springframework.dao.DataRetrievalFailureException;
29-
import org.springframework.dao.EmptyResultDataAccessException;
30-
import org.springframework.dao.InvalidDataAccessApiUsageException;
31-
import org.springframework.dao.OptimisticLockingFailureException;
30+
import org.springframework.dao.*;
3231
import org.springframework.data.domain.Pageable;
3332
import org.springframework.data.domain.Sort;
3433
import org.springframework.data.jdbc.support.JdbcUtil;
@@ -43,6 +42,7 @@
4342
import org.springframework.data.relational.core.sql.IdentifierProcessing;
4443
import org.springframework.data.relational.core.sql.LockMode;
4544
import org.springframework.data.relational.core.sql.SqlIdentifier;
45+
import org.springframework.jdbc.core.ResultSetExtractor;
4646
import org.springframework.jdbc.core.RowMapper;
4747
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
4848
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
@@ -239,6 +239,27 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
239239
.update(sql(propertyPath.getBaseProperty().getOwner().getType()).createDeleteAllSql(propertyPath));
240240
}
241241

242+
/*
243+
* (non-Javadoc)
244+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#acquireLockById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
245+
*/
246+
@Override
247+
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
248+
String acquireLockByIdSql = sql(domainType).getAcquireLockById(lockMode);
249+
SqlIdentifierParameterSource parameter = createIdParameterSource(id, domainType);
250+
operations.queryForObject(acquireLockByIdSql, parameter, Object.class);
251+
}
252+
253+
/*
254+
* (non-Javadoc)
255+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#acquireLockAll(org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
256+
*/
257+
@Override
258+
public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
259+
String acquireLockAllSql = sql(domainType).getAcquireLockAll(lockMode);
260+
operations.query(acquireLockAllSql, Collections.emptyMap(), new NoMappingResultSetExtractor());
261+
}
262+
242263
/*
243264
* (non-Javadoc)
244265
* @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class)
@@ -271,24 +292,6 @@ public <T> T findById(Object id, Class<T> domainType) {
271292
}
272293
}
273294

274-
/*
275-
* (non-Javadoc)
276-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
277-
*/
278-
@Override
279-
@SuppressWarnings("unchecked")
280-
public <T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType) {
281-
282-
String findOneWithLockSql = sql(domainType).getFindOneWithLock(lockMode);
283-
SqlIdentifierParameterSource parameter = createIdParameterSource(id, domainType);
284-
285-
try {
286-
return operations.queryForObject(findOneWithLockSql, parameter, (RowMapper<T>) getEntityRowMapper(domainType));
287-
} catch (EmptyResultDataAccessException e) {
288-
return null;
289-
}
290-
}
291-
292295
/*
293296
* (non-Javadoc)
294297
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)
@@ -602,4 +605,14 @@ public T getBean() {
602605
return null;
603606
}
604607
}
608+
609+
/**
610+
* The type No mapping result set extractor.
611+
*/
612+
static class NoMappingResultSetExtractor implements ResultSetExtractor<Object> {
613+
@Override
614+
public Object extractData(ResultSet resultSet) throws SQLException, DataAccessException {
615+
return null;
616+
}
617+
}
605618
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,35 +111,41 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
111111

112112
/*
113113
* (non-Javadoc)
114-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class)
114+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#acquireLockById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
115115
*/
116116
@Override
117-
public long count(Class<?> domainType) {
118-
return delegate.count(domainType);
117+
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
118+
delegate.acquireLockById(id, lockMode, domainType);
119119
}
120120

121121
/*
122122
* (non-Javadoc)
123-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class)
123+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#acquireLockAll(org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
124124
*/
125125
@Override
126-
public <T> T findById(Object id, Class<T> domainType) {
127-
128-
Assert.notNull(delegate, "Delegate is null");
126+
public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
127+
delegate.acquireLockAll(lockMode, domainType);
128+
}
129129

130-
return delegate.findById(id, domainType);
130+
/*
131+
* (non-Javadoc)
132+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class)
133+
*/
134+
@Override
135+
public long count(Class<?> domainType) {
136+
return delegate.count(domainType);
131137
}
132138

133139
/*
134140
* (non-Javadoc)
135-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
141+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class)
136142
*/
137143
@Override
138-
public <T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType) {
144+
public <T> T findById(Object id, Class<T> domainType) {
139145

140146
Assert.notNull(delegate, "Delegate is null");
141147

142-
return delegate.findByIdWithLock(id, lockMode, domainType);
148+
return delegate.findById(id, domainType);
143149
}
144150

145151
/*

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,23 @@ String getFindOne() {
259259
}
260260

261261
/**
262-
* Create a {@code SELECT FROM … WHERE :id = … (LOCK CLAUSE)} statement.
262+
* Create a {@code SELECT count(id) FROM … WHERE :id = … (LOCK CLAUSE)} statement.
263263
*
264264
* @param lockMode Lock clause mode.
265265
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
266266
*/
267-
String getFindOneWithLock(LockMode lockMode) {
268-
return this.createFindOneWithLockSql(lockMode);
267+
String getAcquireLockById(LockMode lockMode) {
268+
return this.createAcquireLockById(lockMode);
269+
}
270+
271+
/**
272+
* Create a {@code SELECT count(id) FROM … (LOCK CLAUSE)} statement.
273+
*
274+
* @param lockMode Lock clause mode.
275+
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
276+
*/
277+
String getAcquireLockAll(LockMode lockMode) {
278+
return this.createAcquireLockAll(lockMode);
269279
}
270280

271281
/**
@@ -369,10 +379,28 @@ private String createFindOneSql() {
369379
return render(select);
370380
}
371381

372-
private String createFindOneWithLockSql(LockMode lockMode) {
382+
private String createAcquireLockById(LockMode lockMode) {
373383

374-
Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
375-
.lock(lockMode)
384+
Table table = this.getTable();
385+
386+
Select select = StatementBuilder //
387+
.select(getIdColumn()) //
388+
.from(table) //
389+
.where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
390+
.lock(lockMode) //
391+
.build();
392+
393+
return render(select);
394+
}
395+
396+
private String createAcquireLockAll(LockMode lockMode) {
397+
398+
Table table = this.getTable();
399+
400+
Select select = StatementBuilder //
401+
.select(getIdColumn()) //
402+
.from(table) //
403+
.lock(lockMode) //
376404
.build();
377405

378406
return render(select);

0 commit comments

Comments
 (0)