JPA 2 и Hibernate 3.5.1 MEMBER OF запрос не работает - PullRequest
8 голосов
/ 22 апреля 2010

Я пробую следующий 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-запросы, единственные, которые я не могу выполнить, - это ПУСТОЙ, ЧЛЕН условия.

Полный модульный тест выглядит следующим образом:

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

Ответы [ 4 ]

10 голосов
/ 22 апреля 2010

Ваш запрос выглядит отлично для меня. Для справки, вот что спецификация 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

Это выражение проверяет, является ли указанное значение членом коллекция, указанная коллекционное значение пути.

Выражения, которые оценивают как встраиваемые типы не поддерживаются в выражения членов коллекции. Служба поддержки для использования встраиваемых в коллекцию выражения члена могут быть добавлены в будущий выпуск этой спецификации.

Если в коллекции ценится путь выражение обозначает пустой коллекция, стоимость MEMBER OF выражение FALSE и значение выражения NOT MEMBER OF TRUE. В противном случае, если значение collection_valued_path_expression или entity_or_value_expression в выражении члена коллекции имеет значение NULL или неизвестно, значение выражение члена коллекции неизвестно.

* * 1 022 Пример: * 1 023 *
SELECT p
FROM Person p
WHERE 'Joe' MEMBER OF p.nicknames

Итак, поскольку я не вижу ничего неправильного в вашем запросе, я протестировал ваш код с EclipseLink 1 , и следующий фрагмент кода просто работает:

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. Это звучит как ошибка, не стесняйтесь поднимать вопрос 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>

А это мой 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>

Обновление: сообщается в ЧЧЧ-5209

7 голосов
/ 19 марта 2012

Ошибка в спящем режиме # HHH-5209 , В качестве обходного пути используйте следующий синтаксис:

select user from User user where :role in elements(user.roles)
4 голосов
/ 15 марта 2012

Ваш запрос абсолютно правильный и должен работать.Я столкнулся с этой же проблемой только сегодня, и благодаря этому посту смог перестать биться головой об стену.

Так как @ElementCollection действительно хороший ярлык для объединенного элемента с внутренней ассоциацией, вывсе еще может использовать семантику соединения в вашем запросе.

Чтобы выразить это лучше, вам нужно присоединиться к своей коллекции и установить условие в....

Ваш запрос:

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

Может быть записан как:

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

Надеюсь, это поможет!

0 голосов
/ 22 апреля 2010

Я думаю, member of нельзя использовать со свойством @ElementCollection.

...