I wrote a controller that must return an unique String. The requirement is that two calling of this controller never return the same String, even after years and even if the code will scale to more VMs.
My question is if the following code is correct to achieve to declared purpose, or if you have any hint.
Controller:
@RestController
public class UtilityController {
@Autowired
UtilityServices utilityServices;
@GetMapping("/uniqueIdentifier")
@ResponseBody
public String uniqueIdentifier() {
return utilityServices.getUniqueIdentifier();
}
Service:
@Service
public class UtilityServices {
@Autowired
private UniqueIdRepository uniqueIdRepository;
@Transactional
public String getUniqueIdentifier() {
String uniqueId = RandomString.getSecureRandomString();
while (uniqueIdRepository.existsById(uniqueId)) {
uniqueId = RandomString.getSecureRandomString();
}
uniqueIdRepository.save(new UniqueId(uniqueId));
return uniqueId;
}
}
Entity:
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
@ToString
public class UniqueId implements Serializable {
@Id
private String uniqueId;
}
Repository:
public interface UniqueIdRepository extends CrudRepository<UniqueId, String> {
}
That's all. I omit the code the RandomString class because it's not relevant in this context: I wrote a code based on SecureRandom, it is very likely that each time it returns a different String, but I have no guarantees about it. Let's assume that sooner or later my RandomString.getSecureRandomString() method can return the same String.
I'm not sure if the @Transactional annotation guarantees that the getUniqueIdentifier() method never throws an error.
The much better idea at your case will be using UUID:
Thus, anyone can create a UUID and use it to identify something with near certainty that the identifier does not duplicate one that has already been, or will be, created to identify something else. Information labelled with UUIDs by independent parties can, therefore, be later combined into a single database or transmitted on the same channel, with a negligible probability of duplication.
@Service
public class UtilityServices {
@Autowired
private UniqueIdRepository uniqueIdRepository;
@Transactional
public String getUniqueIdentifier() {
String uniqueId = String.format("%s-%s",
RandomStringUtils.randomAlphanumeric(4),
UUID.randomUUID().toString().replace("-", "")
);
// you could left this check
while (uniqueIdRepository.existsById(uniqueId)) {
uniqueId = UUID.randomUUID().toString().replace("-", "");
}
uniqueIdRepository.save(new UniqueId(uniqueId));
return uniqueId;
}
}
BTW you could use @Data for Model:
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class UniqueId implements Serializable {
private static final long serialVersionUID = 0L;
@Id
private String uniqueId;
}
And don't forget about serialVersionUID.
Useful references:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With