[ 
https://issues.apache.org/jira/browse/OPENJPA-1141?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Martin Dirichs updated OPENJPA-1141:
------------------------------------

    Attachment: OpenJPA-Trunk_OJ1141-2.patch

I have tested patch OpenJPA-Trunk_OJ1141.patch with my application. 
Unfortunately, the patch doesn't seem to eliminate the problem. There seemed to 
be no difference at all.

Digging further into this issue, I could spot in the code why the patch did not 
solve the problem. The patch adds a line to the bottom of 
MetaDataRepository.processBuffer() to reorder entities for further processing. 
Method processBuffer() is a bit strange at first view:

private List<ClassMetaData> processBuffer(ClassMetaData meta, 
InheritanceOrderedMetaDataList buffer, int mode) {
    // if we're already processing a metadata, just buffer this one; when
    // the initial metadata finishes processing, we traverse the buffer
    // and process all the others that were introduced during reentrant calls
    if (!buffer.add(meta) || buffer.size() != 1)
        return null;

    // continually pop a metadata and process it until we run out; note
    // that each processing call might place more metas in the buffer as
    // one class tries to access metadata for another; also note that the
    // buffer orders itself from least to most derived
    ClassMetaData buffered;
    List<ClassMetaData> processed = new ArrayList<ClassMetaData>(5);
    while (!buffer.isEmpty()) {
        buffered = buffer.peek();
        buffered.resolve(mode);
        processed.add(buffered);
        buffer.remove(buffered);
    }

    // this line is added by the patch
    processed = resolveFKInPKDependenciesOrdering(processed);

    return processed;
}

Thus, the iteration within processBuffer() is only executed if the supplied 
list of entity meta data solely contains one item, which is also supplied as 
first parameter to the method. This puzzle is solved, of course, by taking into 
account that the line "buffered.resolve(mode);" may lead to recursive calls to 
processBuffer(), thereby growing the list of meta data while iterating over it 
in the outermost call.

Note that the patch only adds a line behind the while loop. However, exceptions 
similar to the one reported in this JIRA issue occur within the while loop, 
during the recursion. The patch may happen to eliminate the meta data 
resolution problem for the supplied test setup, it does not work for the 
general case.

The root problem lies in the usage of an InheritanceOrderedMetaDataList as 
buffer. This kind of list makes sure that new meta data entries put into the 
list are always inserted behind any meta data entries of super classes. This is 
fine, but it is not enough. If entities use other entities for their primary 
key fields, this is a dependency not taken into account by the 
InheritanceOrderedMetaDataList. Instead, entities not inheriting from each 
other are simply ordered alphabetically when inserted into the list.

This is the real flaw of using the InheritanceOrderedMetaDataList in this 
context. Since resolving the meta data of an entity is only guaranteed to work 
if both super classes and entites referred to in the primary key fields are 
already resolved beforehand, the usage of InheritanceOrderedMetaDataList is 
error prone.

There exists another approach to remove this kind of meta data resolution 
problems: Use a simple ArrayList instead of InheritanceOrderedMetaDataList. In 
the process of resolving the meta data for an entity, the current code already 
takes into account all possible dependencies between entities. In short, 
ClassMetaData.resolveMeta() works as follows:
  - resolve super class of current entity, if applicable
  - resolve primary key field entities of current entity, if applicable
Both of these actions lead to calls to MetaDataRepository.processBuffer() with 
the respective entity meta data before processBuffer() is finally called for 
the current entity. Thus, all entities the current entity is dependent on are 
put into the buffer before the current entity. A simple array list thus is 
sufficient to let all entities be processed in the correct order.

I've attached a corresponding patch called OpenJPA-Trunk_OJ1141-2.patch which 
implements these considerations. Although this patch removes the special logic 
within MetaDataRepository introduced by the original patch, the test cases of 
the original patch continue to work flawlessly as does the test suite attached 
to this JIRA issue. What is more, this fix is also able to cope with the 
mapping of my application, which happens to have many instances of foreign key 
references in primary keys as well as sub/superclass dependencies.


> NPE  at 
> org.apache.openjpa.jdbc.meta.MappingInfo.mergeJoinColumn(MappingInfo.java:1400)
> ---------------------------------------------------------------------------------------
>
>                 Key: OPENJPA-1141
>                 URL: https://issues.apache.org/jira/browse/OPENJPA-1141
>             Project: OpenJPA
>          Issue Type: Bug
>          Components: jdbc
>    Affects Versions: 1.2.0
>            Reporter: Yann
>            Assignee: Jody Grassel
>             Fix For: 1.2.2, 1.3.0, 2.0.0
>
>         Attachments: ddl.sql, OpenJPA-1.2.x-OJ1141_10226009.patch, 
> OpenJPA-1.3.x_OJ1141.patch, OpenJPA-Trunk_OJ1141-2.patch, 
> OpenJPA-Trunk_OJ1141.patch, TestMappingProblem.zip
>
>
> We have the NPE shown below in a reproducible testcase (ZIP attached to JIRA 
> ...).
> We've reduced our complex intended target domain model (about 200+ Entities) 
> to a simpler model with only 3 classes which illustrate the problem: You'll 
> find attached a test project with a first entity which has a a OneToMany 
> (with an ElementJoinColumns, but that shouldn't matter?) to a second entity 
> has a ManyToOne to a third entity. The middle entity has a Composite ID Class 
> including a ManyToOne as a key, which according to 
> http://openjpa.apache.org/builds/1.2.0/apache-openjpa-1.2.0/docs/manual/ref_guide_pc_oid.html#ref_guide_pc_oid_entitypk
>  is supported, so this seems a bug in OpenJPA's mapping algos, somehow?
> <openjpa-1.2.1-r752877:753278 nonfatal general error> 
> org.apache.openjpa.persistence.PersistenceException: null
>         at 
> org.apache.openjpa.kernel.QueryImpl.compileForCompilation(QueryImpl.java:610)
>         at 
> org.apache.openjpa.kernel.QueryImpl.compileForExecutor(QueryImpl.java:667)
>         at 
> org.apache.openjpa.kernel.QueryImpl.getOperation(QueryImpl.java:1492)
>         at 
> org.apache.openjpa.kernel.DelegatingQuery.getOperation(DelegatingQuery.java:123)
>         at 
> org.apache.openjpa.persistence.QueryImpl.execute(QueryImpl.java:243)
>         at 
> org.apache.openjpa.persistence.QueryImpl.getResultList(QueryImpl.java:294)
>         at testcase.TestMappingProblem.doTest(TestMappingProblem.java:42)
>         at testcase.TestMappingProblem.testIt(TestMappingProblem.java:20)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>         at 
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>         at java.lang.reflect.Method.invoke(Method.java:597)
>         at junit.framework.TestCase.runTest(TestCase.java:154)
>         at junit.framework.TestCase.runBare(TestCase.java:127)
>         at junit.framework.TestResult$1.protect(TestResult.java:106)
>         at junit.framework.TestResult.runProtected(TestResult.java:124)
>         at junit.framework.TestResult.run(TestResult.java:109)
>         at junit.framework.TestCase.run(TestCase.java:118)
>         at junit.framework.TestSuite.runTest(TestSuite.java:208)
>         at junit.framework.TestSuite.run(TestSuite.java:203)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>         at 
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>         at java.lang.reflect.Method.invoke(Method.java:597)
>         at 
> org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:213)
>         at 
> org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
>         at 
> org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
>         at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>         at 
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>         at java.lang.reflect.Method.invoke(Method.java:597)
>         at 
> org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)
>         at 
> org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)
> Caused by: java.lang.NullPointerException
>         at 
> org.apache.openjpa.jdbc.meta.MappingInfo.mergeJoinColumn(MappingInfo.java:1400)
>         at 
> org.apache.openjpa.jdbc.meta.MappingInfo.createJoins(MappingInfo.java:1206)
>         at 
> org.apache.openjpa.jdbc.meta.MappingInfo.createForeignKey(MappingInfo.java:968)
>         at 
> org.apache.openjpa.jdbc.meta.ValueMappingInfo.getTypeJoin(ValueMappingInfo.java:104)
>         at 
> org.apache.openjpa.jdbc.meta.strats.RelationFieldStrategy.map(RelationFieldStrategy.java:157)
>         at 
> org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(FieldMapping.java:121)
>         at 
> org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:80)
>         at 
> org.apache.openjpa.jdbc.meta.FieldMapping.resolveMapping(FieldMapping.java:454)
>         at 
> org.apache.openjpa.jdbc.meta.FieldMapping.resolve(FieldMapping.java:419)
>         at 
> org.apache.openjpa.jdbc.meta.ClassMapping.resolveNonRelationMappings(ClassMapping.java:869)
>         at 
> org.apache.openjpa.jdbc.meta.MappingRepository.prepareMapping(MappingRepository.java:339)
>         at 
> org.apache.openjpa.meta.MetaDataRepository.preMapping(MetaDataRepository.java:662)
>         at 
> org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:549)
>         at 
> org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:308)
>         at 
> org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:363)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getClassMetaData(JPQLExpressionBuilder.java:159)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.resolveClassMetaData(JPQLExpressionBuilder.java:139)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateMetaData(JPQLExpressionBuilder.java:225)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateMetaData(JPQLExpressionBuilder.java:195)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateType(JPQLExpressionBuilder.java:188)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.access$600(JPQLExpressionBuilder.java:69)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder$ParsedJPQL.populate(JPQLExpressionBuilder.java:1756)
>         at 
> org.apache.openjpa.kernel.jpql.JPQLParser.populate(JPQLParser.java:56)
>         at 
> org.apache.openjpa.kernel.ExpressionStoreQuery.populateFromCompilation(ExpressionStoreQuery.java:153)
>         at 
> org.apache.openjpa.kernel.QueryImpl.newCompilation(QueryImpl.java:658)
>         at 
> org.apache.openjpa.kernel.QueryImpl.compilationFromCache(QueryImpl.java:639)
>         at 
> org.apache.openjpa.kernel.QueryImpl.compileForCompilation(QueryImpl.java:605)
>         ... 33 more

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to