@ -16,19 +16,21 @@
package org.springframework.data.repository.core.support ;
package org.springframework.data.repository.core.support ;
import static java.util.Arrays.* ;
import static java.util.Arrays.* ;
import static org.springframework.util.ClassUtils.* ;
import static org.springframework.data.util.Optionals.* ;
import static org.springframework.util.ReflectionUtils.* ;
import java.io.Serializable ;
import java.lang.reflect.Method ;
import java.lang.reflect.Method ;
import java.util.Optional ;
import java.util.Optional ;
import java.util.function.Supplier ;
import java.util.stream.Stream ;
import org.springframework.core.BridgeMethodResolver ;
import org.springframework.data.domain.Pageable ;
import org.springframework.data.domain.Pageable ;
import org.springframework.data.domain.Sort ;
import org.springframework.data.domain.Sort ;
import org.springframework.data.repository.CrudRepository ;
import org.springframework.data.repository.CrudRepository ;
import org.springframework.data.repository.PagingAndSortingRepository ;
import org.springframework.data.repository.core.CrudMethods ;
import org.springframework.data.repository.core.CrudMethods ;
import org.springframework.data.repository.core.RepositoryMetadata ;
import org.springframework.data.repository.core.RepositoryMetadata ;
import org.springframework.data.util.Optionals ;
import org.springframework.data.util.Pair ;
import org.springframework.util.Assert ;
import org.springframework.util.Assert ;
import org.springframework.util.ClassUtils ;
import org.springframework.util.ClassUtils ;
import org.springframework.util.ReflectionUtils ;
import org.springframework.util.ReflectionUtils ;
@ -44,10 +46,12 @@ import org.springframework.util.ReflectionUtils;
* /
* /
public class DefaultCrudMethods implements CrudMethods {
public class DefaultCrudMethods implements CrudMethods {
private static final String FIND_ONE = "findOne " ;
private static final String FIND_ONE = "findById " ;
private static final String SAVE = "save" ;
private static final String SAVE = "save" ;
private static final String FIND_ALL = "findAll" ;
private static final String FIND_ALL = "findAll" ;
private static final String DELETE = "delete" ;
private static final String DELETE = "delete" ;
private static final String DELETE_BY_ID = "deleteById" ;
private final Optional < Method > findAllMethod ;
private final Optional < Method > findAllMethod ;
private final Optional < Method > findOneMethod ;
private final Optional < Method > findOneMethod ;
@ -79,18 +83,12 @@ public class DefaultCrudMethods implements CrudMethods {
* @param metadata must not be { @literal null } .
* @param metadata must not be { @literal null } .
* @return the most suitable method or { @literal null } if no method could be found .
* @return the most suitable method or { @literal null } if no method could be found .
* /
* /
private Optional < Method > selectMostSuitableSaveMethod ( RepositoryMetadata metadata ) {
private static Optional < Method > selectMostSuitableSaveMethod ( RepositoryMetadata metadata ) {
for ( Class < ? > type : asList ( metadata . getDomainType ( ) , Object . class ) ) {
Method saveMethodCandidate = findMethod ( metadata . getRepositoryInterface ( ) , SAVE , type ) ;
if ( saveMethodCandidate ! = null ) {
return getMostSpecificMethod ( saveMethodCandidate , metadata . getRepositoryInterface ( ) ) ;
}
}
return Optional . empty ( ) ;
return asList ( metadata . getDomainType ( ) , Object . class ) . stream ( ) //
. flatMap ( it - > toStream ( findMethod ( metadata . getRepositoryInterface ( ) , SAVE , it ) ) ) //
. flatMap ( it - > toStream ( getMostSpecificMethod ( it , metadata . getRepositoryInterface ( ) ) ) ) //
. findFirst ( ) ;
}
}
/ * *
/ * *
@ -98,25 +96,28 @@ public class DefaultCrudMethods implements CrudMethods {
* < ol >
* < ol >
* < li > a { @link RepositoryMetadata # getDomainType ( ) } as first parameter over < / li >
* < li > a { @link RepositoryMetadata # getDomainType ( ) } as first parameter over < / li >
* < li > a { @link RepositoryMetadata # getIdType ( ) } as first parameter over < / li >
* < li > a { @link RepositoryMetadata # getIdType ( ) } as first parameter over < / li >
* < li > a { @link Serializable } as first parameter over < / li >
* < li > a { @link Object } as first parameter over < / li >
* < li > an { @link Iterable } as first parameter . < / li >
* < li > an { @link Iterable } as first parameter . < / li >
* < / ol >
* < / ol >
*
*
* @param metadata must not be { @literal null } .
* @param metadata must not be { @literal null } .
* @return the most suitable method or { @literal null } if no method could be found .
* @return the most suitable method or { @literal null } if no method could be found .
* /
* /
private Optional < Method > selectMostSuitableDeleteMethod ( RepositoryMetadata metadata ) {
private static Optional < Method > selectMostSuitableDeleteMethod ( RepositoryMetadata metadata ) {
for ( Class < ? > type : asList ( metadata . getDomainType ( ) , metadata . getIdType ( ) , Serializable . class , Iterable . class ) ) {
Stream < Pair < String , Class < ? > > > source = Stream . of ( //
Pair . of ( DELETE , metadata . getDomainType ( ) ) , //
Pair . of ( DELETE_BY_ID , metadata . getIdType ( ) ) , //
Pair . of ( DELETE , Object . class ) , //
Pair . of ( DELETE_BY_ID , Object . class ) , //
Pair . of ( DELETE , Iterable . class ) ) ;
Method candidate = findMethod ( metadata . getRepositoryInterface ( ) , DELETE , type ) ;
Class < ? > repositoryInterface = metadata . getRepositoryInterface ( ) ;
if ( candidate ! = null ) {
return source //
return getMostSpecificMethod ( candidate , metadata . getRepositoryInterface ( ) ) ;
. flatMap ( it - > toStream ( findMethod ( repositoryInterface , it . getFirst ( ) , it . getSecond ( ) ) ) ) //
}
. flatMap ( it - > toStream ( getMostSpecificMethod ( it , repositoryInterface ) ) ) //
}
. findFirst ( ) ;
return Optional . empty ( ) ;
}
}
/ * *
/ * *
@ -130,49 +131,37 @@ public class DefaultCrudMethods implements CrudMethods {
* @param metadata must not be { @literal null } .
* @param metadata must not be { @literal null } .
* @return the most suitable method or { @literal null } if no method could be found .
* @return the most suitable method or { @literal null } if no method could be found .
* /
* /
private Optional < Method > selectMostSuitableFindAllMethod ( RepositoryMetadata metadata ) {
private static Optional < Method > selectMostSuitableFindAllMethod ( RepositoryMetadata metadata ) {
for ( Class < ? > type : asList ( Pageable . class , Sort . class ) ) {
if ( hasMethod ( metadata . getRepositoryInterface ( ) , FIND_ALL , type ) ) {
Method candidate = findMethod ( PagingAndSortingRepository . class , FIND_ALL , type ) ;
Class < ? > repositoryInterface = metadata . getRepositoryInterface ( ) ;
if ( candidate ! = null ) {
Supplier < Optional < Method > > withPageableOrSort = ( ) - > Stream . of ( Pageable . class , Sort . class ) //
return getMostSpecificMethod ( candidate , metadata . getRepositoryInterface ( ) ) ;
. flatMap ( it - > toStream ( findMethod ( repositoryInterface , FIND_ALL , it ) ) ) //
}
. flatMap ( it - > toStream ( getMostSpecificMethod ( it , repositoryInterface ) ) ) //
}
. findFirst ( ) ;
}
if ( hasMethod ( metadata . getRepositoryInterface ( ) , FIND_ALL ) ) {
Supplier < Optional < Method > > withoutParameter = ( ) - > findMethod ( repositoryInterface , FIND_ALL ) //
return getMostSpecificMethod ( findMethod ( CrudRepository . class , FIND_ALL ) , metadata . getRepositoryInterface ( ) ) ;
. flatMap ( it - > getMostSpecificMethod ( it , repositoryInterface ) ) ;
}
return Optional . empty ( ) ;
return firstNonEmpty ( withPageableOrSort , withoutParameter ) ;
}
}
/ * *
/ * *
* The most suitable findOne method is selected as follows : We prefer
* The most suitable { @code findById } method is selected as follows : We prefer
* < ol >
* < ol >
* < li > a { @link RepositoryMetadata # getIdType ( ) } as first parameter over < / li >
* < li > a { @link RepositoryMetadata # getIdType ( ) } as first parameter over < / li >
* < li > a { @link Serializable } as first parameter < / li >
* < li > a { @link Object } as first parameter < / li >
* < / ol >
* < / ol >
*
*
* @param metadata must not be { @literal null } .
* @param metadata must not be { @literal null } .
* @return the most suitable method or { @literal null } if no method could be found .
* @return the most suitable method or { @literal null } if no method could be found .
* /
* /
private Optional < Method > selectMostSuitableFindOneMethod ( RepositoryMetadata metadata ) {
private static Optional < Method > selectMostSuitableFindOneMethod ( RepositoryMetadata metadata ) {
for ( Class < ? > type : asList ( metadata . getIdType ( ) , Serializable . class ) ) {
Method candidate = findMethod ( metadata . getRepositoryInterface ( ) , FIND_ONE , type ) ;
if ( candidate ! = null ) {
return asList ( metadata . getIdType ( ) , Object . class ) . stream ( ) //
return getMostSpecificMethod ( candidate , metadata . getRepositoryInterface ( ) ) ;
. flatMap ( it - > toStream ( findMethod ( metadata . getRepositoryInterface ( ) , FIND_ONE , it ) ) ) //
}
. flatMap ( it - > toStream ( getMostSpecificMethod ( it , metadata . getRepositoryInterface ( ) ) ) ) //
}
. findFirst ( ) ;
return Optional . empty ( ) ;
}
}
/ * *
/ * *
@ -186,10 +175,10 @@ public class DefaultCrudMethods implements CrudMethods {
* /
* /
private static Optional < Method > getMostSpecificMethod ( Method method , Class < ? > type ) {
private static Optional < Method > getMostSpecificMethod ( Method method , Class < ? > type ) {
return Optional . ofNullable ( ClassUtils . getMostSpecificMethod ( method , type ) ) . map ( it - > {
return Optionals . toStream ( Optional . ofNullable ( ClassUtils . getMostSpecificMethod ( method , type ) ) ) //
ReflectionUtils . makeAccessible ( it ) ;
. map ( it - > BridgeMethodResolver . findBridgedMethod ( it ) ) //
return it ;
. peek ( it - > ReflectionUtils . makeAccessible ( it ) ) //
} ) ;
. findFirst ( ) ;
}
}
/ *
/ *
@ -263,4 +252,8 @@ public class DefaultCrudMethods implements CrudMethods {
public Optional < Method > getDeleteMethod ( ) {
public Optional < Method > getDeleteMethod ( ) {
return this . deleteMethod ;
return this . deleteMethod ;
}
}
private static Optional < Method > findMethod ( Class < ? > type , String name , Class < ? > . . . parameterTypes ) {
return Optional . ofNullable ( ReflectionUtils . findMethod ( type , name , parameterTypes ) ) ;
}
}
}