Hibernate / Spring Data -获取已执行的SQL查询计数

ajsxfq5m  于 2023-03-19  发布在  Spring
关注(0)|答案(3)|浏览(161)

我的工作在演示文稿中,我想显示在deleteByPost()方法之间执行的sql查询数量的差异与自定义查询。我期待的方法没有自定义查询执行10001删除查询,并与它只有2。
我知道Hibernate的Statistics对象和它的方法,我希望其中一个名为getQueryExecutionCount()的对象返回针对db执行的sql查询的数量,但我得到的总是0。
如果有人想知道是否启用了hib统计数据,那就肯定了,因为我在其他属性(如已删除实体的计数)上得到了正确的数字。
下面是一个完整的例子,展示了我正在努力完成的工作。
有没有一种方法可以使用Statistics或其他机制来获得生成和执行的查询的数量?目前我正在查看日志(hibernate.show_sql)并计算打印的查询,但我觉得这似乎是错误的。

package example5

import org.hibernate.SessionFactory
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import org.springframework.test.context.junit.jupiter.SpringJUnitJupiterConfig
import org.springframework.transaction.annotation.EnableTransactionManagement
import org.springframework.transaction.annotation.Transactional
import javax.persistence.*

// ENTITIES

@Entity
@Table(name = "posts")
class Post(
        @Id
        @Column(name = "id")
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        var id: Long? = null,

        @Version
        @Column(name = "version")
        var version: Long? = null
)

@Entity
@Table(name = "comments")
class Comment(
        @Id
        @Column(name = "id")
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        var id: Long? = null,

        @Version
        @Column(name = "version")
        var version: Long? = null,

        @JoinColumn(name = "post_id")
        @ManyToOne(fetch = FetchType.LAZY)
        var post: Post? = null
)

// REPOSITORIES

@Repository
interface PostRepository : PagingAndSortingRepository<Post, Long>

@Repository
interface CommentRepository : PagingAndSortingRepository<Comment, Long> {

    @Modifying
    @Query("delete from Comment c where c.post = :post")
    fun deleteByPost(@Param("post") post: Post)
}

// SERVICES

interface PostService {

    fun delete(post: Post)
}

@Service
open class PostServiceImpl(
        @Autowired
        val postRepository: PostRepository,

        @Autowired
        val commentRepository: CommentRepository
) : PostService {
    @Transactional
    override fun delete(post: Post) {
        commentRepository.deleteByPost(post)
        postRepository.delete(post)
    }

}

// CONFIGURATION

@EnableJpaRepositories(basePackages = ["example5"])
@EnableTransactionManagement
@SpringBootApplication(scanBasePackages = ["example5"])
open class FrameworkApplication

// TESTS

@SpringJUnitJupiterConfig(classes = [FrameworkApplication::class])
class Example5(
        @Autowired
        val postService: PostService,

        @Autowired
        val postRepository: PostRepository,

        @Autowired
        val commentRepository: CommentRepository,

        @Autowired
        val emFactory: EntityManagerFactory
) {

    @AfterEach
    fun cleanUp() {
        commentRepository.deleteAll()
        postRepository.deleteAll()
    }

    @Test
    fun testDelete() {
        //given
        var post = Post()
        post = postRepository.save(post)
        val comments = mutableListOf<Comment>()
        for (i in 1..10000) {
            val comment = Comment()
            comment.post = post
            comments.add(comment)
        }
        commentRepository.save(comments)

        val sessionFactory = emFactory.unwrap(SessionFactory::class.java)
        val statistics = sessionFactory.statistics

        //then
        statistics.clear()
        postService.delete(post)

        val executedQueryCount = statistics.queryExecutionCount

        //then
        assertAll(
                { assertEquals(0, postRepository.count()) },
                { assertEquals(0, commentRepository.count()) },
                { assertEquals(2, executedQueryCount) }
        )
    }

}
olhwl3o2

olhwl3o21#

Spring Hibernate Query Utils(https://github.com/yannbriancon/spring-hibernate-query-utils)库提供了一个查询计数器,您可以使用它来检查生成的查询数。
如果您更喜欢自己做,Hibernate提供了一个类EmptyInterceptor,它包含一个名为onPrepareStatement的钩子,您可以扩展这个类并在onPrepareStatement钩子中添加逻辑来计算查询。
查看库代码,了解如何配置迭代器。

ujv3wf0j

ujv3wf0j2#

方法onPrepareStatement现在被弃用,在新的Hibernate 6版本中被删除。检查SQL的新方法是实现StatementInspector
我写了一个小库(https://github.com/Lemick/hibernate-query-asserts),它可以AssertSpring测试中由Hibernate生成的按类型(SELECT,INSERT,...)的SQL查询的计数,这样,每当SQL语句在测试中发生变化时,您就可以得到警告,并防止N+1选择。如果您想知道这是如何实现的,您可以在这里查看项目。
演示目的的测试示例:

@Test
@Transactional
@AssertHibernateSQLCount(inserts = 3)
void create_two_blog_posts() {
    BlogPost post_1 = new BlogPost("Blog post 1");
    post_1.addComment(new PostComment("Good article"));
    post_1.addComment(new PostComment("Very interesting"));
    blogPostRepository.save(post_1);
}
vxqlmq5t

vxqlmq5t3#

我找到了一个不需要库的解决方案:

@PersistenceContext
public EntityManager entityManager;

Statistics statistics;

@BeforeEach
void setUp() {
    Session session = entityManager.unwrap(Session.class);
    statistics = session.getSessionFactory().getStatistics();
    statistics.clear();
}

@Test
@Transactional
@Commit
void insertEmployees() {
    int inserts = 10;
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
        @Override
        public void afterCommit() {
            assertEquals(inserts, statistics.getEntityInsertCount());
        }
    });

    List<Employee> employees = new ArrayList<>();
    for (int i = 0; i < inserts; i++) {
        Employee entity = Employee.builder()
                .employeeId(i + 500000)
                .firstName("John")
                .lastName("Doe")
                .birthDate(LocalDate.now().minusYears(25))
                .hireDate(LocalDate.now().minusYears(2))
                .gender(Gender.M)
                .build();
        employees.add(entity);
    }
    employeeRepository.saveAll(employees);
}

通过研究统计学中的方法,你应该能够Assert大多数事情。

相关问题