Недопустимая таблица EclipseLink JPA в этом контексте с картой @OneToMany

Я надеюсь, что я просто делаю что-то глупое здесь...

Я пытаюсь настроить аннотации JPA для Map<String, Phone> и получаю следующую трассировку стека.

Exception [EclipseLink-6069] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.QueryException
Exception Description: The field [EMPLOYEE.PHONE_TYPE] in this expression has an invalid table in this context.
Query: ReadAllQuery(name="phones" referenceClass=Phone )
    at org.eclipse.persistence.exceptions.QueryException.invalidTableForFieldInExpression(QueryException.java:739)
    at org.eclipse.persistence.internal.expressions.FieldExpression.validateNode(FieldExpression.java:281)
    at org.eclipse.persistence.expressions.Expression.normalize(Expression.java:3259)
    at org.eclipse.persistence.internal.expressions.DataExpression.normalize(DataExpression.java:369)
    at org.eclipse.persistence.internal.expressions.FieldExpression.normalize(FieldExpression.java:208)
    at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1377)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildNormalSelectStatement(ExpressionQueryMechanism.java:546)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectAllRows(ExpressionQueryMechanism.java:1700)
    at org.eclipse.persistence.queries.ReadAllQuery.prepareSelectAllRows(ReadAllQuery.java:721)
    at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:657)
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:614)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:883)
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:575)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:820)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1109)
    at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:393)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1197)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2875)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1602)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1584)
    at org.eclipse.persistence.internal.indirection.QueryBasedValueHolder.instantiate(QueryBasedValueHolder.java:112)
    at org.eclipse.persistence.internal.indirection.QueryBasedValueHolder.instantiate(QueryBasedValueHolder.java:99)
    at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:88)
    at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiateImpl(UnitOfWorkValueHolder.java:161)
    at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiate(UnitOfWorkValueHolder.java:222)
    at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:88)
    at org.eclipse.persistence.indirection.IndirectMap.buildDelegate(IndirectMap.java:110)
    at org.eclipse.persistence.indirection.IndirectMap.getDelegate(IndirectMap.java:330)
    at org.eclipse.persistence.indirection.IndirectMap.size(IndirectMap.java:637)
    at org.eclipse.persistence.internal.queries.MapContainerPolicy.sizeFor(MapContainerPolicy.java:830)
    at org.eclipse.persistence.internal.indirection.TransparentIndirectionPolicy.instantiateObject(TransparentIndirectionPolicy.java:386)
    at org.eclipse.persistence.mappings.ForeignReferenceMapping.buildCloneFromRow(ForeignReferenceMapping.java:285)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoWorkingCopyClone(ObjectBuilder.java:1594)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneFromRow(ObjectBuilder.java:1741)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:668)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:605)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:564)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:777)
    at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:783)
    at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:434)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1150)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:852)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1109)
    at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:393)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1197)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2875)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1602)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1584)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1549)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:231)
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:411)
    at aaa.Test.main(Test.java:20)

Созданные таблицы выглядят так:

EMP_PHONE
=================================
EMP_ID    PHONES_ID    PHONE_TYPE
------    ---------    ----------
42        1            NULL


EMPLOYEE
=================================
ID
--
42


PHONE
=================================
ID    NUM
--    --------
1     867-5309

NULL PHONE_TYPE, кажется, говорит мне, что происходит что-то странное, и то, как eclipselink ищет EMPLOYEE.PHONE_TYPE, заставляет меня думать, что он думает об отношениях от PHONE-> EMPLOYEE, а не наоборот.

объекты JPA определяются следующим образом. Карта телефонов является однонаправленной, и объекты Phone должны использоваться несколькими сотрудниками (не мои фактические типы, но этот упрощенный пример демонстрирует проблему)

@Entity
public class Employee {
    @Id private long id;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(name="EMP_PHONE", joinColumns=@JoinColumn(name="EMP_ID"))
    @MapKeyColumn(name="PHONE_TYPE")
    private Map<String, Phone> phones = new HashMap<String, Phone>();

    public Employee() {}
    public Employee(long id) {
        this.id = id;
    }
    public long getId() {
        return id;
    }
    public Map<String, Phone> getPhones() {
        return phones;
    }
}

@Entity
public class Phone {
    @Id
    private long id;
    private String num;

    public String getNum() {
        return num;
    }
    public Phone() {}
    public Phone(String num) {
        this.num = num;
    }
    public Phone(long id, String num) {
        this.id = id;
        this.num = num;
    }
    public long getId() {
        return id;
    }
}

используя следующий тестовый код

EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Employee employee = new Employee(42);
// exception will occur with or without phones added to the map
employee.getPhones().put("home", new Phone(1, "867-5309"));
em.getTransaction().begin();
em.persist(employee);
em.getTransaction().commit();

Query q = em.createQuery("select o from Employee o");
q.setHint("javax.persistence.cache.storeMode", "REFRESH");
q.getResultList();  // EXCEPTION THROWN HERE

и постоянство.xml:

<?xml version='1.0' encoding='UTF-8' ?>
<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' xmlns='http://java.sun.com/xml/ns/persistence'>
    <persistence-unit name='test' transaction-type='RESOURCE_LOCAL'>
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>aaa.Phone</class>
        <class>aaa.Employee</class>
        <shared-cache-mode>NONE</shared-cache-mode>
        <properties>
            <property name='javax.persistence.jdbc.user' value='uuuuuuuu' />
            <property name='javax.persistence.jdbc.password' value='pppppppp' />
            <property name='javax.persistence.jdbc.driver'
                      value='oracle.jdbc.driver.OracleDriver' />
            <property name='javax.persistence.jdbc.url' 
                      value='jdbc:oracle:thin:@localhost:1521:xe' />
            <property name='eclipselink.ddl-generation' 
                      value='drop-and-create-tables' />
            <property name='eclipselink.ddl-generation.output-mode' value='database' />
        </properties>
    </persistence-unit>
</persistence>

Я работаю в Windows 7, используя Oracle XE 11g в качестве базы данных. Заранее спасибо за любую информацию!


person Scott Stanchfield    schedule 27.04.2013    source источник


Ответы (1)


Это похоже на ошибку EclipseLink 364922:

Сущность с однонаправленным свойством OneToMany и аннотацией @MapKeyColumn имеет правильные таблицы базы данных, созданные с таблицей сопоставления, содержащей «ключевой столбец», но сохраняющаяся сущность заполняет только столбцы идентификаторов, а не ключевой столбец. Операция сохранения не вызывает ошибка, но последующие запросы вскоре завершатся со следующей ошибкой: Описание исключения: Поле [ORGANIZATION.MAILINGADDRESSES_KEY] в этом выражении имеет недопустимую таблицу в этом контексте.

Что бы это ни стоило, я протестировал ваш код с помощью Hibernate/H2, и он отлично работает.

ОБНОВЛЕНИЕ

Я только что проверил это с помощью EclipseLink. Если вы явно задаете таблицу для MapKeyColumn, ключ заполняется правильно. то есть:

@MapKeyColumn(name="PHONE_TYPE", table="EMP_PHONE")

person hendalst    schedule 30.04.2013
comment
Большое спасибо! Я думаю, что это единственная комбинация вариантов, которую я не пробовал ;) Жаль, что я не увидел эту ошибку, когда искал. - person Scott Stanchfield; 01.05.2013
comment
Я проверил это в своем реальном приложении, и оно отлично работает. Спасибо еще раз - person Scott Stanchfield; 01.05.2013
comment
Рад, что это помогло. Я столкнулся со своей долей неуловимых проблем с реализациями JPA, и я знаю, что они могут свести вас с ума;) - person hendalst; 01.05.2013