Я работал в проекте, где мы используем Spring Data Rest с HATEOAS, Spring JPA и Hibernate, и в настоящее время мы сталкиваемся со странной проблемой, которую нам не удалось исправить:
- Angular frontendпередает два HTTP-запроса на серверную часть.Оба запроса хотят добавить отношение в разные столбцы в одной строке (из-за HATEOAS это должно быть сделано в двух запросах).
- Создаются две транзакции (A и B), и Hibernate выполняет выбор в каждой транзакции.чтобы получить строку, о которой идет речь.
- Hibernate получает права, которые будут связаны в каждой транзакции.
- Hibernate обновляет выбранную строку один раз в транзакции A. Транзакция B вскоре обновляет строку и сбрасывает еезначение, установленное в транзакции A, возвращается к значению из начального выбора, который был выполнен в начале транзакции.Таким образом, мы теряем всю информацию о транзакции A.
Проблема в том, что это не кажется детерминированным.Это иногда случается, а затем нормально работает следующие десять раз.
Мы не смогли исправить это, добавив блокировку (оптимистическая блокировка просто приводит к ObjectOptimisticLockingFailureException
в транзакции B), и пессимистическая блокировка также не имела никакого эффекта.,Мы используем DynamicUpdate для объекта.Мы попытались установить распространение транзакций, но это ничего не изменило.Сейчас у нас нет идей, и поиск в Google не дал никаких результатов.
Право, которое должно быть обновлено, выглядит следующим образом:
@AllArgsConstructor
@RequiredArgsConstructor
@NoArgsConstructor
@Data
@JsonIgnoreProperties(value = { "history" }, allowGetters = true)
@DynamicUpdate
@SelectBeforeUpdate
@Entity
@Table(name = "parameters")
public class Parameter
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "parameter_id", unique = true)
private Long id;
@NonNull
@Column(name = "name", nullable = false, unique = true)
private String key;
@ManyToOne(fetch = FetchType.EAGER,
cascade = {
CascadeType.REFRESH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.DETACH
}
)
private Component component;
@OneToOne(fetch = FetchType.EAGER,
cascade = {
CascadeType.REFRESH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.DETACH
}
)
private ParameterTypes type;
@OneToMany(
mappedBy = "parameter",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true
)
@OrderBy("history_creation_timestamp DESC")
private List<History> history = new LinkedList<>();
@CreationTimestamp
@Column(name = "parameter_creation_timestamp")
private LocalDateTime creationTimestamp;
@UpdateTimestamp
@Column(name = "parameter_update_timestamp")
private LocalDateTime updateTimestamp;
}
И поля component
и type
должно быть обновлено.
Было бы здорово, если бы мы могли убедиться, что оба запроса корректно обновляют строку, или транзакция B ожидает завершения транзакции A и повторно выбирает строку в ее обновленном состоянии.
Мы очень ценим любую помощь, так как мы не смогли найти рабочее решение за последние три дня.Мы с радостью предоставим любую дополнительную информацию.Спасибо всем заранее.
Фронтенд
Мы используем angular4-hal с Angular 7 для связи с бэкэндом Spring.Вызов довольно прост и выглядит следующим образом:
saveParameter(): void {
this.editMode = false;
const saveObservs: Observable<any>[] = [];
if (this.componentControl.dirty) {
saveObservs.push(this.parameter.addRelation('component', this.componentControl.value));
}
if (this.typeControl.dirty) {
saveObservs.push(this.parameter.addRelation('type', this.typeControl.value));
}
forkJoin(
saveObservs
).pipe(
catchError(err => of(err))
).subscribe(val => console.log(val));
}
componentControl
и typeControl
являются экземплярами FormControl
.
Функция addRelation
выглядит следующим образом:
Resource.prototype.addRelation = function (relation, resource) {
if (this.existRelationLink(relation)) {
var header = ResourceHelper.headers.append('Content-Type', 'text/uri-list');
return ResourceHelper.getHttp().put(ResourceHelper.getProxy(this.getRelationLinkHref(relation)), resource._links.self.href, { headers: header });
}
else {
return observableThrowError('no relation found');
}
};
А контекст функции можно найти здесь