如何在Spring Data JPA Query中将字符串强制转换为IP地址



我有一个表,它包含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 Repository:运行此查询

@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关于使用自定义hibernate类型的指南,并将该包包含在我的项目中。但是,我不知道如何在查询中使用它进行强制转换,因为它主要用于列数据类型为Inet的实体中。在我的例子中,表中的列属于varchar类型

有人能告诉我我做错了什么吗?任何关于我如何运行的建议都将不胜感激。

第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)")

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

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%20提供%20data%20types%20to,函数%20(请参阅%20Section%209.12(。

最新更新