JPA 2和Hibernate 3.5.1成员查询不起作用

Ed_*_*ero 8 java orm hibernate jpa jpa-2.0

我正在尝试以下JPQL,它失败了:

Query query = em.createQuery("SELECT u FROM User u WHERE 'admin' MEMBER OF u.roles");
List users = query.query.getResultList();
Run Code Online (Sandbox Code Playgroud)

我得到以下异常:

ERROR [main] PARSER.error(454) | <AST>:0:0: unexpected end of subtree
java.lang.IllegalArgumentException: org.hibernate.hql.ast.QuerySyntaxException: unexpected end of subtree [SELECT u FROM com.online.data.User u WHERE 'admin' MEMBER OF u.roles] ERROR [main] PARSER.error(454) | <AST>:0:0: expecting "from", found '<ASTNULL>'
...
...
Caused by: org.hibernate.hql.ast.QuerySyntaxException: unexpected end of subtree [SELECT u FROM com.online.data.User u WHERE 'admin' MEMBER OF u.roles]
Run Code Online (Sandbox Code Playgroud)

我有Spring 3.0.1.RELEASE,Hibernate 3.5.1-Final和maven来粘合依赖项.

用户类:

@Entity
public class User {
  @Id
  @Column(name = "USER_ID")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;
  @Column(unique = true, nullable = false)
  private String username;

  private boolean enabled;

  @ElementCollection
  private Set<String> roles = new HashSet<String>();

...
}
Run Code Online (Sandbox Code Playgroud)

弹簧配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/tx/spring-context-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

 <!-- Reading annotation driven configuration -->
 <tx:annotation-driven transaction-manager="transactionManager" />

 <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
 <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
  <property name="maxActive" value="100" />
  <property name="maxWait" value="1000" />
  <property name="poolPreparedStatements" value="true" />
  <property name="defaultAutoCommit" value="true" />
 </bean>

 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
  <property name="dataSource" ref="dataSource" />
 </bean>

 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter">
   <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="showSql" value="true" />
    <property name="databasePlatform" value="${hibernate.dialect}" />
   </bean>
  </property>
  <property name="loadTimeWeaver">
   <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
  </property>
  <property name="jpaProperties">
   <props>
    <prop key="hibernate.hbm2ddl.auto">update</prop>
    <prop key="hibernate.current_session_context_class">thread</prop>
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.format_sql">false</prop>
    <prop key="hibernate.show_comments">true</prop>
   </props>
  </property>
  <property name="persistenceUnitName" value="punit" />
 </bean>

 <bean id="JpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
 </bean>



</beans>
Run Code Online (Sandbox Code Playgroud)

persistence.xml中

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence">

 <persistence-unit name="punit" transaction-type="RESOURCE_LOCAL" />

</persistence>
Run Code Online (Sandbox Code Playgroud)

pom.xml maven依赖项.

  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate</artifactId>
   <version>${hibernate.version}</version>
   <type>pom</type>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>${hibernate.version}</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-annotations</artifactId>
   <version>${hibernate.version}</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>${hibernate.version}</version>
  </dependency>
  <dependency>
   <groupId>commons-dbcp</groupId>
   <artifactId>commons-dbcp</artifactId>
   <version>1.2.2</version>
   <type>jar</type>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-taglibs</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-acl</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>javax.annotation</groupId>
   <artifactId>jsr250-api</artifactId>
   <version>1.0</version>
  </dependency>
 <properties>
  <!-- Application settings -->
  <spring.version>3.0.1.RELEASE</spring.version>
  <hibernate.version>3.5.1-Final</hibernate.version>
Run Code Online (Sandbox Code Playgroud)

我运行单元测试来检查配置,我能够运行其他JPQL查询,我唯一无法运行的是IS EMPTY,MEMBER OF条件.

完整的单元测试如下:

TestIntegration

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations =  { "/spring/dataLayer.xml"})
@Transactional
@TransactionConfiguration
public class TestUserDaoImplIntegration {
  @PersistenceContext
  private EntityManager em;

  @Test
  public void shouldTest() throws Exception {
    try {
//WORKS
Query query = em.createQuery("SELECT u FROM User u WHERE 'admin' in elements(u.roles)"); 
List users = query.query.getResultList();
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }
    try {
//DOES NOT WORK
Query query = em.createQuery("SELECT u FROM User u WHERE 'admin' MEMBER OF u.roles"); 
List users = query.query.getResultList();
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Pas*_*ent 10

您的查询对我来说非常好.为了记录,这是JPA 2.0规范写的关于MEMBER OF运算符的内容:

4.6.13收集成员表达式

MEMBER OF在collection_member_expression中使用比较运算符的语法如下:

   collection_member_expression ::=
            entity_or_value_expression [NOT] MEMBER [OF] collection_valued_path_expression
   entity_or_value_expression ::=
            single_valued_object_path_expression |
            state_field_path_expression |
            simple_entity_or_value_expression
   simple_entity_or_value_expression ::=
            identification_variable |
            input_parameter |
            literal
Run Code Online (Sandbox Code Playgroud)

此表达式测试指定的值是否是集合值路径表达式指定的集合的成员.

收集成员表达式不支持评估为可嵌入类型的表达式.可以在本规范的未来版本中添加对集合成员表达式中的嵌入式使用的支持.

如果集合值路径表达式指定空集合,则MEMBER OF表达式FALSE的值为,NOT MEMBER OF表达式的值为 TRUE.否则,如果集合成员表达式中collection_valued_pa​​th_expression或entity_or_value_expression的值为NULL或未知,则集合成员表达式的值未知.

例:

SELECT p
FROM Person p
WHERE 'Joe' MEMBER OF p.nicknames
Run Code Online (Sandbox Code Playgroud)

因此,因为我在查询中看不到任何错误,所以我使用EclipseLink 1测试了您的代码,以下代码片段正常工作:

Query query = em.createQuery("SELECT u FROM User u WHERE 'admin' MEMBER OF u.roles");
List list = query.getResultList();
Run Code Online (Sandbox Code Playgroud)

但是Hibernate EntityManager 3.5.1-Final确实失败了.这听起来像一个bug,随意提出一个Jira问题.

1以防万一,我使用了以下Maven配置文件(对于JPA提供程序):

  <profile>
    <id>eclipselink</id>
    <repositories>
      <repository>
        <id>eclipselink</id>
        <url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo/</url>
      </repository>
    </repositories>
    <dependencies>
      <!-- See http://wiki.eclipse.org/EclipseLink/Maven -->
      <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>eclipselink</artifactId>
        <version>2.0.0</version>
      </dependency>
      <!-- optional - only needed if you are using JPA outside of a Java EE container-->
      <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>javax.persistence</artifactId>
        <version>2.0.0</version>
        <scope>provided</scope>
      </dependency>              
    </dependencies>
  </profile>
Run Code Online (Sandbox Code Playgroud)

这是我的persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">

  <persistence-unit name="TestPu" transaction-type="RESOURCE_LOCAL">    
    <class>com.stackoverflow.q2688144.User</class>    
    <properties>
      <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
      <property name="javax.persistence.jdbc.url" value="jdbc:derby:testdb;create=true"/>    
      <property name="eclipselink.target-database" value="DERBY"/>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>
Run Code Online (Sandbox Code Playgroud)

更新:HHH-5209中报告


小智 7

Hibernate Bug#HHH-5209,解决方法使用以下语法:

select user from User user where :role in elements(user.roles)
Run Code Online (Sandbox Code Playgroud)