1

I have this repository:

public interface AccountsRepository extends CrudRepository<Account, Long> {

}

Then I have this service class:

public class AccountService {

    private AccountsRepository accountsRepository;

    public AccountService(AccountsRepository accountsRepository) {
        this.accountsRepository = accountsRepository;
    }


    public Account createNewAccount() {
        Account account = new Account();
        account = tryStoreAccountElseRepeatWithDifferentIdentifier(account);
        return account;
    }

    private Account tryStoreAccountElseRepeatWithDifferentIdentifier(Account account) {
        account.setIdentifier(IdentifierGenerator.generateString(6));
        try {
            return accountsRepository.save(account);
        } catch (DataIntegrityViolationException e) {
            return tryStoreAccountElseRepeatWithDifferentIdentifier(account);
        }
    }
}

Unit test:

public class AccountServiceUnitTests {

    AccountService fixture;

    AccountsRepository mockAccountRespository;

    @Before
    public void setup() {
        mockAccountRespository = mock(AccountsRepository.class);
        fixture = new AccountService(mockAccountRespository);
    }

    @Test
    public void repeatCreateAccountWhenIdentifierIsDuplicateValue() {

        Account account = new Account();
        account.setId(123L);
        account.setIdentifier("ABCDEF");

        when(mockAccountRespository.save(any(Account.class)))
                .thenThrow(DataIntegrityViolationException.class)
                .thenThrow(DataIntegrityViolationException.class)
                .thenThrow(DataIntegrityViolationException.class)
                .thenReturn(account);


        Account newAccount = fixture.createNewAccount();
        assertEquals(account, newAccount);
    }
}

Entity:

@Entity
@Table(name = "accounts")
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "identifier", unique = true)
    private String identifier;

    @Column(name = "username", unique = true)
    private String username;

    // getter, setter shortened
}

Thing is, I want to store a new Account into the database. Some columns have index UNIQUE. So when you try to insert data MySQL throws an exception if there is a DUPLICATE value.

Since the save(Entitiy) method does not throw a documented exception I tried what would happen and saw that a DataIntegrityViolationException is thrown in case I try to add duplicate value.

So my idea was basically try recursively to insert a new row till no exception is thrown. See: tryStoreAccountElseRepeatWithDifferentIdentifier

Is this way of checking for duplicate value good practise? Or is there a "built-in" solution I don't know?

TomGrill Games
  • 1,553
  • 2
  • 16
  • 25
  • Obvious don't is a good practice. You should search for the keyword type, and if not found, create it. – Xstian Dec 07 '15 at 16:20
  • ..why not just use data that doesn't cause the violation? – user2864740 Dec 07 '15 at 16:27
  • @user2864740 Sometimes not possible. Assume a newsletter list with emails. Add email only when not already on the list. – TomGrill Games Dec 07 '15 at 16:33
  • Of course its a good practice to do some validation before persisting data to storage. Checking for duplicate records is a part of validation stuff. So it is good practice. Its much better to find duplicate and show nice validation failed message to user, instead of get Exception from Data Storage. You can even use JSR-303 for it! Also, do not make modification of user input silently. Always show to user that account is already exists, and suggest to use generated one, but give user a chance to decide what to do. – antonu17 Dec 07 '15 at 16:52

0 Answers0