我有一个页面以分页的方式显示网格中的一些记录。我有表my_table
和实体MyTable
链接到它:
@SuppressWarnings("serial")
@Entity
@Table(name="my_table")
@Inheritance(strategy=InheritanceType.JOINED)
public class MyTable extends BaseEntity implements Auditable, Serializable {
//...
private Integer myAttribute; //This does not exist in the table
//...
@Formula(value = "(myFunction(attr1, attr2))")
public Integer getMyAttribute() {
return myAttribute;
}
public void setMyAttribute(Integer myAttribute) {
this.myAttribute = myAttribute;
}
//...
}
当我打算通过“普通”字段进行查询时,一切都很有效,但是当我尝试按myAttribute
过滤时,例如:
queryInput.addAndCriterion(Restrictions.eq("myAttribute", v));
例如,v
是一个值为123的Integer
,搜索将会超时。如果我直接在MySQL中运行存储的函数,那么它会立即执行。我认为这段代码会对每个项目发送一个单独的请求,这可以解释问题。有没有办法确保我可以通过我的存储函数以高效的方式进行过滤(可能会在查询中生成对存储函数的调用)?我需要定义一个标准,规定每个记录需要调用一个存储的函数,并传递attr1
和attr2
,这些是记录的字段?
如下面的评论中所述,这个答案并没有解决Hibernate故障排除的问题,但是无论如何OP都喜欢它。
答案如下......
查询任何函数,存储函数或内置函数始终是表扫描。
例如,即使存在一个索引,也无法在create_date
上使用索引:
SELECT * FROM MyTable WHERE MONTH(create_date) = 2
任何时候使用索引列作为函数的参数都是如此。
MySQL 5.7及更高版本的解决方法是对该表达式使用generated column,然后为生成的列编制索引。
ALTER TABLE MyTable
ADD COLUMN created_month INT AS (MONTH(create_date)),
ADD INDEX (created_month);
一旦你这样做,你可以查询created_month = 2
或者你甚至可以查询原始表达式MONTH(create_date) = 2
,它将使用索引。
不幸的是,只有内置的MySQL函数才能使用此功能。
https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html说:
生成的列表达式必须遵守以下规则。如果表达式包含不允许的构造,则会发生错误。
- 不允许存储函数和用户定义的函数。
另一种解决方案是为您创建一个新的具体列来存储存储函数的结果,假设该值是从其参数确定的,并且不依赖于其他表中的数据状态。
ALTER TABLE MyTable
ADD COLUMN myAttribute INT,
ADD INDEX (myAttribute);
CREATE TRIGGER att_ins BEFORE INSERT ON MyTable
FOR EACH ROW SET NEW.myAttribute = MyFunction(NEW.attr1, NEW.attr2);
CREATE TRIGGER att_upd BEFORE UPDATE ON MyTable
FOR EACH ROW SET NEW.myAttribute = MyFunction(NEW.attr1, NEW.attr2);
然后,您将查询新列而不是表达式。
这有点麻烦,但这是对存储函数的结果进行索引查找的唯一方法。