Introduced pessimistic locks for derived queries.
Methods which use the derive query functionality now can be annotated with `@Lock` to used a given `LockMode`. Right now there are two different modes `PESSIMISTIC_READ` and `PESSIMISTIC_WRITE`. Based on the dialect the right select is generated. For example for HSQLDB `Select ... FOR UPDATE`. See #1041 Original pull request #1158
This commit is contained in:
committed by
Jens Schauder
parent
dbf5dd56a6
commit
e68c3557c0
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,18 +30,21 @@ import org.springframework.data.relational.repository.query.RelationalParameterA
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* {@link JdbcQueryCreator} that creates {@code COUNT(*)} queries without applying limit/offset and {@link Sort}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Diego Krupitza
|
||||
* @since 2.2
|
||||
*/
|
||||
class JdbcCountQueryCreator extends JdbcQueryCreator {
|
||||
|
||||
JdbcCountQueryCreator(RelationalMappingContext context, PartTree tree, JdbcConverter converter, Dialect dialect,
|
||||
RelationalEntityMetadata<?> entityMetadata, RelationalParameterAccessor accessor, boolean isSliceQuery,
|
||||
ReturnedType returnedType) {
|
||||
super(context, tree, converter, dialect, entityMetadata, accessor, isSliceQuery, returnedType);
|
||||
ReturnedType returnedType, Optional<Lock> lockMode) {
|
||||
super(context, tree, converter, dialect, entityMetadata, accessor, isSliceQuery, returnedType, lockMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.jdbc.repository.query;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -57,6 +58,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Diego Krupitza
|
||||
* @since 2.0
|
||||
*/
|
||||
class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
|
||||
@@ -69,6 +71,7 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
|
||||
private final RenderContextFactory renderContextFactory;
|
||||
private final boolean isSliceQuery;
|
||||
private final ReturnedType returnedType;
|
||||
private final Optional<Lock> lockMode;
|
||||
|
||||
/**
|
||||
* Creates new instance of this class with the given {@link PartTree}, {@link JdbcConverter}, {@link Dialect},
|
||||
@@ -85,7 +88,7 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
|
||||
*/
|
||||
JdbcQueryCreator(RelationalMappingContext context, PartTree tree, JdbcConverter converter, Dialect dialect,
|
||||
RelationalEntityMetadata<?> entityMetadata, RelationalParameterAccessor accessor, boolean isSliceQuery,
|
||||
ReturnedType returnedType) {
|
||||
ReturnedType returnedType, Optional<Lock> lockMode) {
|
||||
super(tree, accessor);
|
||||
|
||||
Assert.notNull(converter, "JdbcConverter must not be null");
|
||||
@@ -102,6 +105,7 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
|
||||
this.renderContextFactory = new RenderContextFactory(dialect);
|
||||
this.isSliceQuery = isSliceQuery;
|
||||
this.returnedType = returnedType;
|
||||
this.lockMode = lockMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +172,12 @@ class JdbcQueryCreator extends RelationalQueryCreator<ParametrizedQuery> {
|
||||
whereBuilder);
|
||||
selectOrderBuilder = applyOrderBy(sort, entity, table, selectOrderBuilder);
|
||||
|
||||
Select select = selectOrderBuilder.build();
|
||||
SelectBuilder.BuildSelect completedBuildSelect = selectOrderBuilder;
|
||||
if (this.lockMode.isPresent()) {
|
||||
completedBuildSelect = selectOrderBuilder.lock(this.lockMode.get().value());
|
||||
}
|
||||
|
||||
Select select = completedBuildSelect.build();
|
||||
|
||||
String sql = SqlRenderer.create(renderContextFactory.createRenderContext()).render(select);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,6 +47,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Kazuki Shimizu
|
||||
* @author Moises Cisneros
|
||||
* @author Hebert Coelho
|
||||
* @author Diego Krupitza
|
||||
*/
|
||||
public class JdbcQueryMethod extends QueryMethod {
|
||||
|
||||
@@ -168,7 +169,6 @@ public class JdbcQueryMethod extends QueryMethod {
|
||||
return StringUtils.hasText(annotatedName) ? annotatedName : super.getNamedQueryName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the class to be used as {@link org.springframework.jdbc.core.RowMapper}
|
||||
*
|
||||
@@ -245,6 +245,22 @@ public class JdbcQueryMethod extends QueryMethod {
|
||||
return doFindAnnotation(Query.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return is a {@link Lock} annotation present or not.
|
||||
*/
|
||||
public boolean hasLockMode() {
|
||||
return lookupLockAnnotation().isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the {@link Lock} annotation from the query method.
|
||||
*
|
||||
* @return the {@link Optional} wrapped {@link Lock} annotation.
|
||||
*/
|
||||
Optional<Lock> lookupLockAnnotation() {
|
||||
return doFindAnnotation(Lock.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.query;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
import org.springframework.data.relational.core.sql.LockMode;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to provide a lock mode for a given query.
|
||||
*
|
||||
* @author Diego Krupitza
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@QueryAnnotation
|
||||
@Documented
|
||||
public @interface Lock {
|
||||
|
||||
/**
|
||||
* Defines which type of {@link LockMode} we want to use.
|
||||
*/
|
||||
LockMode value() default LockMode.PESSIMISTIC_READ;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,6 +50,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Diego Krupitza
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
@@ -163,7 +164,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
RelationalEntityMetadata<?> entityMetadata = getQueryMethod().getEntityInformation();
|
||||
|
||||
JdbcCountQueryCreator queryCreator = new JdbcCountQueryCreator(context, tree, converter, dialect,
|
||||
entityMetadata, accessor, false, processor.getReturnedType());
|
||||
entityMetadata, accessor, false, processor.getReturnedType(), getQueryMethod().lookupLockAnnotation());
|
||||
|
||||
ParametrizedQuery countQuery = queryCreator.createQuery(Sort.unsorted());
|
||||
Object count = singleObjectQuery((rs, i) -> rs.getLong(1)).execute(countQuery.getQuery(),
|
||||
@@ -181,7 +182,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
RelationalEntityMetadata<?> entityMetadata = getQueryMethod().getEntityInformation();
|
||||
|
||||
JdbcQueryCreator queryCreator = new JdbcQueryCreator(context, tree, converter, dialect, entityMetadata, accessor,
|
||||
getQueryMethod().isSliceQuery(), returnedType);
|
||||
getQueryMethod().isSliceQuery(), returnedType, this.getQueryMethod().lookupLockAnnotation());
|
||||
return queryCreator.createQuery(getDynamicSort(accessor));
|
||||
}
|
||||
|
||||
@@ -231,7 +232,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
private final LongSupplier countSupplier;
|
||||
|
||||
PageQueryExecution(JdbcQueryExecution<? extends Collection<T>> delegate, Pageable pageable,
|
||||
LongSupplier countSupplier) {
|
||||
LongSupplier countSupplier) {
|
||||
this.delegate = delegate;
|
||||
this.pageable = pageable;
|
||||
this.countSupplier = countSupplier;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,6 +51,7 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.jdbc.repository.query.Lock;
|
||||
import org.springframework.data.jdbc.repository.query.Modifying;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
@@ -61,6 +62,7 @@ import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
|
||||
import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
|
||||
import org.springframework.data.relational.core.sql.LockMode;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
@@ -331,6 +333,13 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(repository.findAllByNamedQuery()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByFirstnameWithLock() {
|
||||
DummyEntity dummyEntity = createDummyEntity();
|
||||
repository.save(dummyEntity);
|
||||
assertThat(repository.findAllByName(dummyEntity.getName())).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // GH-1022
|
||||
public void findAllByCustomQueryName() {
|
||||
|
||||
@@ -574,7 +583,11 @@ public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
|
||||
|
||||
@Lock(LockMode.PESSIMISTIC_WRITE)
|
||||
List<DummyEntity> findAllByName(String name);
|
||||
|
||||
List<DummyEntity> findAllByNamedQuery();
|
||||
|
||||
@Query(name = "DummyEntity.customQuery")
|
||||
List<DummyEntity> findAllByCustomNamedQuery();
|
||||
|
||||
@@ -624,6 +637,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
List<DummyEntity> findByFlagTrue();
|
||||
|
||||
List<DummyEntity> findByRef(int ref);
|
||||
|
||||
List<DummyEntity> findByRef(AggregateReference<DummyEntity, Long> ref);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.relational.core.sql.LockMode;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
@@ -41,6 +42,7 @@ import org.springframework.jdbc.core.RowMapper;
|
||||
* @author Oliver Gierke
|
||||
* @author Moises Cisneros
|
||||
* @author Mark Paluch
|
||||
* @author Diego Krupitza
|
||||
*/
|
||||
public class JdbcQueryMethodUnitTests {
|
||||
|
||||
@@ -120,6 +122,37 @@ public class JdbcQueryMethodUnitTests {
|
||||
assertThat(queryMethod.getDeclaredQuery()).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test // GH-1041
|
||||
void returnsQueryMethodWithLock() throws NoSuchMethodException {
|
||||
|
||||
JdbcQueryMethod queryMethodWithWriteLock = createJdbcQueryMethod("queryMethodWithWriteLock");
|
||||
JdbcQueryMethod queryMethodWithReadLock = createJdbcQueryMethod("queryMethodWithReadLock");
|
||||
|
||||
assertThat(queryMethodWithWriteLock.hasLockMode()).isTrue();
|
||||
assertThat(queryMethodWithReadLock.hasLockMode()).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-1041
|
||||
void returnsQueryMethodWithCorrectLockType() throws NoSuchMethodException {
|
||||
|
||||
JdbcQueryMethod queryMethodWithWriteLock = createJdbcQueryMethod("queryMethodWithWriteLock");
|
||||
JdbcQueryMethod queryMethodWithReadLock = createJdbcQueryMethod("queryMethodWithReadLock");
|
||||
|
||||
assertThat(queryMethodWithWriteLock.lookupLockAnnotation()).isPresent();
|
||||
assertThat(queryMethodWithReadLock.lookupLockAnnotation()).isPresent();
|
||||
|
||||
assertThat(queryMethodWithWriteLock.lookupLockAnnotation().get().value()).isEqualTo(LockMode.PESSIMISTIC_WRITE);
|
||||
assertThat(queryMethodWithReadLock.lookupLockAnnotation().get().value()).isEqualTo(LockMode.PESSIMISTIC_READ);
|
||||
}
|
||||
|
||||
@Lock(LockMode.PESSIMISTIC_WRITE)
|
||||
@Query
|
||||
private void queryMethodWithWriteLock() {}
|
||||
|
||||
@Lock(LockMode.PESSIMISTIC_READ)
|
||||
@Query
|
||||
private void queryMethodWithReadLock() {}
|
||||
|
||||
@Query(value = QUERY, rowMapperClass = CustomRowMapper.class)
|
||||
private void queryMethod() {}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,6 +42,8 @@ import org.springframework.data.relational.core.dialect.H2Dialect;
|
||||
import org.springframework.data.relational.core.mapping.Embedded;
|
||||
import org.springframework.data.relational.core.mapping.MappedCollection;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.relational.core.sql.In;
|
||||
import org.springframework.data.relational.core.sql.LockMode;
|
||||
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.Repository;
|
||||
@@ -58,6 +60,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Diego Krupitza
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class PartTreeJdbcQueryUnitTests {
|
||||
@@ -85,7 +88,7 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
|
||||
Hobby hobby = new Hobby();
|
||||
hobby.name = "twentythree";
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] {hobby}), returnedType);
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { hobby }), returnedType);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -96,6 +99,47 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-922
|
||||
void createQueryWithPessimisticWriteLock() throws Exception {
|
||||
|
||||
JdbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameAndLastName", String.class, String.class);
|
||||
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
|
||||
|
||||
String firstname = "Diego";
|
||||
String lastname = "Krupitza";
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { firstname, lastname }),
|
||||
returnedType);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
softly.assertThat(query.getQuery().toUpperCase()).endsWith("FOR UPDATE");
|
||||
|
||||
softly.assertThat(query.getParameterSource().getValue("first_name")).isEqualTo(firstname);
|
||||
softly.assertThat(query.getParameterSource().getValue("last_name")).isEqualTo(lastname);
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-922
|
||||
void createQueryWithPessimisticReadLock() throws Exception {
|
||||
|
||||
JdbcQueryMethod queryMethod = getQueryMethod("findAllByFirstNameAndAge", String.class, Integer.class);
|
||||
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
|
||||
|
||||
String firstname = "Diego";
|
||||
Integer age = 22;
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { firstname, age }),
|
||||
returnedType);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
// this is also for update since h2 dialect does not distinguish between lockmodes
|
||||
softly.assertThat(query.getQuery().toUpperCase()).endsWith("FOR UPDATE");
|
||||
|
||||
softly.assertThat(query.getParameterSource().getValue("first_name")).isEqualTo(firstname);
|
||||
softly.assertThat(query.getParameterSource().getValue("age")).isEqualTo(age);
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-318
|
||||
public void shouldFailForQueryByList() throws Exception {
|
||||
|
||||
@@ -116,7 +160,7 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
JdbcQueryMethod queryMethod = getQueryMethod("findViaReferenceByHobbyReference", AggregateReference.class);
|
||||
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
|
||||
AggregateReference<Object, String> hobby = AggregateReference.to("twentythree");
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] {hobby}), returnedType);
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { hobby }), returnedType);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -133,7 +177,7 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
JdbcQueryMethod queryMethod = getQueryMethod("findViaIdByHobbyReference", String.class);
|
||||
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
|
||||
String hobby = "twentythree";
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] {hobby}), returnedType);
|
||||
ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { hobby }), returnedType);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -213,7 +257,6 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
+ ".\"FIRST_NAME\" = :first_name)");
|
||||
}
|
||||
|
||||
|
||||
@Test // DATAJDBC-318
|
||||
public void createsQueryToFindAllEntitiesByDateAttributeBetween() throws Exception {
|
||||
|
||||
@@ -644,6 +687,12 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
@NoRepositoryBean
|
||||
interface UserRepository extends Repository<User, Long> {
|
||||
|
||||
@Lock(LockMode.PESSIMISTIC_WRITE)
|
||||
List<User> findAllByFirstNameAndLastName(String firstName, String lastName);
|
||||
|
||||
@Lock(LockMode.PESSIMISTIC_READ)
|
||||
List<User> findAllByFirstNameAndAge(String firstName, Integer age);
|
||||
|
||||
List<User> findAllByFirstName(String firstName);
|
||||
|
||||
List<User> findAllByHated(Hobby hobby);
|
||||
@@ -758,7 +807,6 @@ public class PartTreeJdbcQueryUnitTests {
|
||||
}
|
||||
|
||||
static class Hobby {
|
||||
@Id
|
||||
String name;
|
||||
@Id String name;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user