使用@OneToMany Map的EclipseLink JPA“此上下文中的无效表”

我希望我在这里做些傻事……

我正在尝试为Map设置JPA注释并获得以下堆栈跟踪。

 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似乎告诉我一些时髦的事情,以及PHONE_TYPE寻找EMPLOYEE.PHONE_TYPE让我觉得它正在考虑来自PHONE – > EMPLOYEE的关系,而不是相反。

JPA实体定义如下。 电话映射是单向的,Phone对象应由多个Employees共享(不是我的实际类型,但这个简化示例演示了这个问题)

 @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 phones = new HashMap(); public Employee() {} public Employee(long id) { this.id = id; } public long getId() { return id; } public Map 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 

和persistence.xml:

    org.eclipse.persistence.jpa.PersistenceProvider aaa.Phone aaa.Employee NONE           

我在Windows 7上运行,使用Oracle XE 11g作为数据库。 提前感谢您的任何见解!

这看起来像EclipseLink错误364922 :

具有单向OneToMany属性和@MapKeyColumn批注的实体创建了正确的数据库表,映射表包含“键列,但持久化实体仅填充id列而不是键列。持久化操作不会抛出错误,但后续查询很快失败,并显示以下错误:exception说明:此表达式中的字段[ORGANIZATION.MAILINGADDRESSES_KEY]在此上下文中具有无效表。

为了它的价值,我使用Hibernate / H2测试了你的代码,它运行正常。

UPDATE

我刚用EclipseLink测试了这个。 如果为MapKeyColumn显式设置表, MapKeyColumn正确填充该键。 即:

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