Transaction isolation READ_UNCOMMITED doesn't work

719 views Asked by At

I have written an application using Springboot 2.0 to store books catalog in MySQL 8 database. Book information is being read from the file.

Book object has a list of authors as there can be more than one author to the book. At the same time, author can have many books. Therefore, I have many-to-many relationship and created three database tables: BOOKS, AUTHORS and AUTHORS_BOOKS. In my transaction, I first check if author already exist in the database and if not, inserting the author record. Than I store book info and at the end store relationship (IDs of two previous records) in the AUTHORS_BOOKS table. By default auto-commit is set to true and everything works fine. However, if I set it to false, I am getting org.springframework.dao.DataIntegrityViolationException while inserting those last records. Apparently, it can't read book and author ID inserted within same transaction.

Here is my application.properties:

spring.datasource.hikari.transaction-isolation=TRANSACTION_READ_UNCOMMITTED
spring.datasource.hikari.auto-commit: false
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.pool-name="con-pool"
spring.datasource.hikari.minimumIdle=2
spring.datasource.hikari.maximumPoolSize=4
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=60000

And here is the code:

@Transactional
public String storeCatalogEntry(Book book, String serId, short volNum) throws SQLException {
    List<String> authorsId = storeAuthors(book.getAuthors());
    String bookId = storeBookData(book, serId, volNum);
    storeBookAuthorRefs(bookId, authorsId);
    return bookId;
}

I am getting nested exception is java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (catalog_db.authors_books, CONSTRAINT FK_BOOK_AUTHORS FOREIGN KEY (BOOK_ID) REFERENCES books (BOOK_ID) ON DELETE CASCADE ON UPDATE CASCADE) at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:251)

I did try to do @Transactional(isolation = Isolation.READ_UNCOMMITED) but result is the same. I am using Spring JdbcTemplate to perform database operations. Here is my code:

    private List<String> storeAuthors(Collection<Author> authors) throws SQLException {
    List<String> ids = new ArrayList<>(authors.size());
    for (Author author : authors) {
        String id = findAuthor(author);
        if (id == null) {
            id = storeNewAuthor(author);
        }
        ids.add(id);
    }
    return ids;
}
private String storeNewAuthor(Author author) throws SQLException {
    String id = UUID.randomUUID().toString();
    String qry = queries.get("storeAuthor");
    try {
        jdbc.update(qry, (PreparedStatement ps) -> {
            ps.setString(1, id);
            if (author.getFirstName() == null) {
                ps.setNull(2, Types.VARCHAR);
            } else {
                ps.setString(2, author.getFirstName());
            }
            ps.setString(3, author.getLastName());
        });
    } catch (DataAccessException daex) {
        LOG.error("JDBC error", daex);
        throw new SQLException(daex);
    }
    return id;
}

private String storeBookData(Book book, String serialId, short volumeNumber) throws SQLException {
    String id = UUID.randomUUID().toString();
    String qry = queries.get("storeBookData");
    try {
        jdbc.update(qry, (PreparedStatement ps) -> {
            ps.setString(1, id);
            ps.setString(2, book.getTitle());
            ps.setString(3, book.getUrl());
            ...
            ps.setString(8, book.getAnnotation());
            ps.setString(9, serialId);
            ps.setShort(10, volumeNumber);
        });
    } catch (DataAccessException daex) {
        LOG.error("JDBC error", daex);
        throw new SQLException(daex);            
    }
    return id;
}

private void storeBookAuthorRefs(String bookId, List<String> authorsId) throws SQLException {
    String qry = queries.get("storeBookAuthorRef");
    for (int i = 0; i < authorsId.size(); i++) {
        String authId = authorsId.get(i);
        short v = (short) i;
        try {
            jdbc.update(qry, (PreparedStatement ps) -> {
                ps.setString(1, bookId);
                ps.setString(2, authId);
                ps.setShort(3, v);
            });
        } catch (DataAccessException daex) {
            LOG.error("JDBC error", daex);
            throw new SQLException(daex);
        }
    }
}

Can anyone suggest what I am doing wrong and how to fix it?

0

There are 0 answers