Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Migration: Setter changes are persisted before data verification checks #12779

Open
marquestye opened this issue Feb 24, 2024 · 1 comment
Labels
c.Bug Bug/defect report committers only Difficult; better left for committers or more senior developers
Milestone

Comments

@marquestye
Copy link
Contributor

marquestye commented Feb 24, 2024

In some logic class methods,

  1. an entity is retrieved from the database,
  2. the entity's fields are updated with new values, before
  3. the entity is passed into an db.updateEntity() method, which then performs the verification checks.

For example:

// update question
question.setQuestionNumber(updateRequest.getQuestionNumber());
question.setDescription(updateRequest.getQuestionDescription());
question.setQuestionDetails(updateRequest.getQuestionDetails());
question.setGiverType(updateRequest.getGiverType());
question.setRecipientType(updateRequest.getRecipientType());
question.setNumOfEntitiesToGiveFeedbackTo(updateRequest.getNumberOfEntitiesToGiveFeedbackTo());
question.setShowResponsesTo(updateRequest.getShowResponsesTo());
question.setShowGiverNameTo(updateRequest.getShowGiverNameTo());
question.setShowRecipientNameTo(updateRequest.getShowRecipientNameTo());
// validate questions (giver & recipient)
String err = question.getQuestionDetailsCopy().validateGiverRecipientVisibility(question);
if (!err.isEmpty()) {
throw new InvalidParametersException(err);
}
// validate questions (question details)
FeedbackQuestionDetails questionDetails = question.getQuestionDetailsCopy();
List<String> questionDetailsErrors = questionDetails.validateQuestionDetails();

for (FeedbackResponseComment oldResponseComment : oldResponseComments) {
if (isGiverSectionChanged) {
oldResponseComment.setGiverSection(newResponse.getGiverSection());
}
if (isRecipientSectionChanged) {
oldResponseComment.setRecipientSection(newResponse.getRecipientSection());
}
frcLogic.updateFeedbackResponseComment(oldResponseComment);

However, according to the Hibernate documentation, changes made by the setters are automatically persisted into the database, even though the new values could be invalid and fail the verification checks. This results in the database entity being polluted with invalid data.

I've tested this locally on NotificationsLogic.updateNotification:

notification.setStartTime(startTime);
notification.setEndTime(endTime);
notification.setStyle(style);
notification.setTargetUser(targetUser);
notification.setTitle(title);
notification.setMessage(message);
if (!notification.isValid()) {
throw new InvalidParametersException(notification.getInvalidityInfo());
}

    @Test
    public void testUpdateNotification_invalidParameters_originalUnchanged() throws Exception {

        Notification notif = new Notification(
                Instant.parse("2011-01-01T00:00:00Z"),
                Instant.parse("2099-01-01T00:00:00Z"),
                NotificationStyle.DANGER,
                NotificationTargetUser.GENERAL,
                "A deprecation note",
                "<p>Deprecation happens in three minutes</p>");
        notificationsLogic.createNotification(notif);

        String invalidLongTitle = "1234567890".repeat(10);

        assertThrows(
                InvalidParametersException.class,
                () -> notificationsLogic.updateNotification(
                        notif.getId(),
                        Instant.parse("2011-01-01T00:00:00Z"),
                        Instant.parse("2099-01-01T00:00:00Z"),
                        NotificationStyle.DANGER,
                        NotificationTargetUser.GENERAL,
                        invalidLongTitle,
                        "<p>Deprecation happens in three minutes</p>"
                )
        );

        Notification actual = notificationsLogic.getNotification(notif.getId());
        assert actual.getTitle().equals("A deprecation note") : actual.getTitle();
        // java.lang.AssertionError: 12345678901234567890...

    }

The update fails, but the entity retrieved from the database afterwards is still updated with invalidLongTitle.

@cedricongjh cedricongjh added committers only Difficult; better left for committers or more senior developers c.Bug Bug/defect report labels Feb 25, 2024
@cedricongjh cedricongjh added this to the V9.0.0-beta.0 milestone Feb 25, 2024
@jayasting98
Copy link
Contributor

The only solutions I can think of are to validate before using the setters, or evicting the object from the session cache.

void evict(Object object) throws HibernateException
Remove this instance from the session cache. Changes to the instance will not be synchronized with the database. This operation cascades to associated instances if the association is mapped with cascade="evict".

See also:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c.Bug Bug/defect report committers only Difficult; better left for committers or more senior developers
Projects
None yet
Development

No branches or pull requests

4 participants