Search code examples
javasqlhbasedatanucleuskundera

DataNucleus / Kundera with HBase with ElementCollection - Unable to find the object with a null id


To start with, I use HBase 0.94.5 (also tried 0.92.2 with the same results).

I have a situation like this:

Variable class:

@Entity
@Table(name = "variable", schema = "keyspace@hbase-pu")
public class Variable {
    @Id
    private String Id;

    @Column(name = "Name")
    private String Name;

    @ElementCollection(fetch = FetchType.EAGER)
   //@CollectionTable(name = "datavalues") // Doesn't work with or without.
    private List<DataValue> DataValues;

    // Getters and setters omitted.
}

DataValue class:

@Embeddable
public class DataValue {
    @Column(name = "Value")
    private Object Value;

    // Getters and setters omitted.
}

Now, storing this is no problem at all, but when I want to retrieve it, boom goes the dynamite. This exception is thrown:

Caused by: org.datanucleus.exceptions.NucleusUserException: Unable to find the object with a null id!

Exception in thread "main" javax.persistence.PersistenceException: Unable to find the object with a null id!
    at org.datanucleus.api.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:414)
    at org.datanucleus.api.jpa.JPAQuery.getResultList(JPAQuery.java:203)
    at Main.main(Main.java:73)
Caused by: org.datanucleus.exceptions.NucleusUserException: Unable to find the object with a null id!
    at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3441)
    at org.datanucleus.store.hbase.fieldmanager.FetchFieldManager.fetchObjectField(FetchFieldManager.java:287)
    at org.datanucleus.state.JDOStateManager.replacingObjectField(JDOStateManager.java:2178)
    at Variable.jdoReplaceField(Variable.java)
    at Variable.jdoReplaceFields(Variable.java)
    at org.datanucleus.state.JDOStateManager.replaceFields(JDOStateManager.java:3415)
    at org.datanucleus.state.JDOStateManager.replaceFields(JDOStateManager.java:3442)
    at org.datanucleus.store.hbase.query.HBaseQueryUtils$2.fetchFields(HBaseQueryUtils.java:270)
    at org.datanucleus.state.JDOStateManager.loadFieldValues(JDOStateManager.java:2547)
    at org.datanucleus.state.JDOStateManager.initialiseForHollow(JDOStateManager.java:298)
    at org.datanucleus.state.ObjectProviderFactoryImpl.newForHollow(ObjectProviderFactoryImpl.java:89)
    at org.datanucleus.ExecutionContextImpl.newObjectProviderForHollowPopulated(ExecutionContextImpl.java:1237)
    at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3053)
    at org.datanucleus.store.hbase.query.HBaseQueryUtils.getObjectUsingApplicationIdForResult(HBaseQueryUtils.java:265)
    at org.datanucleus.store.hbase.query.HBaseQueryUtils.getObjectsOfType(HBaseQueryUtils.java:194)
    at org.datanucleus.store.hbase.query.HBaseQueryUtils.getObjectsOfCandidateType(HBaseQueryUtils.java:93)
    at org.datanucleus.store.hbase.query.JPQLQuery.performExecute(JPQLQuery.java:327)
    at org.datanucleus.store.query.Query.executeQuery(Query.java:1786)
    at org.datanucleus.store.query.Query.executeWithMap(Query.java:1690)
    at org.datanucleus.api.jpa.JPAQuery.getResultList(JPAQuery.java:186)
    ... 1 more

This is because of the ElementCollection, as when I remove this it retreives the variable class perfectly from HBase. I also tried Kundera 2.4 (same example code), which is a lot easier to setup than this DataNucleus 'enhance' thing, but it throws something with a Stream exception. Both sites show code samples similar to my code above, so I'm very curious to what I'm doing wrong here.

Relevant pom.xml:

    <dependency>
        <groupId>org.apache.geronimo.specs</groupId>
        <artifactId>geronimo-jpa_2.0_spec</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>javax.jdo</groupId>
        <artifactId>jdo-api</artifactId>
        <version>3.0</version>
    </dependency>

   <dependency>
        <groupId>org.datanucleus</groupId>
        <artifactId>datanucleus-core</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.datanucleus</groupId>
        <artifactId>datanucleus-api-jpa</artifactId>
        <version>3.2.0-release</version>
    </dependency>
    <dependency>
        <groupId>org.datanucleus</groupId>
        <artifactId>datanucleus-hbase</artifactId>
        <version>3.2.0-release</version>
    </dependency>
    <dependency>
        <groupId>org.datanucleus</groupId>
        <artifactId>datanucleus-enhancer</artifactId>
        <version>3.1.1</version>
    </dependency>
    <dependency>
        <groupId>javax.persistence</groupId>
        <artifactId>persistence-api</artifactId>
        <version>2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-core</artifactId>
        <version>1.1.2</version>
        <exclusions>
            <exclusion>
                <groupId>org.codehaus.jackson</groupId>
                <artifactId>jackson-mapper-asl</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.codehaus.jackson</groupId>
                <artifactId>jackson-core-asl</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase</artifactId>
        <version>0.94.5</version>
    </dependency>


        <plugin>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-maven-plugin</artifactId>
            <version>3.2.0-release</version>
            <configuration>
                <api>JPA</api>
                <persistenceUnitName>hbase-pu</persistenceUnitName>
                <verbose>true</verbose>
            </configuration>
            <executions>
                <execution>
                    <phase>process-classes</phase>
                    <goals>
                        <goal>enhance</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

persistance.xml for DataNucleus:

    <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">

    <!-- JPA tutorial "unit" -->
    <persistence-unit name="hbase-pu" transaction-type="RESOURCE_LOCAL">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
        <class>Variable</class>
        <class>DataValue</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="datanucleus.storeManagerType" value="hbase" />
            <property name="datanucleus.ConnectionURL" value="hbase:localhost:2281"/>
            <property name="datanucleus.ConnectionUserName" value=""/>
            <property name="datanucleus.ConnectionPassword" value=""/>
            <property name="datanucleus.autoCreateSchema" value="true"/>
            <property name="datanucleus.autoCreateTables" value="true" />
            <property name="datanucleus.autoCreateColumns" value="true" />
            <property name="datanucleus.validateTables" value="true"/>
            <property name="datanucleus.validateConstraints" value="false"/>
            <property name="datanucleus.Optimistic" value="false"/>
            <property name="datanucleus.Multithreaded" value="true" />
        </properties>
    </persistence-unit>
</persistence>

persistence.xml for Kundera:

<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
https://raw.github.com/impetus-opensource/Kundera/Kundera-2.0.4/kundera-core/src/test/resources/META-INF/persistence_2_0.xsd"
version="2.0">
 <persistence-unit name="hbase-pu">
        <provider>com.impetus.kundera.KunderaPersistence</provider>
        <properties>
            <property name="kundera.nodes" value="localhost" />
            <property name="kundera.port" value="2182" />
            <property name="kundera.keyspace" value="keyspace" />
            <property name="kundera.dialect" value="hbase" />
            <property name="kundera.client.lookup.class" value="com.impetus.client.hbase.HBaseClientFactory" />
        <property name="kundera.cache.provider.class" value="com.impetus.kundera.cache.ehcache.EhCacheProvider" />
        <property name="kundera.cache.config.resource" value="/ehcache-test.xml" />
        <property name="kundera.ddl.auto.prepare" value="update" />
        </properties>
    </persistence-unit>
</persistence>

I'm kinda shooting blanks here with this whole thing and I have a feeling I pretty much tried everything at this moment and have no idea how to continue.

-- See comment below.

New Variable class:

@Entity
@Table(name = "variable", schema = "keyspace@hbase-pu")
public class Variable {
    @Id
    private String Id;

    @Column(name = "Name")
    private String Name;

    @ElementCollection(fetch = FetchType.EAGER) 
    private List<Object> DataValues;

    @Column(name = "Value")
    @Lob
    private Object Value;
    // Getters and setters omitted.
}

Solution

  • So you want to have an embedded collection ? But then as this page shows clearly enough, this is not supported for HBase at the moment (supported for MongoDB, but then that has a much more flexible datastore structure to allow it). You can have a Collection of non-Entities (persisted into the same HTable as the owner), and you can have a Collection of (non-embedded) Entities, but not an embedded Collection. Obviously you could contribute your time to provide support for that should it be important to you (and I'll even provide you with starting points in the code to look at).

    No reason why "CollectionTable" should make a difference ... it's an annotation for RDBMS for the schema (but then JPA is designed solely for RDBMS).

    PS 1, this "enhance thing" is as simple as adding the "plugin" block to pom.xml. So unless you're prepared to express to the DataNucleus project what your difficulty with it is, then how can they know it.

    PS 2, if reporting a problem I'd expect you to show the exception + stack trace no matter what software is involved, because from an exception message you tell people very little.