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?