0

In postgresql, I have a table Student(id, name), Guardian(id, type,name) and a relationship table StudentXGuardian, a Student table has a column Guardian.

And I have a constraint to constraint a Student must have a Guardian which has type primary. This is my constraint, which will be triggered after a transaction:

CREATE OR REPLACE FUNCTION ensure_primary_guardian_on_delete()
RETURNS TRIGGER AS $$
BEGIN
    -- Check if the guardian being deleted is a primary guardian
    IF OLD.guardian_id IN (SELECT id FROM Guardian WHERE type = 'primary') THEN
        -- Check if deleting this guardian would leave the student without a primary guardian
        IF NOT EXISTS (
            SELECT 1
            FROM StudentXGuardian
            WHERE student_id = OLD.student_id
              AND guardian_id != OLD.guardian_id
              AND guardian_id IN (SELECT id FROM Guardian WHERE type = 'primary')
        ) THEN
            RAISE EXCEPTION 'Cannot delete the only primary guardian for the student';
        END IF;
    END IF;
    RETURN OLD;
END;
$$ LANGUAGE plpgsql;


CREATE TRIGGER check_primary_guardian_on_delete
AFTER DELETE ON StudentXGuardian
INITIALLY DEFERRED
FOR EACH ROW EXECUTE FUNCTION ensure_primary_guardian_on_delete();

This constraint works fine when I try it with pure sql. However it doesn't work in integration test in spring.

And I have a repository which has methods addStudentXGuardian(...), and deleteStudentXGuardian(...) With a test method:

@Transactional
@Test
void myTest() {
  var primaryGuardian=...;
  addStudentXGuardian(primaryGuardian);
  assertThrows(
        QueryExecutionException.class,deleteStudentXGuardian(primaryGuardian));
}

And this method passes.

I know you should not use @Transactional, because the exception will only be thrown after the transaction, but the transaction happens after the assertThrows(...).

I have tried to use TransactionTemplate in the test, and it throws exception saying "Cannot delete the only primary guardian for the student".

I also have noticed that Spring treats @Transactional differently like described in the document. Does that mean that my the transaction is committed only after the test method returning when using @Transactional? I have debugged it, it seems like so.

However when I the debugger went to commit the transaction eventually after the test method, it still didn't throw this exception. What is wrong here?

Thank you for reading this!

1
  • There is only a rollback after the test execution not a commit. That is the transactional boundary, before that nothing will be triggered. Assuming you are using JPA you can inject the EntityManager or even better the TestEntityManager and call flush to simulate (sort of) a transaction commit at the points you want it.
    – M. Deinum
    Commented Jun 10 at 14:31

0