I'm facing the following problem:
I have an entity:
@Table(name = "host",
uniqueConstraints =
{
@UniqueConstraint(name = "uq_host_0",
columnNames = {"orgName", "hostName"})}
)
class Host {
private String id;
private String hostName;
private String orgName;
//gets
//sets
//constructors
//...
}
This entity has uniqueness constrain on orgName + hostName fields.
And corresponding Repository for the entity:
public interface HostRepository extends JpaRepository<Host, String> {
Page<Host> findByOrgId(String orgId, Pageable pageable);
Host findOneByOrgNameIdAndId(String orgName, String id);
Host findOneByOrgNameAndHostName(String orgName, String hostName);
//..
}
I want to create a service with findOrCreate method that would:
Taking into account uniqueness constraint on hostName + orgName fields.
This method should work in assumption that it can be executed on multiple different instances of the same application as well as in different threads.
Currently I came up with two solutions:
Use separate method for creation with Propagation = RequiresNew
@Service
public class HostService {
@Autowired
private HostRepository hostRepository;
@Transactional
public Host findOrCreate(Host host) {
try {
return create(host);
} catch(ConstraintViolationException e) {
//means the host has already been created by other transaction
return hostRepository.findFirstByOrgNameAndHostName(host.getHostName(), host.getOrgName());
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Host create(Host host) {
//constraint violation may be thrown
hostRepository.save(host);
}
}
Perform all the logic in one method, but with isolation level = serializable:
@Service
public class HostService {
@Autowired
private HostRepository hostRepository;
@Transactional(isolation = Isolation.SERIALIZABLE)
public Host findOrCreate(Host host) {
Optional<Host> existing = Optional.ofNullable(hostRepository.findOneByOrgNameAndHostName(host.getOrgName(), host.getHostName()));
if(existing.isPresent()) {
return existing;
}
return hostRepository.save(host);
}
}
It seems to me that both of these options would work in concurrent environment and the first option is preferable because works faster. However, I'm afraid that I may miss underwater rocks.
Did anyone face the problem before?
If so, I would really grateful for any advice or alternative solutions apart from listed above,
Thanks, cheers
I have an idea. You can define one method in HostRepository like:
Optional<Host> findByOrgNameAndHostName(String orgName, String hostName)
And in HostService, you can use the method:
Host host = hostRepository.findByOrgNameAndHostName("hoge", "fuga")
.orElseGet(() -> hostRepository.save(new Host("hoge", "fuga")))
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