我想使用JPA使用MySQL's full text search功能,而不必使用本机查询。
我正在使用EclipseLink,它具有支持本机SQL命令的功能:FUNC。但是,帮助示例仅显示这与简单的MySQL函数一起使用。我尽最大的努力让它与MATCH&反对如下:
@PersistenceContext(name="test")
EntityManager em;
Query query = em.createQuery("SELECT person FROM People person WHERE FUNC('MATCH', person.name) FUNC('AGAINST', :searchTerm)");
...
query.getResultList();
这给出了以下例外:
Caused by: NoViableAltException(32@[()* loopback of 822:9: (m= MULTIPLY right= arithmeticFactor | d= DIVIDE right= arithmeticFactor )*])
at org.eclipse.persistence.internal.libraries.antlr.runtime.DFA.noViableAlt(DFA.java:159)
at org.eclipse.persistence.internal.libraries.antlr.runtime.DFA.predict(DFA.java:116)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.arithmeticTerm(JPQLParser.java:4557)
... 120 more
我对使用FUNC方法的替代方案持开放态度。
我正在使用EJB 3和EclipseLink 2.3.1。
答案 0 :(得分:8)
改进了@Markus Barthlen的答案,适用于Hibernate。
public class MySQLDialectCustom extends MySQL5Dialect {
public MySQLDialect() {
super();
registerFunction("match", new SQLFunctionTemplate(StandardBasicTypes.DOUBLE,
"match(?1) against (?2 in boolean mode)"));
}
}
并通过设置hibernate.dialect
属性来注册它。
:
Query query = entityManager
.createQuery("select an from Animal an " +
"where an.type = :animalTypeNo " +
"and match(an.name, :animalName) > 0", Animal.class)
.setParameter("animalType", "Mammal")
.setParameter("animalName", "Tiger");
List<Animal> result = query.getResultList();
return result;
或使用Criteria API:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Animal> criteriaQuery = criteriaBuilder.createQuery(Animal.class);
Root<Animal> root = criteriaQuery.from(Animal.class);
List<Predicate> predicates = new ArrayList<>();
Expression<Double> match = criteriaBuilder.function("match", Double.class, root.get("name"),
criteriaBuilder.parameter(String.class, "animalName"));
predicates.add(criteriaBuilder.equal(root.get("animalType"), "Mammal"));
predicates.add(criteriaBuilder.greaterThan(match, 0.));
criteriaQuery.where(predicates.toArray(new Predicate[]{}));
TypedQuery<Animal> query = entityManager.createQuery(criteriaQuery);
List<Animal> result = query.setParameter("animalName", "Tiger").getResultList();
return result;
此博文中的更多详细信息:http://pavelmakhov.com/2016/09/jpa-custom-function
答案 1 :(得分:5)
FUNC仅适用于普通打印功能,
即。 MATCH(arg1,arg2)
因为MATCH arg1 AGAINST arg2没有按照正常打印功能的方式打印,所以FUNC不能用来调用它。
EclipseLink ExpressionOperators确实支持这样的打印功能,因此您可以定义自己的ExpressionOperator,但ExpressionOperators目前仅通过EclipseLink Expression查询支持,而不是通过JPQL支持。您可以记录增强功能以在JPQL中获得操作员支持。
您也可以使用本机SQL查询。
答案 2 :(得分:4)
只是为了完成答案:我遇到了同样的问题,但是使用了标准构建器。如果您正在使用EclipseLink,那么就可以解决标准实现中的限制:
示例:强>
JpaCriteriaBuilder cb = (JpaCriteriaBuilder) cb;
List<String> args = new ArrayList();
args.add("Keyword");
Expression<Boolean> expr = cb.fromExpression (
cb.toExpression(
cb.function("", String.class,
table.get(Table_.text1), table.get(Table_.text2))
)
.sql("MATCH ? AGAINST (?)", args)
);
query.where(expr);
如果需要将表达式强制转换为谓词,请使用以下命令:
query.where( cb.gt(expr, 0));
答案 3 :(得分:2)
EclipseLink 4.0中的新SQL运算符怎么样?我认为它可以帮助您从JPQL进行全文搜索。但是你必须升级到EclipseLink 4.0。
修改强>
抱歉迟到了。
已正确使用EclispeLink 2.4.0“SQL”运算符和MySQL全文搜索
SELECT person FROM People person WHERE SQL('MATCH(name) AGAINST( ? )', :searchTerm)"
其中name
是定义了全文索引的列。 :searchTerm
是您用于搜索的字符串
工作没有问题。
答案 4 :(得分:1)
详细说明詹姆斯的答案:
似乎我有幸使用
扩展了mysql方言registerFunction("match", new SQLFunctionTemplate(DoubleType.INSTANCE, "match(?1) against (?2 in boolean mode)"));
并通过以下jpql片段调用该函数
match(" + binaryDataColumn + ",'" + StringUtils.join(words, " ") + "') > 0
我不得不猜测返回类型,但这应该让你开始。
答案 5 :(得分:0)
最终工作
如果您将表格列设置为索引全搜索
@NamedNativeQuery(name =“searchclient”, query =“SELECT * FROM client WHERE MATCH(clientFullName,lastname,secondname,firstphone,” +“第二部电话,工作电话,其他电话,其他电话1,” +“otherphone2,detailsFromClient,email,company,” +“address,contractType,paymantCondition)AGAINST(?)”,
列表list = em.createNamedQuery(“searchclient”)。setParameter(1,searchKey).getResultList();
答案 6 :(得分:0)
最简单的变体是使用NativeQuery
使用它与映射到JPA实体(FiasAddress)的示例:
public class FiasServiceBean implements FiasService {
@PersistenceContext(unitName = "fias")
EntityManager entityManager;
@Override
public Collection<FiasAddress> search(String name, int limit, int aolevel) {
Query query = entityManager.createNativeQuery(
"SELECT fa.* FROM fias.addressobject fa" +
" WHERE MATCH(FORMALNAME) AGAINST (:name IN NATURAL LANGUAGE MODE)" +
" AND AOLEVEL = :AOLEVEL" +
" LIMIT :limit",
FiasAddress.class
);
query.setParameter("name", name);
query.setParameter("limit", limit);
query.setParameter("AOLEVEL", aolevel);
Iterator iterator = query.getResultList().iterator();
ArrayList<FiasAddress> result = new ArrayList<>();
while (iterator.hasNext()) {
result.add((FiasAddress) iterator.next());
}
return result;
}
}