【问题标题】:How to create a transactional context on spring boot test classes using JDBC?如何使用 JDBC 在 Spring Boot 测试类上创建事务上下文?
【发布时间】:2022-01-26 16:39:03
【问题描述】:

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

但是当我将 JDBC 与我的 DAO 实现而不是 JpaRepositories 一起使用时,测试期间发生的突变不会被回滚。

我怎样才能让所有更改都被回滚?

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

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

【问题讨论】:

  • 您能出示您的BookDao 代码吗?
  • @fladdimir 我已将其添加到问题中。
  • 您不应该让 BookDao 事务性以使事情回滚吗?
  • 请修剪您的代码,以便更容易找到您的问题。请按照以下指南创建minimal reproducible example

标签: java spring hibernate jpa jdbc


【解决方案1】:

connection 是从你的 DAO 代码 does not participate in the spring-managed transaction 中的 datasource 获得的,之前由 @DataJpaTest 打开。

并且由于新的 DAO 事务终于关闭,它是probably committed(取决于您没有显示的应用程序代码..)。


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

【讨论】:

  • 谢谢@fladdimir 现在我了解情况了。
猜你喜欢
  • 2018-09-28
  • 2023-02-16
  • 2017-02-05
  • 2022-12-22
  • 2018-01-20
  • 1970-01-01
  • 2018-04-03
  • 2021-12-21
  • 1970-01-01
相关资源
最近更新 更多