【问题标题】:How to cast string to IP Address in Spring Data JPA Query如何在 Spring Data JPA Query 中将字符串转换为 IP 地址
【发布时间】:2020-10-16 23:51:15
【问题描述】:

我有一个表,其中包含 IP 地址范围,在它们自己的列中具有起始值和结束值。这些列的类型为 varchar。我需要查询数据库以获取范围涵盖该 IP 地址的行。

这是我的查询,与 PGSQL 完美配合:

select * from ip_whitelist iw where iw.ip_address::inet <= '1.1.1.54'::inet and iw.ip_address_end::inet >= '1.1.1.54'::inet

1.1.1.54 在这里只是一个虚拟值,它将被我的 JPA 查询中的一个变量替换:

这是我尝试通过 JPA 存储库运行此查询的方式:

@Query("select ip from AllowedIp ip where CAST(ip.ipAddress AS inet) <= CAST((?1) AS inet) and CAST(ip.ipAddressEnd AS inet) >= CAST((?1) AS inet)")
List<AllowedIp> findDuplicatesByIP(String ipAddress);

我的问题是我需要在此 JPA 查询中将 ipAddressipAddressEnd 列转换为 inet,以便我可以使用 = 运算符来搜索范围是否已经覆盖给定的 IP。当我尝试启动我的 Tomcat 应用程序时,上面提到的 JPA 查询会引发错误。这是堆栈跟踪:

    Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: Could not resolve requested type for CAST : inet [select ip from com.uxl.dataobjects.domain.AllowedIp ip where CAST(ip.ipAddress AS inet) <= CAST((?1) AS inet) and CAST(ip.ipAddressEnd AS inet) >= CAST((?1) AS inet)]
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:331)
        at sun.reflect.GeneratedMethodAccessor69.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:344)
        at com.sun.proxy.$Proxy152.createQuery(Unknown Source)
        at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:86)
        ... 101 more
    Caused by: org.hibernate.QueryException: Could not resolve requested type for CAST : inet [select ip from com.uxl.dataobjects.domain.AllowedIp ip where CAST(ip.ipAddress AS inet) <= CAST((?1) AS inet) and CAST(ip.ipAddressEnd AS inet) >= CAST((?1) AS inet)]
        at org.hibernate.QueryException.generateQueryException(QueryException.java:137)
        at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
        at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
        at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
        at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:131)
        at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:93)
        at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:167)
        at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:301)
        at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:236)
        at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1836)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:328)
        ... 107 more
    Caused by: org.hibernate.QueryException: Could not resolve requested type for CAST : inet
        at org.hibernate.hql.internal.ast.tree.CastFunctionNode.resolve(CastFunctionNode.java:87)
        at org.hibernate.hql.internal.ast.HqlSqlWalker.processCastFunction(HqlSqlWalker.java:1097)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.functionCall(HqlSqlBaseWalker.java:2748)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1342)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4686)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:4252)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2104)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2029)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:796)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:597)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:301)
        at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:249)
        at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:278)
        at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
        ... 115 more

我探索过但没有成功的可能解决方案:

  1. 改为将字符串 IP 转换为 java 中的 InetAddress 数据类型。这不起作用,因为表列仍为字符串类型,因此仍需要强制转换才能使用运算符。
  2. 在 Java 中获取所有行和过滤器。虽然这可能有效,但这似乎是一个不切实际的解决方案,更不用说糟糕的做法了。
  3. 我发现Vlad Mihalcea's guide 使用自定义休眠类型并将包包含在我的项目中。但是,我不知道如何在查询中使用它进行转换,因为它主要用于列数据类型为 Inet 的实体。在我的例子中,表中的列是 varchar 类型的。

谁能告诉我我做错了什么?任何关于我如何让它运行的建议将不胜感激。

编辑 1:添加了我尝试过的另一种可能的解决方案。

【问题讨论】:

  • 你可以使用接口AttributeConverter&lt;X,Y&gt;来教Hibernate如何在两种类型之间转换数据。不确定这是否对您有帮助,您可能必须直接在方言中实现新的 SQL 函数模板。

标签: java spring postgresql hibernate jpa


【解决方案1】:

经过一番研究,我意识到答案一直都在那里。这不是一个理想的解决方案,但在 Hibernate 原生支持 inet 数据类型之前已经足够了。

解决方法是在@Query 中使用nativeQuery = true 参数。所以最终的查询变成了:

@Query(nativeQuery = true, value = "select * from ip_whitelist where CAST(ip_address AS inet) <= CAST((?1) AS inet) and CAST(ip_address_end AS inet) >= CAST((?1) AS inet)")

【讨论】:

    【解决方案2】:

    内置的 cidr 和 inet 类型将执行您想要的操作并提供合适的运算符,有关 postgres 中强制转换的更多详细信息,请使用 link

    SELECT '192.168.1.19'::inet << '192.168.1.0/24'::cidr 
    

    上面的查询可以帮助你直接给出 ip 地址,如果需要,然后再转换它 利用以下有助于这些转化的链接

    https://www.postgresql.org/message-id/Pine.LNX.4.20.0107211126100.4270-100000@Larry.bks

    https://www.postgresql.org/message-id/F28A2B83DFE0D411A7B3006097487147468FDB%40sv7007.scania.se

    https://www.postgresql.org/docs/9.1/datatype-net-types.html#:~:text=PostgreSQL%20offers%20data%20types%20to,functions%20(see%20Section%209.12).

    【讨论】:

    • 如果您查看我的问题,我已经有了查询并将字符串转换为 inet。在 Postgres 中运行时,我的查询运行良好。问题出在我尝试在休眠状态下执行此操作时。它不起作用,因为hibernate不知道如何转换。
    猜你喜欢
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多