JPA EntityManager原生查询中MySQL全文搜索无法找到数据的问题
排查方案与解决思路
1. 检查Hibernate实际执行的SQL语句
你的application.yml已开启show-sql: true,运行测试时查看控制台输出的全文搜索SQL,确认和你在MySQL CLI中执行的语句完全一致。
- 重点关注单引号内的搜索词是否被不必要转义(比如变成
\'title*\'),转义错误会直接导致搜索失效。
2. 确认全文索引未被Hibernate覆盖
你同时配置了spring.jpa.hibernate.ddl-auto: update和spring.sql.init.schema-locations: schema.sql,可能出现Hibernate在schema.sql执行后重新生成表结构、覆盖全文索引的情况:
- 测试运行后,登录MySQL执行以下命令,确认全文索引存在:
SHOW CREATE TABLE notice;
- 如果索引丢失,建议将
ddl-auto改为none,避免Hibernate修改schema.sql创建的表结构;也可以在实体类中通过注解指定全文索引(替代手动创建):
@Entity @Table( indexes = { @Index(name = "ft_title_index", columnList = "title", fullText = true), @Index(name = "ft_content_index", columnList = "content", fullText = true) } ) public class Notice { // ... 实体字段 }
注:Hibernate注解配置全文索引配合NGRAM解析器时,优先用
schema.sql+ddl-auto:none的组合更稳妥。
3. 验证事务内数据的索引可见性
普通查询能查到数据,但全文索引更新可能存在延迟,在事务内插入数据后可手动触发索引更新:
- 在
@BeforeEach的em.persist(notice);后添加em.flush();,确保数据立即写入数据库并同步索引:
@BeforeEach void init() { Notice notice = Notice.builder() .author("user") .title("title") .content("content") .build(); em.persist(notice); em.flush(); // 强制刷入数据库 }
4. 统一JDBC连接与数据库的字符集配置
在数据源URL中添加字符集参数,确保和schema.sql指定的utf8_unicode_ci完全一致:
spring: datasource: url: jdbc:mysql://localhost:3306/testboard?useUnicode=true&characterEncoding=utf8&connectionCollation=utf8_unicode_ci
全文搜索对字符集、排序规则的一致性要求极高,即使普通查询不受影响,字符集不匹配也会导致索引无法命中。
5. 改用参数绑定传递搜索词
避免硬编码搜索词,用参数绑定方式排除Hibernate对字符串的转义问题:
List<Notice> lists = em.createNativeQuery( "SELECT * FROM notice WHERE MATCH (title) AGAINST (?);", Notice.class) .setParameter(1, "+title*") .getResultList();
6. 确认MySQL的NGRAM配置
虽然CLI能正常查询,仍可验证NGRAM的token尺寸是否符合预期:
SHOW VARIABLES LIKE 'ngram_token_size';
默认值为2,搜索词title长度为5,前缀搜索title*可正常匹配,若token尺寸被修改可能影响结果。
内容的提问来源于stack exchange,提问作者홍석호
相关产品推荐
相关产品推荐

