Я столкнулся со следующей проблемой:
У меня есть объект:
@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
//...
}
Этот объект имеет ограничение уникальности orgName + hostName fields.
И соответствующий репозиторий для сущности:
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);
//..
}
Я хочу создать сервис с методом findOrCreate, который бы:
- Создать новый хостесли Хост не существует
- Если Хост существует, вернуть Хост
С учетом ограничения уникальности полей hostName + orgName .
Этот метод должен работать в предположении, что он может выполняться в нескольких разных экземплярах одного и того же приложения, а также в разных потоках.
В настоящее время у меня есть два решения:
Использовать отдельный метод для создания с распространением = Требуется новый
@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);
}
}
Выполните всю логику в одном методе, но с уровнем изоляции = 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);
}
}
Мне кажется, что оба эти варианта будут работать одновременносреда и первый вариант предпочтительнее, потому что работает быстрее.Однако я боюсь, что могу пропустить подводные камни.
Кто-нибудь сталкивался с проблемой раньше?
Если это так, я был бы очень признателен за любые советы или альтернативные решения, помимо перечисленных выше,
Спасибо, ура