Skip to content

Commit 7bb2beb

Browse files
author
mhyeon-lee
committed
[DATAJDBC-493] Add findOneWithLock for DataAccessStrategy
1 parent 0b5e918 commit 7bb2beb

File tree

7 files changed

+124
-10
lines changed

7 files changed

+124
-10
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.data.domain.Sort;
2525
import org.springframework.data.mapping.PersistentPropertyPath;
2626
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
27+
import org.springframework.data.relational.core.sql.LockMode;
2728

2829
/**
2930
* Delegates each methods to the {@link DataAccessStrategy}s passed to the constructor in turn until the first that does
@@ -33,6 +34,7 @@
3334
* @author Mark Paluch
3435
* @author Tyler Van Gorder
3536
* @author Milan Milanov
37+
* @author Myeonghyeon Lee
3638
* @since 1.1
3739
*/
3840
public class CascadingDataAccessStrategy implements DataAccessStrategy {
@@ -133,6 +135,15 @@ public <T> T findById(Object id, Class<T> domainType) {
133135
return collect(das -> das.findById(id, domainType));
134136
}
135137

138+
/*
139+
* (non-Javadoc)
140+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
141+
*/
142+
@Override
143+
public <T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType) {
144+
return collect(das -> das.findByIdWithLock(id, lockMode, domainType));
145+
}
146+
136147
/*
137148
* (non-Javadoc)
138149
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
2424
import org.springframework.data.mapping.PersistentPropertyPath;
2525
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
26+
import org.springframework.data.relational.core.sql.LockMode;
2627
import org.springframework.lang.Nullable;
2728

2829
/**
@@ -33,6 +34,7 @@
3334
* @author Jens Schauder
3435
* @author Tyler Van Gorder
3536
* @author Milan Milanov
37+
* @author Myeonghyeon Lee
3638
*/
3739
public interface DataAccessStrategy extends RelationResolver {
3840

@@ -148,6 +150,18 @@ public interface DataAccessStrategy extends RelationResolver {
148150
@Nullable
149151
<T> T findById(Object id, Class<T> domainType);
150152

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+
151165
/**
152166
* Loads all entities of the given type.
153167
*

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4242
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
4343
import org.springframework.data.relational.core.sql.IdentifierProcessing;
44+
import org.springframework.data.relational.core.sql.LockMode;
4445
import org.springframework.data.relational.core.sql.SqlIdentifier;
4546
import org.springframework.jdbc.core.RowMapper;
4647
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -62,6 +63,7 @@
6263
* @author Tom Hombergs
6364
* @author Tyler Van Gorder
6465
* @author Milan Milanov
66+
* @author Myeonghyeon Lee
6567
* @since 1.1
6668
*/
6769
public class DefaultDataAccessStrategy implements DataAccessStrategy {
@@ -269,6 +271,24 @@ public <T> T findById(Object id, Class<T> domainType) {
269271
}
270272
}
271273

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+
272292
/*
273293
* (non-Javadoc)
274294
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.springframework.data.domain.Sort;
2020
import org.springframework.data.mapping.PersistentPropertyPath;
2121
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
22+
import org.springframework.data.relational.core.sql.LockMode;
2223
import org.springframework.util.Assert;
2324

2425
/**
@@ -28,6 +29,7 @@
2829
* @author Jens Schauder
2930
* @author Tyler Van Gorder
3031
* @author Milan Milanov
32+
* @author Myeonghyeon Lee
3133
* @since 1.1
3234
*/
3335
public class DelegatingDataAccessStrategy implements DataAccessStrategy {
@@ -128,6 +130,18 @@ public <T> T findById(Object id, Class<T> domainType) {
128130
return delegate.findById(id, domainType);
129131
}
130132

133+
/*
134+
* (non-Javadoc)
135+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
136+
*/
137+
@Override
138+
public <T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType) {
139+
140+
Assert.notNull(delegate, "Delegate is null");
141+
142+
return delegate.findByIdWithLock(id, lockMode, domainType);
143+
}
144+
131145
/*
132146
* (non-Javadoc)
133147
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
* @author Tom Hombergs
5353
* @author Tyler Van Gorder
5454
* @author Milan Milanov
55+
* @author Myeonghyeon Lee
5556
*/
5657
class SqlGenerator {
5758

@@ -257,6 +258,16 @@ String getFindOne() {
257258
return findOneSql.get();
258259
}
259260

261+
/**
262+
* Create a {@code SELECT … FROM … WHERE :id = … (LOCK CLAUSE)} statement.
263+
*
264+
* @param lockMode Lock clause mode.
265+
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
266+
*/
267+
String getFindOneWithLock(LockMode lockMode) {
268+
return this.createFindOneWithLockSql(lockMode);
269+
}
270+
260271
/**
261272
* Create a {@code INSERT INTO … (…) VALUES(…)} statement.
262273
*
@@ -358,6 +369,15 @@ private String createFindOneSql() {
358369
return render(select);
359370
}
360371

372+
private String createFindOneWithLockSql(LockMode lockMode) {
373+
374+
Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
375+
.lock(lockMode)
376+
.build();
377+
378+
return render(select);
379+
}
380+
361381
private String createFindAllSql() {
362382
return render(selectBuilder().build());
363383
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
4242
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
4343
import org.springframework.data.relational.core.sql.IdentifierProcessing;
44+
import org.springframework.data.relational.core.sql.LockMode;
4445
import org.springframework.data.relational.core.sql.SqlIdentifier;
4546
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
4647
import org.springframework.util.Assert;
@@ -60,6 +61,7 @@
6061
* @author Mark Paluch
6162
* @author Tyler Van Gorder
6263
* @author Milan Milanov
64+
* @author Myeonghyeon Lee
6365
*/
6466
public class MyBatisDataAccessStrategy implements DataAccessStrategy {
6567

@@ -260,6 +262,17 @@ public <T> T findById(Object id, Class<T> domainType) {
260262
return sqlSession().selectOne(statement, parameter);
261263
}
262264

265+
/*
266+
* (non-Javadoc)
267+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, org.springframework.data.relational.core.sql.LockMode, java.lang.Class)
268+
*/
269+
@Override
270+
public <T> T findByIdWithLock(Object id, LockMode lockMode, Class<T> domainType) {
271+
String statement = namespace(domainType) + ".findByIdWithLock";
272+
MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
273+
return sqlSession().selectOne(statement, parameter);
274+
}
275+
263276
/*
264277
* (non-Javadoc)
265278
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4747
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
4848
import org.springframework.data.relational.core.sql.Aliased;
49+
import org.springframework.data.relational.core.sql.LockMode;
4950
import org.springframework.data.relational.core.sql.SqlIdentifier;
5051
import org.springframework.data.relational.core.sql.Table;
5152

@@ -59,6 +60,7 @@
5960
* @author Mark Paluch
6061
* @author Tom Hombergs
6162
* @author Milan Milanov
63+
* @author Myeonghyeon Lee
6264
*/
6365
public class SqlGeneratorUnitTests {
6466

@@ -95,16 +97,36 @@ public void findOne() {
9597

9698
SoftAssertions softAssertions = new SoftAssertions();
9799
softAssertions.assertThat(sql) //
98-
.startsWith("SELECT") //
99-
.contains("dummy_entity.id1 AS id1,") //
100-
.contains("dummy_entity.x_name AS x_name,") //
101-
.contains("dummy_entity.x_other AS x_other,") //
102-
.contains("ref.x_l1id AS ref_x_l1id") //
103-
.contains("ref.x_content AS ref_x_content").contains(" FROM dummy_entity") //
104-
.contains("ON ref.dummy_entity = dummy_entity.id1") //
105-
.contains("WHERE dummy_entity.id1 = :id") //
106-
// 1-N relationships do not get loaded via join
107-
.doesNotContain("Element AS elements");
100+
.startsWith("SELECT") //
101+
.contains("dummy_entity.id1 AS id1,") //
102+
.contains("dummy_entity.x_name AS x_name,") //
103+
.contains("dummy_entity.x_other AS x_other,") //
104+
.contains("ref.x_l1id AS ref_x_l1id") //
105+
.contains("ref.x_content AS ref_x_content").contains(" FROM dummy_entity") //
106+
.contains("ON ref.dummy_entity = dummy_entity.id1") //
107+
.contains("WHERE dummy_entity.id1 = :id") //
108+
// 1-N relationships do not get loaded via join
109+
.doesNotContain("Element AS elements");
110+
softAssertions.assertAll();
111+
}
112+
113+
@Test // DATAJDBC-493
114+
public void findOneWithLock() {
115+
116+
String sql = sqlGenerator.getFindOneWithLock(LockMode.PESSIMISTIC_WRITE);
117+
118+
SoftAssertions softAssertions = new SoftAssertions();
119+
softAssertions.assertThat(sql) //
120+
.startsWith("SELECT") //
121+
.contains("dummy_entity.id1 AS id1,") //
122+
.contains("dummy_entity.x_name AS x_name,") //
123+
.contains("dummy_entity.x_other AS x_other,") //
124+
.contains("ref.x_l1id AS ref_x_l1id") //
125+
.contains("ref.x_content AS ref_x_content").contains(" FROM dummy_entity") //
126+
.contains("ON ref.dummy_entity = dummy_entity.id1") //
127+
.contains("WHERE dummy_entity.id1 = :id") //
128+
.contains("FOR UPDATE") //
129+
.doesNotContain("Element AS elements");
108130
softAssertions.assertAll();
109131
}
110132

0 commit comments

Comments
 (0)