'How to create a transactional context on spring boot test classes using JDBC?
When I was using hibernate, once I'd made a test, all the changes made were being rolled back once those tests are completed.
But when I use JDBC with my DAO implementations instead JpaRepositories, mutations made during tests are not being rolled back.
How can I get all the changes are being rolled back?
Here you can see how one of my test classes looks like:
package com.cemonan.bookdb2;
import com.cemonan.bookdb2.dao.BookDao;
import com.cemonan.bookdb2.domain.Book;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import java.util.List;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ComponentScan(basePackages = {"com.cemonan.bookdb2.dao"})
public class BookDaoIntegrationTest {
@Autowired
BookDao bookDao;
@Test
void testCreateBook() {
List<Book> books = bookDao.findAll();
int countBefore = books.size();
Book book = new Book();
book.setTitle("A book");
book.setIsbn("123");
book.setPublisher("Someone");
Book savedBook = bookDao.save(book);
books = bookDao.findAll();
int countAfter = books.size();
assertThat(savedBook).isNotNull();
assertThat(countAfter).isGreaterThan(countBefore);
}
}
package com.cemonan.bookdb2.dao;
import com.cemonan.bookdb2.domain.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import static com.cemonan.bookdb2.dao.utils.Utils.closeAll;
@Component
public class BookDaoImpl implements BookDao {
@Autowired
DataSource source;
Book mapRsToBook(ResultSet rs) throws SQLException {
Book book = new Book();
book.setId(rs.getLong("id"));
book.setTitle(rs.getString("title"));
book.setIsbn(rs.getString("isbn"));
book.setPublisher(rs.getString("publisher"));
return book;
}
@Override
public Book findById(Long id) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = this.source.getConnection();
ps = connection.prepareStatement("SELECT * FROM book WHERE id = ?");
ps.setLong(1, id);
rs = ps.executeQuery();
if (rs.next()) {
return mapRsToBook(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
closeAll(rs, ps, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public Book save(Book book) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = this.source.getConnection();
ps = connection.prepareStatement("INSERT INTO book (title, isbn, publisher) VALUES (?, ?, ?)");
ps.setString(1, book.getTitle());
ps.setString(2, book.getIsbn());
ps.setString(3, book.getPublisher());
ps.execute();
Statement stmt = connection.createStatement();
rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
if (rs.next()) {
return this.findById(rs.getLong(1));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
closeAll(rs, ps, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public Book update(Book book) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = this.source.getConnection();
ps = connection.prepareStatement("UPDATE book SET title = ?, isbn = ?, publisher = ? WHERE id = ?");
ps.setString(1, book.getTitle());
ps.setString(2, book.getIsbn());
ps.setString(3, book.getPublisher());
ps.setLong(4, book.getId());
ps.execute();
if (rs.next()) {
return this.findById(book.getId());
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
closeAll(rs, ps, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public void delete(Book book) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = this.source.getConnection();
ps = connection.prepareStatement("DELETE FROM book WHERE id = ?");
ps.setLong(1, book.getId());
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
closeAll(rs, ps, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Override
public Book findByTitle(String title) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = this.source.getConnection();
ps = connection.prepareStatement("SELECT * FROM book WHERE title = ?");
ps.setString(1, title);
rs = ps.executeQuery();
if (rs.next()) {
return this.mapRsToBook(rs);
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
closeAll(rs, ps, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public List<Book> findAll() {
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
List<Book> books = new ArrayList<>();
try {
connection = this.source.getConnection();
stmt = connection.createStatement();
rs = stmt.executeQuery("SELECT * FROM book");
while(rs.next()) {
Book book = this.mapRsToBook(rs);
books.add(book);
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
closeAll(rs, stmt, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return books;
}
}
Solution 1:[1]
The connection which is obtained from the datasource inside your DAO code does not participate in the spring-managed transaction, which was opened before by the @DataJpaTest.
And since the new DAO transaction is finally closed, it is probably committed (depending on your application code that you did not show..).
To fix this, you could make your DAO code participate in the spring-managed transaction by annotating it with @Transactional, so that it uses any already opened transaction when being called. This way your code will run within the transaction which is rollbacked by the test.
https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch11s05.html
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
