Проблема с правильным использованием удаления потерянных файловJAVA

Программисты JAVA общаются здесь
Ответить Пред. темаСлед. тема
Anonymous
 Проблема с правильным использованием удаления потерянных файлов

Сообщение Anonymous »

  • Я использую Spring Boot 2.7.18 с Java 17 и maven.
  • Пожалуйста, относитесь к этому исключительно в образовательных целях ( не кричите на меня
    что фильтрация и обновление должны работать не так, я это знаю).
  • Я создал этот простой пример после прочтения статьи о
    'лучший способ обработки исключения MultipleBagFetchException в спящем режиме' и наткнулся
    на проблему с правильным удалением потерянных объектов, отсюда и вопрос.
У меня есть следующее объекты:

Код: Выделить всё

@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;

@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
List comments;

@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(fetch = FetchType.EAGER, mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
List metadata;

@Enumerated(EnumType.STRING)
PostStatus status;
}
и

Код: Выделить всё

@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;
}
и

Код: Выделить всё

@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;
}
и соответствующие DTO:

Код: Выделить всё

@Builder(toBuilder = true)
@Getter
@Jacksonized
public class PostDTO {
Long id;
MetadataDTO metadata;
List comments;
PostStatus status;
}
и

Код: Выделить всё

@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());
}
}
услуга:

Код: Выделить всё

@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);
}
}
контроллер:

Код: Выделить всё

@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);
}
}
и тест:

Код: Выделить всё

@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() {
};

@Test
void shouldUpdatePostComments() throws Exception {
//given
CommentDTO comment1 = CommentDTO.builder()
.text("test1")
.build();
CommentDTO comment2 = CommentDTO.builder()
.text("test2")
.build();
List commentsBeforeUpdate = List.of(comment1);
List commentsAfterUpdate = List.of(comment1, comment2);
PostDTO postWithOneComment = PostDTO.builder()
.status(PostStatus.OPEN)
.metadata(MetadataDTO.builder()
.metadata(Map.of(
"reference_id", "100",
"origin", "test"))
.build())
.comments(commentsBeforeUpdate)
.build();
PostDTO savedPost = postService.save(postWithOneComment);

List  postBeforeUpdate = postService.findAllByReferenceId(100L);

//when
MockHttpServletResponse response = mockMvc.perform(post("/api/posts")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(savedPost.toBuilder()
.comments(commentsAfterUpdate)
.build())))
.andReturn().getResponse();

//then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
PostDTO returnedPost = objectMapper.readValue(response.getContentAsString(), POST_TYPE_REFERENCE);
PostDTO postAfterUpdate = postService.findAllByReferenceId(100L).get(0);

assertThat(returnedPost).isEqualTo(postAfterUpdate);
assertThat(postBeforeUpdate.size()).isEqualTo(1);
assertThat(postBeforeUpdate.get(0).getComments()).isEqualTo(commentsBeforeUpdate);
assertThat(postAfterUpdate.getComments()).isEqualTo(commentsAfterUpdate);
}
}
  • Проблема в том, что тест не проходит из-за: 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):

Код: Выделить всё

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();
}
Может ли кто-нибудь объяснить мне, почему удаление потерянных файлов не воссоздает/удаляет всю коллекцию метаданных, а только «reference_id»? Почему метаданные с ключом «origin» также не воссоздаются?

Подробнее здесь: https://stackoverflow.com/questions/786 ... an-removal
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Проблема с правильным использованием удаления потерянных файлов
    Anonymous » » в форуме JAVA
    0 Ответы
    20 Просмотры
    Последнее сообщение Anonymous
  • Проблема с правильным использованием удаления потерянных файлов
    Anonymous » » в форуме JAVA
    0 Ответы
    21 Просмотры
    Последнее сообщение Anonymous
  • К вопросу о потерянных элементах в lock-free очереди из "C++ Concurrency in Action"
    Anonymous » » в форуме C++
    0 Ответы
    43 Просмотры
    Последнее сообщение Anonymous
  • Как передать модель из представления, в другой вид, без потерянных данных?
    Anonymous » » в форуме C#
    0 Ответы
    8 Просмотры
    Последнее сообщение Anonymous
  • Почему деструктор удаления вектора вызывается в результате скалярного удаления?
    Гость » » в форуме C++
    0 Ответы
    90 Просмотры
    Последнее сообщение Гость

Вернуться в «JAVA»