6

I'm using Spring Data JPA and I have a bunch of repositories like this one:

public interface CustomerRepository extends JpaRepository<Customer, Long> {}

Under repositories I have services and a lot of them need to have implemented method findOrCreate(String name) like this one:

@Override
    @Transactional
    public List<Customer> findOrCreate(final String name) {
        checkNotNull(name);
        List<Customer> result = this.customerRepository.findByName(name);
        if (result.isEmpty()) {
            LOGGER.info("Cannot find customer. Creating a new customer. [name={}]", name);
            Customer customer = new Customer(name);
            return Arrays.asList(this.customerRepository.save(customer));
        }
        return result;
    }

I would like to extract method to the abstract class or somewhere to avoid implementing it for each services, testing and so on.

Abstract class can be look like this:

public abstract class AbstractManagementService<T, R extends JpaRepository<T, Serializable>> {

    protected List<T> findOrCreate(T entity, R repository) {
        checkNotNull(entity);
        checkNotNull(repository);

        return null;
    }

}

And the problem is it due to the fact that I need to find object by name as a string before creating a new one. Of course interface JpaRepository doesn't offer this method.

How can I solve this problem?

Best Regards

tomasz-mer
  • 3,753
  • 10
  • 50
  • 69
  • There is a race condition here: when this method is called from two threads on an entity that has not yet been created. Both will try to fetch (and won't find anything), then one of the `save()` will fail. – Jezor Feb 13 '20 at 10:38
  • I don't think there is, because both statements are executed in a transaction. – Koen. Jun 08 '22 at 13:25

1 Answers1

1

Create a custom JpaRepository implementation that includes this behaviour. See this post for an example of writing a custom JpaRepository implementation.

Community
  • 1
  • 1
manish
  • 19,695
  • 5
  • 67
  • 91