在 Spring JPA 存储库中的删除 @Query 中使用 ?#{principal.username} 时如何修复语法错误?
How to fix syntax error when using ?#{principal.username} in a delete @Query in a Spring JPA repository?
我试图让用户只能访问他们自己的配置记录,但允许 ROLE_ADMIN 的用户访问任何记录。
我在 select 查询中使用 SpEL 表达式 ?#{principal.username}
时遇到问题,就像在 findByUid 方法中一样,工作正常,但删除查询中完全相同的表达式会导致语法错误。通过对 deleteByUid 方法使用注释掉的查询,我已将问题缩小到该特定表达式。 deleteByUid 上的注释掉的查询不会抛出语法错误,但当然会拒绝任何非ROLE_ADMIN 用户删除自己的配置记录的能力。
我还需要做一些其他的秘密歌舞来完成这项工作吗?
public interface WidgetConfigRepository extends JpaRepository<WidgetConfig,Long> {
@Query("SELECT wc FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.uid = :uid")
WidgetConfig findByUid(@Param("uid") String uid);
@Query("SELECT wc FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.name LIKE LOWER(CONCAT('%',:name,'%'))")
Page findLikeName(@Param("name") String name, Pageable pageable);
@Override
@Query("SELECT wc FROM WidgetConfig wc WHERE wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true'")
Page findAll(Pageable pageable);
@Query("SELECT wc FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.type = :type")
Page findAllByType(@Param("type") String type, Pageable pageable);
@Modifying
// @Query("DELETE FROM WidgetConfig wc WHERE ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true' AND wc.uid = :uid")
@Query("DELETE FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.uid = :uid")
int deleteByUid(@Param("uid") String uid);
}
编辑:将 Hibernate 日志记录设置为 DEBUG 的日志输出
2017-08-17 11:03:52.845 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Enter: gov.nasa.gsfc.gmsec.gss.portal.web.rest.WidgetConfigResource.deleteWidgetConfig() with argument[s] = [dashboard-1502982221773]
2017-08-17 11:03:52.845 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.w.rest.WidgetConfigResource : REST request to delete WidgetConfig : dashboard-1502982221773
2017-08-17 11:03:52.856 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.spi.AbstractTransactionImpl : begin
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Obtaining JDBC connection
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Obtained JDBC connection
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : initial autocommit status: true
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : disabling autocommit
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Enter: gov.nasa.gsfc.gmsec.gss.portal.service.WidgetConfigService.deleteByUid() with argument[s] = [dashboard-1502982221773]
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.s.i.WidgetConfigServiceImpl : Request to delete WidgetConfig : dashboard-1502982221773
2017-08-17 11:03:52.863 DEBUG 8725 --- [nio-8080-exec-5] org.hibernate.SQL : delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?
Hibernate: delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?
2017-08-17 11:03:52.866 DEBUG 8725 --- [nio-8080-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : could not prepare statement [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "DELETE FROM WIDGET_CONFIG CROSS[*] JOIN JHI_USER USER1_ WHERE (LOGIN=? OR ?='true') AND UID=? "; SQL statement:
delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=? [42000-194]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
(large stack trace left out for brevity)
2017-08-17 11:03:52.892 WARN 8725 --- [nio-8080-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 42000, SQLState: 42000
2017-08-17 11:03:52.892 ERROR 8725 --- [nio-8080-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : Syntax error in SQL statement "DELETE FROM WIDGET_CONFIG CROSS[*] JOIN JHI_USER USER1_ WHERE (LOGIN=? OR ?='true') AND UID=? "; SQL statement:
delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=? [42000-194]
2017-08-17 11:03:52.909 DEBUG 8725 --- [nio-8080-exec-5] o.h.jpa.spi.AbstractEntityManagerImpl : Mark transaction for rollback
2017-08-17 11:03:52.923 ERROR 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Exception in gov.nasa.gsfc.gmsec.gss.portal.service.WidgetConfigService.deleteByUid() with cause = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement' and exception = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement'
2017-08-17 11:03:52.923 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.spi.AbstractTransactionImpl : rolling back
2017-08-17 11:03:52.924 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : rolled JDBC Connection
2017-08-17 11:03:52.924 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : re-enabling autocommit
2017-08-17 11:03:52.924 INFO 8725 --- [nio-8080-exec-5] i.StatisticalLoggingSessionEventListener : Session Metrics {
155229 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
349635 nanoseconds spent preparing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
29111 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)
}
2017-08-17 11:03:52.924 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Releasing JDBC connection
2017-08-17 11:03:52.925 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Released JDBC connection
2017-08-17 11:03:52.925 ERROR 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Illegal argument: [dashboard-1502982221773] in gov.nasa.gsfc.gmsec.gss.portal.web.rest.WidgetConfigResource.deleteWidgetConfig()
2017-08-17 11:03:52.925 ERROR 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Exception in gov.nasa.gsfc.gmsec.gss.portal.web.rest.WidgetConfigResource.deleteWidgetConfig() with cause = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement' and exception = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement'
2017-08-17 11:03:52.944 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Enter: gov.nasa.gsfc.gmsec.gss.portal.web.rest.errors.ExceptionTranslator.processRuntimeException() with argument[s] = [java.lang.IllegalArgumentException: org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement]
2017-08-17 11:03:52.949 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Exit: gov.nasa.gsfc.gmsec.gss.portal.web.rest.errors.ExceptionTranslator.processRuntimeException() with result = <500 Internal Server Error,gov.nasa.gsfc.gmsec.gss.portal.web.rest.errors.ErrorVM@3caeaf1c,{}>
2017-08-17 11:03:52.951 WARN 8725 --- [nio-8080-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: java.lang.IllegalArgumentException: org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
编辑:这是最终起作用的
根据 Cepr0 的回答,我想到了这个:
@Modifying
@Query("DELETE FROM WidgetConfig wc WHERE (wc.user = (SELECT u FROM User u WHERE u.login = ?#{principal.username}) OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.uid = :uid")
int deleteByUid(@Param("uid") String uid);
我仍然有点不明白为什么这行得通,但我原来的方法行不通。
也许这可以帮助(未测试):
delete from WidgetConfig wc
where
wc.user = (
select u from User u where u.id = ?1 and (
u.login = ?#{principal.username} or
?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true'
)
)
(而且我不确定 'true' 和 'false' 周围是否需要单引号...)
我试图让用户只能访问他们自己的配置记录,但允许 ROLE_ADMIN 的用户访问任何记录。
我在 select 查询中使用 SpEL 表达式 ?#{principal.username}
时遇到问题,就像在 findByUid 方法中一样,工作正常,但删除查询中完全相同的表达式会导致语法错误。通过对 deleteByUid 方法使用注释掉的查询,我已将问题缩小到该特定表达式。 deleteByUid 上的注释掉的查询不会抛出语法错误,但当然会拒绝任何非ROLE_ADMIN 用户删除自己的配置记录的能力。
我还需要做一些其他的秘密歌舞来完成这项工作吗?
public interface WidgetConfigRepository extends JpaRepository<WidgetConfig,Long> {
@Query("SELECT wc FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.uid = :uid")
WidgetConfig findByUid(@Param("uid") String uid);
@Query("SELECT wc FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.name LIKE LOWER(CONCAT('%',:name,'%'))")
Page findLikeName(@Param("name") String name, Pageable pageable);
@Override
@Query("SELECT wc FROM WidgetConfig wc WHERE wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true'")
Page findAll(Pageable pageable);
@Query("SELECT wc FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.type = :type")
Page findAllByType(@Param("type") String type, Pageable pageable);
@Modifying
// @Query("DELETE FROM WidgetConfig wc WHERE ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true' AND wc.uid = :uid")
@Query("DELETE FROM WidgetConfig wc WHERE (wc.user.login = ?#{principal.username} OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.uid = :uid")
int deleteByUid(@Param("uid") String uid);
}
编辑:将 Hibernate 日志记录设置为 DEBUG 的日志输出
2017-08-17 11:03:52.845 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Enter: gov.nasa.gsfc.gmsec.gss.portal.web.rest.WidgetConfigResource.deleteWidgetConfig() with argument[s] = [dashboard-1502982221773]
2017-08-17 11:03:52.845 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.w.rest.WidgetConfigResource : REST request to delete WidgetConfig : dashboard-1502982221773
2017-08-17 11:03:52.856 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.spi.AbstractTransactionImpl : begin
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Obtaining JDBC connection
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Obtained JDBC connection
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : initial autocommit status: true
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : disabling autocommit
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Enter: gov.nasa.gsfc.gmsec.gss.portal.service.WidgetConfigService.deleteByUid() with argument[s] = [dashboard-1502982221773]
2017-08-17 11:03:52.857 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.s.i.WidgetConfigServiceImpl : Request to delete WidgetConfig : dashboard-1502982221773
2017-08-17 11:03:52.863 DEBUG 8725 --- [nio-8080-exec-5] org.hibernate.SQL : delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?
Hibernate: delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?
2017-08-17 11:03:52.866 DEBUG 8725 --- [nio-8080-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : could not prepare statement [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "DELETE FROM WIDGET_CONFIG CROSS[*] JOIN JHI_USER USER1_ WHERE (LOGIN=? OR ?='true') AND UID=? "; SQL statement:
delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=? [42000-194]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
(large stack trace left out for brevity)
2017-08-17 11:03:52.892 WARN 8725 --- [nio-8080-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 42000, SQLState: 42000
2017-08-17 11:03:52.892 ERROR 8725 --- [nio-8080-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : Syntax error in SQL statement "DELETE FROM WIDGET_CONFIG CROSS[*] JOIN JHI_USER USER1_ WHERE (LOGIN=? OR ?='true') AND UID=? "; SQL statement:
delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=? [42000-194]
2017-08-17 11:03:52.909 DEBUG 8725 --- [nio-8080-exec-5] o.h.jpa.spi.AbstractEntityManagerImpl : Mark transaction for rollback
2017-08-17 11:03:52.923 ERROR 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Exception in gov.nasa.gsfc.gmsec.gss.portal.service.WidgetConfigService.deleteByUid() with cause = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement' and exception = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement'
2017-08-17 11:03:52.923 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.spi.AbstractTransactionImpl : rolling back
2017-08-17 11:03:52.924 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : rolled JDBC Connection
2017-08-17 11:03:52.924 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.t.internal.jdbc.JdbcTransaction : re-enabling autocommit
2017-08-17 11:03:52.924 INFO 8725 --- [nio-8080-exec-5] i.StatisticalLoggingSessionEventListener : Session Metrics {
155229 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
349635 nanoseconds spent preparing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
29111 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)
}
2017-08-17 11:03:52.924 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Releasing JDBC connection
2017-08-17 11:03:52.925 DEBUG 8725 --- [nio-8080-exec-5] o.h.e.j.internal.LogicalConnectionImpl : Released JDBC connection
2017-08-17 11:03:52.925 ERROR 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Illegal argument: [dashboard-1502982221773] in gov.nasa.gsfc.gmsec.gss.portal.web.rest.WidgetConfigResource.deleteWidgetConfig()
2017-08-17 11:03:52.925 ERROR 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Exception in gov.nasa.gsfc.gmsec.gss.portal.web.rest.WidgetConfigResource.deleteWidgetConfig() with cause = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement' and exception = 'org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement'
2017-08-17 11:03:52.944 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Enter: gov.nasa.gsfc.gmsec.gss.portal.web.rest.errors.ExceptionTranslator.processRuntimeException() with argument[s] = [java.lang.IllegalArgumentException: org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement]
2017-08-17 11:03:52.949 DEBUG 8725 --- [nio-8080-exec-5] g.n.g.g.g.p.aop.logging.LoggingAspect : Exit: gov.nasa.gsfc.gmsec.gss.portal.web.rest.errors.ExceptionTranslator.processRuntimeException() with result = <500 Internal Server Error,gov.nasa.gsfc.gmsec.gss.portal.web.rest.errors.ErrorVM@3caeaf1c,{}>
2017-08-17 11:03:52.951 WARN 8725 --- [nio-8080-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: java.lang.IllegalArgumentException: org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [delete from widget_config cross join jhi_user user1_ where (login=? or ?='true') and uid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
编辑:这是最终起作用的
根据 Cepr0 的回答,我想到了这个:
@Modifying
@Query("DELETE FROM WidgetConfig wc WHERE (wc.user = (SELECT u FROM User u WHERE u.login = ?#{principal.username}) OR ?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true') AND wc.uid = :uid")
int deleteByUid(@Param("uid") String uid);
我仍然有点不明白为什么这行得通,但我原来的方法行不通。
也许这可以帮助(未测试):
delete from WidgetConfig wc
where
wc.user = (
select u from User u where u.id = ?1 and (
u.login = ?#{principal.username} or
?#{hasRole('ROLE_ADMIN') ? 'true' : 'false'} = 'true'
)
)
(而且我不确定 'true' 和 'false' 周围是否需要单引号...)