【问题标题】:JPA 2 and Hibernate 3.5.1 MEMBER OF query doesnt workJPA 2 和 Hibernate 3.5.1 MEMBER OF 查询不起作用
【发布时间】:2011-02-10 21:00:27
【问题描述】:

我正在尝试以下 JPQL,但失败得很惨:

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

我得到以下异常:

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]

我有 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>();

...
}

弹簧配置:

<?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>

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>

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>

我正在运行一个单元测试来检查配置,并且我能够运行其他 JPQL 查询,我唯一无法运行的是 IS EMPTY、MEMBER OF 条件。

完整的单元测试如下:

测试集成

@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;
    }
  }
}

【问题讨论】:

    标签: java hibernate orm jpa jpa-2.0


    【解决方案1】:

    我认为member of 不能与@ElementCollection 属性一起使用。

    【讨论】:

    • 规范中没有类似的东西。 MEMBER OF 应该适用于集合(并且确实如此),而不管使用的注释如何。
    • 感谢指正。还没有机会研究规范,这只是一个疯狂的猜测。
    • JPA 规范中有很多东西,但 Hibernate 就是不支持它。围绕 Hibernate 构建的应用服务器如何获得 Java EE 认证?
    【解决方案2】:

    在我看来,您的查询非常好。作为记录,这是 JPA 2.0 规范中关于 MEMBER OF 运算符的内容:

    4.6.13 集合成员表达式

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

       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
    

    这个表达式测试指定的值是否是 指定的集合 集合值路径表达式。

    计算结果为的表达式 不支持可嵌入类型 集合成员表达式。支持 用于集合中的嵌入 成员表达式可以添加到 本规范的未来版本。

    如果集合值路径 表达式指定一个空 集合,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
    

    因此,由于我在您的查询中看不到任何错误,因此我已经使用 EclipseLink1 测试了您的代码,并且以下 sn-p 可以正常工作:

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

    但是 Hibernate EntityManager 3.5.1-Final 确实失败了。这听起来像是一个错误,请随时提出a Jira issue

    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>
    

    这是我的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>
    

    更新:报告于HHH-5209

    【讨论】:

    • 谢谢,我只是觉得我疯了。
    • @Ed_Zero 您显然不是 :) 如果您创建一个 Jira 问题的链接,能否更新问题?提前致谢。
    【解决方案3】:

    您的查询是绝对正确的,应该可以工作。我今天也遇到了同样的问题,多亏了这篇文章,我才得以停止用头撞墙。

    由于 @ElementCollection 确实是通过内在关联连接元素的一个不错的快捷方式,因此您仍然可以在查询中使用连接语义。

    为了更好地表达,您需要加入您的收藏,并在 where 中设置条件。

    把它变成更具体的东西......

    您的查询:

    SELECT u FROM User u WHERE 'admin' MEMBER OF u.roles
    

    可以写成:

    SELECT u FROM User u JOIN u.roles r WHERE r = 'admin'
    

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

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

      select user from User user where :role in elements(user.roles)
      

      【讨论】:

      • 感谢您的解决方法...您能否指出解决方法是 [not] empty 运算符?
      猜你喜欢
      • 2013-08-23
      • 1970-01-01
      • 1970-01-01
      • 2012-02-02
      • 2014-01-19
      • 1970-01-01
      • 1970-01-01
      • 2011-08-27
      • 2017-03-10
      相关资源
      最近更新 更多