views:

1558

answers:

2

I'm trying the following JPQL and it fails misserably:

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

I get the following exception:

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]

I have Spring 3.0.1.RELEASE, Hibernate 3.5.1-Final and maven to glue dependencies.

User class:

@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>();

...
}

Spring configuration:

<?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"&gt;

 <!-- 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"&gt;

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

</persistence>

pom.xml maven dependencies.

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

Im running a unit test to check the configuration and I am able to run other JPQL queries the only ones that I am unable to run are the IS EMPTY, MEMBER OF conditions.

The complete unit test is as follows:

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;
    }
  }
}
A: 

I think member of cannot be used with a @ElementCollection property.

Bytecode Ninja
There is nothing like that in the spec. `MEMBER OF` is supposed to work on collection (and it does) regardless of the annotation used.
Pascal Thivent
Thanks for the correction. Haven't had the chance to study the spec yet and it was just a wild guess.
Bytecode Ninja
There are so many things that are in the JPA spec, yet Hibernate just doesn't support it. How can an application server build around Hibernate ever be Java EE certified?
arjan
+2  A: 

Your query looks perfectly fine to me. For the record, this is what the JPA 2.0 specification writes about the MEMBER OF operator:

4.6.13 Collection Member Expressions

The syntax for the use of the comparison operator MEMBER OF in an collection_member_expression is as follows:

   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

This expression tests whether the designated value is a member of the collection specified by the collection-valued path expression.

Expressions that evaluate to embeddable types are not supported in collection member expressions. Support for use of embeddables in collection member expressions may be added in a future release of this specification.

If the collection valued path expression designates an empty collection, the value of the MEMBER OF expression is FALSE and the value of the NOT MEMBER OF expression is TRUE. Otherwise, if the value of the collection_valued_path_expression or entity_or_value_expression in the collection member expression is NULL or unknown, the value of the collection member expression is unknown.

Example:

SELECT p
FROM Person p
WHERE 'Joe' MEMBER OF p.nicknames

So, because I can't see anything wrong in your query, I've tested your code with EclipseLink1 and the following snippet just works:

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

But fails indeed with Hibernate EntityManager 3.5.1-Final. This sounds like a bug, feel free to raise a Jira issue.

1 Just in case, I used the following Maven profile (for the JPA provider):

  <profile>
    <id>eclipselink</id>
    <repositories>
      <repository>
        <id>eclipselink</id>
        <url>http://www.eclipse.org/downloads/download.php?r=1&amp;amp;nf=1&amp;amp;file=/rt/eclipselink/maven.repo/&lt;/url&gt;
      </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>

And this is my 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>

Update: reported in HHH-5209

Pascal Thivent
Thanks I just thought that I was becoming insane.
Ed_Zero
@Ed_Zero You're clearly not :) Could you please update the question with a link to the Jira issue if you create one? Thanks in advance.
Pascal Thivent