如何使用JDBC在spring引导测试类上创建事务上下文



当我使用hibernate时,一旦我进行了测试,所有所做的更改都会在这些测试完成后回滚。

但是,当我在DAO实现中使用JDBC而不是JpaRepository时,测试过程中发生的突变不会被回滚。

如何才能回滚所有更改?

在这里你可以看到我的一个测试类是什么样子的:

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;
}
}

从DAO代码内的datasource获得的connection不参与之前由@DataJpaTest打开的spring托管事务。

由于新的DAO事务最终关闭,它可能已提交(取决于您没有显示的应用程序代码(。


要解决此问题,您可以通过用@Transactional对DAO代码进行注释,使其参与spring托管事务,以便在调用时使用任何已打开的事务。这样,您的代码将在由测试回滚的事务中运行
https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch11s05.html