简体   繁体   中英

Getting single NonNull value using Spring Data

I'd like to have a method in my Repository that returns a single value. Like this:

TrainingMode findByTrainingNameAndNameEng( String trainingName, String nameEng );

http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/ Spring Data Docs describe that in this case the method can return null if no entity is found. I'd like to throw an exception with generic message like No TrainingMode found by %trainingName% and %nameEng% or smth like that.

I can use Optional<TrainingMode> as a return value and then use orElseThrow

Optional<TrainingMode> findByTrainingNameAndNameEng( String trainingName, String nameEng );

repository.findByTrainingNameAndNameEng(name, nameEng).orElseThrow(() -> new RuntimeException(...));

But I should call this method each time when this method is called. It's not clear - DRY priciple is broken.

How to get nonnull single value with orElseThrow using Spring Data?

The DRY principle would be violated if you duplicate null handling throughout the application logic where it is being invoked. If DRY principle is the thing you are worried the most then i can think of:

  1. You can make a "Service" class which would delegate calls to annotated repository and handle null response logic to it, and use that service class instead of calling repositories directly. Drawback would be introducing another layer to your application (which would decouple repositories from your app logic).

  2. There is possibility of adding custom behavior to your data repositories which is described in " 3.6.1. Adding custom behavior to single repositories " section of documentation. Sorry for not posting the snippet.

The issue I personally have with second approach is that it pollutes app with interfaces, enforces you to follow a certain naming patterns (never liked 'Impl' suffixes), and might make migrating code a bit more time consuming (when app becomes big it becomes harder to track which interface is responsible for which custom behavior and then people just simply start creating their own behavior which turns out to be duplicate of another).

I found a solution.

First, Spring Data processes getByName and findByName equally. And we can use it: in my case find* can return null (or returns not null Optional, as you wish) and get* should return only value: if null is returned then exception is thrown.

I decided to use AOP for this case. Here's the aspect:

@Aspect
@Component
public class GetFromRepositoryAspect {
    @Around("execution(public !void org.springframework.data.repository.Repository+.get*(..))")
    public Object aroundDaoMethod( ProceedingJoinPoint joinpoint ) throws Throwable {
        Object result = joinpoint.proceed();
        if (null == result) {
            throw new FormattedException( "No entity found with arhs %s",
                    Arrays.toString( joinpoint.getArgs() ) );
        }
        return result;
    }
}

That's all.

You can achieve this by using the Spring nullability annotations . If the method return type is just some Entity and it's not a wrapper type, such as Optional<T> , then org.springframework.dao.EmptyResultDataAccessException will be thrown in case of no results.

Read more about Null Handling of Repository Methods .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM