【问题标题】:Substring search a numeric field with JPA/Hibernate使用 JPA/Hibernate 搜索数字字段的子字符串
【发布时间】:2017-04-02 17:34:41
【问题描述】:

我有一个具有数字字段的 JPA 实体。比如:

@Basic(optional = false)
@Column(name = "FISCAL_YEAR", nullable = false)
private int fiscalYear;

我需要对这个字段进行子字符串搜索。例如,我想搜索17 以得到201719171789。暂时忘记这是多么疯狂的请求,并假设我有一个有意义的真实用例。将列更改为数据库中的 varchar 不是一种选择。

在 PL/SQL 中,我会将字段转换为 varchar 并执行 like '%17%'。在不使用本机查询的情况下,我将如何使用 Hibernate/JPA 完成此操作?我需要能够使用 HQL 或 Criteria 来做同样的事情。

【问题讨论】:

标签: sql hibernate jpa hql


【解决方案1】:

使用标准构建器

在数值上实现like

表格

Employee | CREATE TABLE `Employee` (
`id` int(11) NOT NULL,
`first` varchar(255) DEFAULT NULL,
`last` varchar(255) DEFAULT NULL,
`occupation` varchar(255) DEFAULT NULL,
`year` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

实体

private Integer year;
public Integer getYear() {
    return year;
}
public void setYear(Integer year) {
    this.year = year;
}

表格中的数据

+----+-------+------+------------+------+
| id | first | last | occupation | year |
+----+-------+------+------------+------+
|  2 | Ravi  | Raj  | Textile    | 1718 |
|  3 | Ravi  | Raj  | Textile    | 1818 |
|  4 | Ravi  | Raj  | Textile    | 1917 |
|  5 | Ravi  | Raj  | Textile    | NULL |
|  6 | Ravi  | Raj  | Textile    | NULL |
|  7 | Ravi  | Raj  | Textile    | NULL |
+----+-------+------+------------+------+

使用标准构建器构建查询

public List<Employee> getEmployees() {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Employee> q = cb.createQuery(Employee.class);
    Root<Employee> emp = q.from(Employee.class);
    Predicate year_like = cb.like(emp.<Integer>get("year").as(String.class), "%17%");
    CriteriaQuery<Employee> fq = q.where(year_like);

    List<Employee> resultList = (List<Employee>) entityManager.createQuery(fq).getResultList();
    return resultList;
}

查询生成(使用 show_sql: true)

Hibernate: select employee0_.id as id1_0_, employee0_.first as first2_0_, employee0_.last as last3_0_, employee0_.occupation as occupati4_0_, employee0_.year as year5_0_ from Employee employee0_ where cast(employee0_.year as char) like ?

查询输出

// i have printed only id and year in the console
id, year
2, 1718
4, 1917

----------------------------------------------- -------------

替代方式
LIKE在使用 JPA、hibernate、mysql 测试时在 JPA 中用于数字字段。
注意:-可能不适用于其他 jpa提供者

Query r = entityManager.createQuery("select c from Employee c where c.year like '%17%'");

查询已触发(使用 show_sql=true)

Hibernate: select employee0_.id as id1_0_, employee0_.first as first2_0_, employee0_.last as last3_0_, employee0_.occupation as occupati4_0_, employee0_.year as year5_0_ from Employee employee0_ where employee0_.year like '%17%'

查询结果

// i have printed only id and year in the console
id, year
2, 1718
4, 1917

【讨论】:

  • 不。 JPQL 规范非常明确。仅适用于字符串。因此,您不再独立于供应商或符合 JPA 规范
  • 有关于这个的链接吗?
  • like_expression ::= string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]。 “字符串”!=“数字”
  • @NeilStockton 我同意“喜欢”对数值没有意义。我只是尝试了一个特定的测试设置并且它有效。无论如何,我已经使用标准生成器更新了我的解决方案
【解决方案2】:

你可以声明自己的Criterion类型

public class CrazyLike implements Criterion {

    private final String propertyName;
    private final int intValue;

    public CrazyLike(String propertyName, int intValue) {
        this.propertyName = propertyName;
        this.intValue = intValue;
    }

    @Override
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
            throws HibernateException {
        final String[] columns = criteriaQuery.findColumns( propertyName, criteria );
        if ( columns.length != 1 ) {
            throw new HibernateException( "Crazy Like may only be used with single-column properties" );
        }

        final String column = columns[0];
        return "cast(" + column + " as text) like  '%" + intValue + "%'";
    }

    @Override
    public TypedValue[] getTypedValues(Criteria criteria,
            CriteriaQuery criteriaQuery) throws HibernateException {
        return new TypedValue[] { };    
    }

}

然后像这样使用它:

Criteria criteria = session.createCriteria(Person.class);
List<Person> persons = criteria.add(new CrazyLike("year", 17)).list();

假设Person 有一个名为year 的int 属性。这应该会产生这样的 SQL:

select
    this_.id as id1_2_0_,
    this_.birthdate as birthdat2_2_0_,
    this_.firstname as firstnam3_2_0_,
    this_.lastname as lastname4_2_0_,
    this_.ssn as ssn5_2_0_,
    this_.version as version6_2_0_,
    this_.year as year7_2_0_ 
from
    Person this_ 
where
    cast(this_.year as text) like  '%17%'

这是用 Postgres 测试的。 cast() 语法可能因您的数据库引擎而异。如果是,只需在您实现的 Criterion 类中使用该语法即可。

【讨论】:

    猜你喜欢
    • 2011-02-14
    • 1970-01-01
    • 2021-04-25
    • 2016-01-14
    • 2013-05-10
    • 2023-04-02
    • 2015-09-28
    • 2016-06-21
    • 2013-11-09
    相关资源
    最近更新 更多