openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Martin Dirichs (JIRA)" <>
Subject [jira] Updated: (OPENJPA-1141) NPE at org.apache.openjpa.jdbc.meta.MappingInfo.mergeJoinColumn(
Date Mon, 16 Nov 2009 21:36:39 GMT


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

    // 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(
> ---------------------------------------------------------------------------------------
>                 Key: OPENJPA-1141
>                 URL:
>             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,
> 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
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:
>         at org.apache.openjpa.kernel.QueryImpl.compileForCompilation(
>         at org.apache.openjpa.kernel.QueryImpl.compileForExecutor(
>         at org.apache.openjpa.kernel.QueryImpl.getOperation(
>         at org.apache.openjpa.kernel.DelegatingQuery.getOperation(
>         at org.apache.openjpa.persistence.QueryImpl.execute(
>         at org.apache.openjpa.persistence.QueryImpl.getResultList(
>         at testcase.TestMappingProblem.doTest(
>         at testcase.TestMappingProblem.testIt(
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at sun.reflect.NativeMethodAccessorImpl.invoke(
>         at sun.reflect.DelegatingMethodAccessorImpl.invoke(
>         at java.lang.reflect.Method.invoke(
>         at junit.framework.TestCase.runTest(
>         at junit.framework.TestCase.runBare(
>         at junit.framework.TestResult$1.protect(
>         at junit.framework.TestResult.runProtected(
>         at
>         at
>         at junit.framework.TestSuite.runTest(
>         at
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at sun.reflect.NativeMethodAccessorImpl.invoke(
>         at sun.reflect.DelegatingMethodAccessorImpl.invoke(
>         at java.lang.reflect.Method.invoke(
>         at org.apache.maven.surefire.junit.JUnitTestSet.execute(
>         at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(
>         at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(
>         at
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at sun.reflect.NativeMethodAccessorImpl.invoke(
>         at sun.reflect.DelegatingMethodAccessorImpl.invoke(
>         at java.lang.reflect.Method.invoke(
>         at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(
>         at org.apache.maven.surefire.booter.SurefireBooter.main(
> Caused by: java.lang.NullPointerException
>         at org.apache.openjpa.jdbc.meta.MappingInfo.mergeJoinColumn(
>         at org.apache.openjpa.jdbc.meta.MappingInfo.createJoins(
>         at org.apache.openjpa.jdbc.meta.MappingInfo.createForeignKey(
>         at org.apache.openjpa.jdbc.meta.ValueMappingInfo.getTypeJoin(
>         at
>         at org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(
>         at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(
>         at org.apache.openjpa.jdbc.meta.FieldMapping.resolveMapping(
>         at org.apache.openjpa.jdbc.meta.FieldMapping.resolve(
>         at org.apache.openjpa.jdbc.meta.ClassMapping.resolveNonRelationMappings(
>         at org.apache.openjpa.jdbc.meta.MappingRepository.prepareMapping(
>         at org.apache.openjpa.meta.MetaDataRepository.preMapping(
>         at org.apache.openjpa.meta.MetaDataRepository.resolve(
>         at org.apache.openjpa.meta.MetaDataRepository.getMetaData(
>         at org.apache.openjpa.meta.MetaDataRepository.getMetaData(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getClassMetaData(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.resolveClassMetaData(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateMetaData(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateMetaData(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.getCandidateType(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.access$600(
>         at org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder$ParsedJPQL.populate(
>         at org.apache.openjpa.kernel.jpql.JPQLParser.populate(
>         at org.apache.openjpa.kernel.ExpressionStoreQuery.populateFromCompilation(
>         at org.apache.openjpa.kernel.QueryImpl.newCompilation(
>         at org.apache.openjpa.kernel.QueryImpl.compilationFromCache(
>         at org.apache.openjpa.kernel.QueryImpl.compileForCompilation(
>         ... 33 more

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

View raw message