Skip to content

BeforeConvertCallback and auditing not invoked on nested object #1899

@jiallombardo

Description

@jiallombardo

Assume the following classes (with Lombok @Data annotations):

public interface PersistableEntity {
    long getId();
    void setId(long id);
}

@Data
public class Base implements PersistableEntity {
    @Id
    long baseId;
    String name; 
    @CreatedDate
   Timestamp created;

    @MappedColumn(idColumn="baseId", keyColumn="baseId")
    SequentialSet<Sub> items;

    @Override
    long getId() {
        return baseId;
    }

    @Override
    void setId(long id) {
        this.baseId = id;
    } 
}

@Data
public class Sub implements PersistableEntity{
    @Id
    long subId;
    long baseId;
    String data;
    @CreatedDate
    Timestamp created;

    @Override
    long getId() {
        return subId;
    } 
    @Override
    void setId(long id) {
        this.subId = id;
    } 
}

We also have a BeforeConvertCallback for each type, which gets the id from a PostgreSQL sequence.

@Log4j2
@Component
public class AbstractBeforeConvertCallback<T extends PersistableEntity> implements BeforeConvertCallback<T> {

    private static final String SEQUENCE_ID_REQUEST = "SELECT nextval('%s')";

    protected final JdbcTemplate jdbcTemplate;

    @Override
    public @NotNull T onBeforeConvert(@NotNull T value) {
        log.debug("Converting entity {}", value);
        if (value.getId() == DEFAULT_ID) {
            value.setId(getNextId());
        }

        return value;
    }

    private Long getNextId() {
        return jdbcTemplate.queryForObject(String.format(SEQUENCE_ID_REQUEST, getSequence()), Long.class);
    }
}

@Component
public class BaseBeforeConvertCallback extends AbstractBeforeConvertCallback<Base> {

    private static final String SEQUENCE = "sq_base";

    public BaseBeforeConvertCallback(JdbcTemplate jdbcTemplate) {
        super(jdbcTemplate);
    }

    @Override
    protected String getSequence() {
        return SEQUENCE;
    }
}

@Component
public class SubBeforeConvertCallback extends AbstractBeforeConvertCallback<Sub> {

    private static final String SEQUENCE = "sq_sub";

    public SubBeforeConvertCallback(JdbcTemplate jdbcTemplate) {
        super(jdbcTemplate);
    }

    @Override
    protected String getSequence() {
        return SEQUENCE;
    }
}

Let's consider a test example (in Groovy) for BaseRepository:

def saveObject = new Base(baseId: 1, name: 'SomeName', items: [new Sub(subId: 1, data: 'data')]
baseRepository.save(saveObject)

This results in an erroneous query being generated for the sub item. Here is a part of the resulting stacktrace (let's assume that the next value generated for the Base sequence was 5):

Batch entry 0 INSERT INTO "sub" ("data", "base_id", "sub_id", "created") VALUES (('data'), (5), (NULL), (NULL))

So, what I assume is happening here:

  1. The Base entity is being properly saved
  2. The repository then attempts to save the Sub entity, but fails, since the BeforeConvertCallback and the auditing logic are not invoked for it.

I'm not sure, whether the repository is intended to work like this at all (although the fact that it attempts to save the mapped entities certainly hints that it might have been intended), but this seems like an oversight. Or, perhaps, it was a structural issue on my part?

Thank you.

Metadata

Metadata

Assignees

Labels

has: design-decisionAn issue that contains a design decision about its topicstatus: invalidAn issue that we don't feel is valid

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions