Поэтому я делаю простую операцию поиска записи в моем хранилище. Если запись отсутствует, выдается исключение.

@NotNull
public static User getUserFromUuid(UUID userUuid) {
    Optional<User> userOptional = userRepository.findByUserIdentifier(userUuid);
    if (!userOptional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("Unable to find user with uuid %s", userUuid.toString()));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User Not Found");
    }
    return userOptional.get();
}
@NotNull
public static Group getGroupFromId(Long groupId) {
    Optional<Group> groupOptional = groupRepository.findById(groupId);
    if (!groupOptional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("Group with id %s does not exist", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found");
    }
    return groupOptional.get();
}

Я понимаю, что в конечном итоге сделаю это много раз для всех моих методов поиска. И большинство из них будут выполнять очень похожую задачу.

Один из способов - расширить CrudRepository моей версией, но я хочу реализовать этот шаблон для всех находками.

Другой способ - передать класс, метод, параметры и сообщение об ошибке для поиска. Лямбда-метод, кажется, подходит, но я не мог понять, как я смогу применить это к моей проблеме.

Это приближает к решению проблемы, но тип возвращаемого значения меняется. Я бы также передавал переменное количество аргументов.

Есть ли какой-нибудь подход, который я могу предпринять, чтобы сделать это?

РЕДАКТИРОВАТЬ:

Я также хотел бы разобраться с этим делом

Optional<GroupUser> groupUser = groupUserRepository.findByUserAndGroup(user, group

Где я мог в конечном итоге иметь несколько параметров поиска.

Нечто подобное в питоне будет

def perform( fun, *args ):
    fun( *args )

def action1( args ):
    something

def action2( args ):
    something

perform( action1 )
perform( action2, p )
perform( action3, p, r )
2
Akshat Malik 5 Май 2020 в 15:34

2 ответа

Лучший ответ

Поскольку все ваши значения могут быть представлены строкой, вы можете сделать следующее:

public static Object getAccountDetails(String primaryKey, Class<?> targetClass) {   
        Optional<?> result;

        switch(targetClass.getSimpleName())
        {
        case "Group":
             result = Test.dummyFind(primaryKey);
             break;
        case "User":
            result =  Test.dummyFind(primaryKey);
            break;
        default:
            throw new IllegalArgumentException("The provided class: "+targetClass.getCanonicalName()+" was not a valid class to be resolved by this method.");      
        }

        if(result.isPresent())
        {
            logger.info(String.format("Group with id %s does not exist", groupId));
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found"); 
        }

        return result.get();
    }

Насколько я знаю, нет необходимости проверять, включен ли в логгере информационный уровень, поскольку это определяется вашей конфигурацией обратного входа. Так что, если нет информации для регистрации, она не будет зарегистрирована.

Использование функции было бы примерно таким (классы случайные, так как я хотел, чтобы моя подсветка синтаксиса):

UserDataHandler data = (UserDataHandler) getAccountDetails("1234", UserDataHandler.class);

Это независимо от какой-либо функциональности Spring, так как я не работаю с ним. <?> является оператором подстановки. Однако, поскольку ваш результат является содержимым необязательного, вы должны вернуть объект, который затем должен быть проанализирован в соответствии с типом.

Два способа: либо вы используете такой реестр:

public static Object getAccountDetails(Object primaryKey, Class<?> targetClass) {   

    Optional<?> result;

    // This map should be acquired from a Singleton where you register these classes once in @PostConstruct.
    Map<String, Method> methodMap = new TreeMap<>();
    try {
        methodMap.put("UserDataHandler", Test.class.getMethod("dummyFind"));
    } catch (NoSuchMethodException | SecurityException e) {}


    if(methodMap.containsKey(targetClass.getName()))
    {
        Method method = methodMap.get(targetClass.getName());
        try {
            result = (Optional<?>) method.invoke(primaryKey);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            // do some more error handling
            return null;
        }
    }
    else throw new IllegalArgumentException("The provided class: "+targetClass.getCanonicalName()+" was not a valid class to be resolved by this method.");


    if(!result.isPresent())
    {
        logger.info(String.format("Group with id %s does not exist", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found"); 
    }

    return result.get();
}

Или даже более разумный метод будет состоять в том, чтобы всегда называть методы в одном и том же шаблоне, если любой получит целевой репозиторий с помощью Class.forName(String):

public static Object getAccountDetails(Object primaryKey, Class<?> targetClass) {   

    Optional<?> result;

    Class<?> myRepository = Class.forName(targetClass.getSimpleName()+"Repository");

    String methodName = "findBy"+targetClass.getName();

    try
    {
        Method findMethod = targetClass.getMethod(methodName);
        result = (Optional<?>) findMethod.invoke(primaryKey);
    }
    catch (NoSuchMethodException e){throw new IllegalArgumentException("The method "+methodName+" couldn't be found in the repository");}
    catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {return null;}


    if(!result.isPresent())
    {
        logger.info(String.format("Group with id %s does not exist", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found"); 
    }

    return result.get();
}

Для обоих методов ваша функция find должна принять Object в качестве аргумента, который должен быть приведен для его использования:

public static Optional<Long> dummyFind(Object primaryKey)
{
    long typedPrimaryKey = (long) primaryKey;
    return Optional.of(typedPrimaryKey);
}

Но как я уже дважды думал об этом, все, что вы хотите, уже существует: EntityManager.find(Class<T> entityClass,Object primaryKey)

1
maio290 5 Май 2020 в 15:04

Вы можете создать универсальный метод, который принимает Optional любого типа и строку для сообщения журнала. Он вернет объект, если он есть, иначе будет исключение

public <T> T returnIfPresent(Optional<T> optional, String id){

    if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("Group with id %s does not exist", id));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found");
    }
    return optional.get();
}

И вы можете вызывать этот метод из любого метода

@NotNull
public static User getUserFromUuid(UUID userUuid) {

    Optional<User> userOptional = userRepository.findByUserIdentifier(userUuid);
    return returnIfPresent(userOptional, userUuid.toString());
 }

 @NotNull
 public static Group getGroupFromId(Long groupId) {
     Optional<Group> groupOptional = groupRepository.findById(groupId);

     return returnIfPresent(groupOptional, groupId.toString());
 }

Еще одно предложение, которое я бы порекомендовал, это иметь сообщение в качестве второго параметра, чтобы вы могли построить сообщение в оригинальном методе и передать его

public <T> T returnIfPresent(Optional<T> optional, String message){

    if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(message);
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, message);
    }
    return optional.get();
}
1
Deadpool 5 Май 2020 в 14:37