Пожалуйста, относитесь к этому исключительно в образовательных целях ( не кричите на меня
что фильтрация и обновление должны работать не так, я это знаю).
Я создал этот простой пример после прочтения статьи о
'лучший способ обработки исключения MultipleBagFetchException в спящем режиме' и наткнулся
на проблему с правильным удалением потерянных объектов, отсюда и вопрос.
@Service
@RequiredArgsConstructor
public class PostService {
public final PersistablePostMapper persistablePostMapper;
public final PostRepository postRepository;
public final EntityManager entityManager;
@Transactional
public PostDTO saveAll(final PostDTO postDTO) throws BadRequestException {
String referenceId = postDTO.getMetadata().getMetadata().get("reference_id");
List alreadyClosedPostIds = findAllByReferenceId(Long.valueOf(referenceId)).stream()
.filter(p -> PostStatus.CLOSED.equals(p.getStatus()))
.map(PostDTO::getId)
.toList();
if (alreadyClosedPostIds.contains(postDTO.getId())) {
throw new BadRequestException();
}
return save(postDTO);
}
public List
findAllByReferenceId(final Long referenceId) {
List posts = entityManager.createQuery("""
select distinct p
from Post p
left join fetch p.metadata m
where m.key=:key and m.value=:value""", Post.class)
.setParameter("key", "reference_id")
.setParameter("value", String.valueOf(referenceId))
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
posts = entityManager.createQuery("""
select distinct p
from Post p
left join fetch p.comments l
where p in :posts"""
, Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
return posts.stream().map(persistablePostMapper::mapToPost).collect(Collectors.toList());
}
public PostDTO save(final PostDTO postDTO) {
Post persistablePost = persistablePostMapper.mapToPersistablePost(postDTO);
Post savedPersistablePost = postRepository.save(persistablePost);
return persistablePostMapper.mapToPost(savedPersistablePost);
}
}
Проблема в том, что тест не проходит из-за: java.lang.IllegalStateException: дублирование источника ключа (попытка слияния) проверки значений и тест)
Сгенерированные SQL-запросы показывают, что удаляется только одна строка и только одни метаданные (тот, у которого есть «reference_id», а не тот, у которого есть «origin» ")
Это происходит, вероятно, потому, что метаданные в Post имеют orphanRemoval = true и по какой-то причине удаляется только "reference_id" и ' put' снова (из-за cascade.ALL).
Это означает, что во время запроса «обновления» в приведенном выше тесте у нас есть три метаданных в сообщении: 2x с ключом «origin» (старый и новый, поскольку orphanremoval его не удалил) и 1x с ключом «reference_id» (старый заменен новым).
Это вызывает проблему в: Posts.stream().map(persistablePostMapper::mapToPost).collect(Collectors.toList()); где он отображает метаданные:
stacktrace в исключении дублированного ключа указывает сюда (на toMap):
Может ли кто-нибудь объяснить мне, почему удаление потерянных файлов не воссоздает/удаляет всю коллекцию метаданных, а только «reference_id»? Почему метаданные с ключом «origin» также не воссоздаются?
[list] [*]Я использую Spring Boot 2.7.18 с Java 17 и maven. [*]Пожалуйста, относитесь к этому исключительно в образовательных целях ( не кричите на меня что фильтрация и обновление должны работать не так, я это знаю). [*]Я создал этот простой пример после прочтения статьи о 'лучший способ обработки исключения MultipleBagFetchException в спящем режиме' и наткнулся на проблему с правильным удалением потерянных объектов, отсюда и вопрос. [/list] У меня есть следующее объекты: [code]@Entity @Getter @Setter @Table(name = "post") @SequenceGenerator(name = "post_seq", sequenceName = "post_id_seq", allocationSize = 1) public class Post {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_seq") Long id;
@Enumerated(EnumType.STRING) PostStatus status; } [/code] и [code]@Entity @Getter @Setter @Table(name = "metadata") @SequenceGenerator(name = "post_metadata_seq", sequenceName = "post_metadata_id_seq", allocationSize = 1) public class Metadata {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_metadata_seq") Long id;
@ManyToOne @JoinColumn(name = "post_id") Post post;
@Column(name = "\"key\"") String key;
@Column(name = "\"value\"") String value; } [/code] и [code]@Entity @Getter @Setter @Table(name = "comment") @SequenceGenerator(name = "post_comment_seq", sequenceName = "post_comment_id_seq", allocationSize = 1) public class Comment {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_comment_seq") Long id;
@ManyToOne @JoinColumn(name = "post_id") Post post;
String text; } [/code] и соответствующие DTO: [code]@Builder(toBuilder = true) @Getter @Jacksonized public class PostDTO { Long id; MetadataDTO metadata; List comments; PostStatus status; } [/code] и [code]@ToString @Value(staticConstructor = "of") public class MetadataDTO {
Map metadata;
@JsonCreator @Builder(toBuilder = true) public MetadataDTO(@JsonProperty("metadata") final Map metadata) { this.metadata = Optional.ofNullable(metadata) .map(HashMap::new) .map(Collections::unmodifiableMap) .orElse(Map.of()); } } [/code] услуга: [code]@Service @RequiredArgsConstructor public class PostService {
public final PersistablePostMapper persistablePostMapper; public final PostRepository postRepository; public final EntityManager entityManager;
@Transactional public PostDTO saveAll(final PostDTO postDTO) throws BadRequestException { String referenceId = postDTO.getMetadata().getMetadata().get("reference_id"); List alreadyClosedPostIds = findAllByReferenceId(Long.valueOf(referenceId)).stream() .filter(p -> PostStatus.CLOSED.equals(p.getStatus())) .map(PostDTO::getId) .toList(); if (alreadyClosedPostIds.contains(postDTO.getId())) { throw new BadRequestException(); }
return save(postDTO); }
public List findAllByReferenceId(final Long referenceId) { List posts = entityManager.createQuery(""" select distinct p from Post p left join fetch p.metadata m where m.key=:key and m.value=:value""", Post.class) .setParameter("key", "reference_id") .setParameter("value", String.valueOf(referenceId)) .setHint(QueryHints.PASS_DISTINCT_THROUGH, false) .getResultList();
posts = entityManager.createQuery(""" select distinct p from Post p left join fetch p.comments l where p in :posts""" , Post.class) .setParameter("posts", posts) .setHint(QueryHints.PASS_DISTINCT_THROUGH, false) .getResultList(); return posts.stream().map(persistablePostMapper::mapToPost).collect(Collectors.toList()); }
public PostDTO save(final PostDTO postDTO) { Post persistablePost = persistablePostMapper.mapToPersistablePost(postDTO); Post savedPersistablePost = postRepository.save(persistablePost);
return persistablePostMapper.mapToPost(savedPersistablePost); } } [/code] контроллер: [code]@RequiredArgsConstructor @RestController @RequestMapping("/api/posts") public class PostController {
private final PostService postService;
@PostMapping PostDTO createOrUpdatePosts(@RequestBody final PostDTO postDTO) throws BadRequestException { return postService.saveAll(postDTO); } } [/code] и тест: [code]@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles(profiles = {"test"}) @AutoConfigureMockMvc class PostControllerTest {
@Autowired protected MockMvc mockMvc;
@Autowired protected ObjectMapper objectMapper;
@Autowired protected PostService postService;
private static final TypeReference POST_TYPE_REFERENCE = new TypeReference() { };
[list] [*]Проблема в том, что тест не проходит из-за: java.lang.IllegalStateException: дублирование источника ключа (попытка слияния) проверки значений и тест) [*]Сгенерированные SQL-запросы показывают, что удаляется только одна строка и только одни метаданные (тот, у которого есть «reference_id», а не тот, у которого есть «origin» ") [*]Это происходит, вероятно, потому, что метаданные в Post имеют orphanRemoval = true и по какой-то причине удаляется только "reference_id" и ' put' снова (из-за cascade.ALL). [*]Это означает, что во время запроса «обновления» в приведенном выше тесте у нас есть три метаданных в сообщении: 2x с ключом «origin» (старый и новый, поскольку orphanremoval его не удалил) и 1x с ключом «reference_id» (старый заменен новым). [*]Это вызывает проблему в: Posts.stream().map(persistablePostMapper::mapToPost).collect(Collectors.toList()); где он отображает метаданные: [/list] stacktrace в исключении дублированного ключа указывает сюда (на toMap): [code]default MetadataDTO mapToMetadataDTO(final List persistableMetadata) { Map metadata = persistableMetadata .stream() .filter(content -> content.getKey() != null && content.getValue() != null) .collect(Collectors.toMap(Metadata::getKey, Metadata::getValue)); return MetadataDTO.builder() .metadata(metadata) .build(); } [/code] Может ли кто-нибудь объяснить мне, почему удаление потерянных файлов не воссоздает/удаляет всю коллекцию метаданных, а только «reference_id»? Почему метаданные с ключом «origin» также не воссоздаются?
Я использую Spring Boot 2.7.18 с Java 17 и maven.
Пожалуйста, относитесь к этому исключительно в образовательных целях ( не кричите на меня
что фильтрация и обновление должны работать не так, я это знаю).
Я создал этот простой пример после...
Я использую Spring Boot 2.7.18 с Java 17 и maven.
Пожалуйста, относитесь к этому исключительно в образовательных целях ( не кричите на меня
что фильтрация и обновление должны работать не так, я это знаю).
Я создал этот простой пример после...
Я читал главу 7 книги C++ Concurrency в действии (2-е издание). Во время тестирования реализации очереди без блокировки, представленной в книге, я иногда наблюдал ситуацию, когда элементы, поставленные в очередь, терялись. Ниже представлена...
Я знаю, что вопрос может быть странным, но я пытаюсь передавать данные, которые заполняются в представлении и возвращаются к моему контроллеру, в другой вид, чтобы заполнить все переменные. Моя проблема заключается в том, что каждый раз, когда я...
У меня есть код, который дает сбой в большой системе.
Однако, по сути, код сводится к следующему псевдокоду.
Я удалил большую часть деталей, как и пытался свести это к минимуму;
Однако я не думаю, что это упускает что-то важное.