Je fais donc une opération simple pour trouver une entrée dans mon référentiel. Si l'entrée n'est pas présente, lève une exception.

@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();
}

Je réalise que je finirais par faire cela plusieurs fois pour toutes mes méthodes de recherche. Et la plupart d'entre eux feront une tâche très similaire.

Une façon est d'étendre le CrudRepository avec ma version, mais je veux implémenter ce modèle pour al par find.

Une autre façon serait de transmettre la classe, la méthode, les paramètres et le message d'erreur à rechercher. La méthode Lambda semble être la solution, mais je n'ai pas pu comprendre comment je pourrais l'appliquer à mon problème.

Cela est sur le point de résoudre le problème, mais le type de retour est en train de changer. Je passerais également un nombre variable d'arguments.

Y a-t-il une approche que je puisse adopter pour y parvenir?

ÉDITER:

Je souhaite également gérer ce cas

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

Où je pourrais finir par avoir plus d'un paramètre de recherche.

Quelque chose de similaire en python serait

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 mai 2020 à 15:34

3 réponses

Meilleure réponse

Étant donné que toutes vos valeurs peuvent être représentées par une chaîne, vous pouvez effectuer les opérations suivantes:

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();
    }

Pour autant que je sache, il n'est pas nécessaire de vérifier si l'enregistreur a le niveau d'informations activé, car cela est défini par votre configuration de connexion. Donc, s'il n'y a pas de journalisation pour les informations, elles ne seront pas enregistrées.

L'utilisation de la fonction serait quelque chose comme ça (les classes sont aléatoires puisque je voulais que ma syntaxe soit mise en évidence):

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

Ceci est indépendamment de toute fonctionnalité de Spring puisque je ne travaille pas avec. Le <?> est un opérateur générique. Puisque votre résultat est cependant le contenu d'un facultatif, vous devez renvoyer un objet qui doit ensuite être analysé selon le type correspondant.

Deux façons: soit vous utilisez un registre comme celui-ci:

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();
}

Ou une méthode encore plus intelligente serait simplement de toujours nommer les méthodes dans le même modèle pour acquérir le référentiel cible par 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();
}

Pour les deux méthodes, votre fonction de recherche doit prendre un objet comme argument qui doit être casté pour pouvoir l'utiliser:

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

Mais en y réfléchissant deux fois, tout ce que vous voulez existe déjà: EntityManager.find(Class<T> entityClass,Object primaryKey)

1
maio290 5 mai 2020 à 15:04

Vous pouvez créer une méthode générique qui accepte Optional de n'importe quel type et chaîne pour le message de journal. Il retournera l'objet s'il est présent, sinon il fera exception

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();
}

Et vous pouvez appeler cette méthode à partir de chaque méthode

@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());
 }

L'autre suggestion que je recommanderais est d'avoir un message comme deuxième paramètre afin que vous puissiez créer le message dans la méthode d'origine et le transmettre

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 mai 2020 à 14:37

Cherchez-vous quelque chose comme ça?

public static <T, ID>  T process(Class<T> cls, CrudRepository<T,ID> r, ID id, String errTemplate){
    Optional<T> groupOptional = r.findById(id);
    if (groupOptional.isEmpty()) {
        if (logger.isInfoEnabled()) logger.info(String.format(errTemplate, id));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, cls.getName() + " Not Found");
    }
    return groupOptional.get();
}
1
Giga Kokaia 5 mai 2020 à 12:46