ObjectDB ObjectDB

New to Product & Having An Issue

#1

Greetings ObjectDB Users!


I'm new to the product, and I've setup a simple test after reading the tutorial, and I'm running into some issues with queries, specifically querying by primary key.   I have a domain object class Called Department, which has a member of type DomainId.  DomainId has two member fields:  String accountId and String objectId.  The following code shows these two classes.

package test.Domain;

...imports....


@Entity

@Access(AccessType.PROPERTY)

@NamedQueries({
    @NamedQuery(name="Department.findAll", query="SELECT d FROM Department AS d"),
    @NamedQuery(name="Department.byName", query="SELECT d from Department AS d WHERE d.name = :name"),
    @NamedQuery(name="Department.findByCmpPK", 
            query="SELECT d FROM test.Domain.Department AS d " +
                    "WHERE d.domainId.accountId = :aid AND d.domainId.objectId = :oid"),
    @NamedQuery(name="Department.findByPK", 
            query="SELECT d FROM Department AS d WHERE d.domainId.equals(:id)")
})
public class Department implements Serializable {
    public static final long serialVersionUID = 1L;
    
    private DomainId domainId;
    private String name;
    private Set<Person> employees;
    
    public Department() {
        super();
    }

    public Department(DomainId domainId, String name, Set<Person> employees) {
        super();
        this.domainId = domainId;
        this.name = name;
        this.employees = employees;
    }

    @EmbeddedId
    public DomainId getDomainId() {
        return domainId;
    }

    public void setDomainId(DomainId domainId) {
        this.domainId = domainId;
    }

    @Unique(name="name2")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToMany(fetch=FetchType.LAZY)
    public Set<Person> getEmployees() {
        return employees;
    }

    public void setEmployees(Set<Person> employees) {
        this.employees = employees;
    }

    public boolean equals(Object obj) {
        if(((Department) obj).getDomainId() == null || this.domainId == null)
            return false;
        return this.domainId.equals(obj);
    }
}
package test.Domain;

...imports....


@Embeddable

@Access(AccessType.PROPERTY)
public class DomainId implements Serializable {
    public static final long serialVersionUID = 1L;
    
    private String accountId;
    private String objectId;

    public DomainId(String accountId, String objectId) {
        super();
        this.accountId = accountId;
        this.objectId = objectId;
    }

    public DomainId() {
        super();
    }

    @Basic(optional=false)
    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    @Basic(optional=false)
    public String getObjectId() {
        return objectId;
    }

    public void setObjectId(String objectId) {
        this.objectId = objectId;
    }
    
    public synchronized static String getNewId(){
        UUID uuid = UUID.randomUUID();
        return uuid.toString().replaceAll("-","");  
    }

    public boolean equals(Object obj) {
        if(obj instanceof DomainId){
            DomainId foreign = (DomainId) obj;
            if(this.accountId == null || this.objectId == null)
                return false;
            if(foreign.getAccountId() == null || foreign.getObjectId() == null)
                return false;
            return (this.accountId.equals(foreign.getAccountId())) && (this.objectId.equals(foreign.getObjectId()));        
        }
        return false;
    }
}

I then setup a JUnit test case, like this:

package test.test;

....imports.....

public class TestDepartment {
    private EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-pu");
    private EntityManager em;
    private DomainId departmentId;
    private Department foundResult = null;
    
    @Before
    public void setUp(){
        this.em = emf.createEntityManager();
        this.departmentId = new DomainId(DomainId.getNewId(), DomainId.getNewId());
    }
    
    @Test
    public void createDepartment(){
        Department department = new Department(this.departmentId, "dOne", null);
        try{
            em.getTransaction().begin();
            em.persist(department);
            em.getTransaction().commit();
        }catch(Exception e){
            e.printStackTrace();
            if(em.getTransaction().isActive())
                em.getTransaction().rollback();
            assertTrue(false);
        }
    }
    
    @Test
    public void findDepartment(){
        Department d = em.find(Department.class, this.departmentId);
        assertNotNull(d);
        assertNotNull(d.getDomainId());
        assertEquals(this.departmentId, d.getDomainId());
    }
    
    @Test
    public void queryDepById(){
        TypedQuery<department> q = em.createNamedQuery("Department.findByPK", Department.class);
        q.setParameter("id", this.departmentId);
        Department d = q.getSingleResult();
        assertNotNull(d);
        assertNotNull(d.getDomainId());
        assertEquals(this.departmentId, d.getDomainId());
        this.foundResult = d;
    }
    
    @Test
    public void queryDepByIdComponents(){
        TypedQuery&lr;Department> q = em.createNamedQuery("Department.findByCmpPK", Department.class);
        q.setParameter("aid", this.departmentId.getAccountId());
        q.setParameter("oid", this.departmentId.getObjectId());
        Department d = q.getSingleResult();
        assertNotNull(d);
        assertNotNull(d.getDomainId());
        assertEquals(this.departmentId, d.getDomainId());
        this.foundResult = d;
    }
    
    @Test
    public void simpleCleanUp(){
        assertNotNull(this.foundResult);
        try{
            em.getTransaction().begin();
            em.remove(this.foundResult);
            em.getTransaction().commit();
        }catch(Exception e){
            e.printStackTrace();
            if(em.getTransaction().isActive())
                em.getTransaction().rollback();
            assertTrue(false);
        }
    }
    
    @After
    public void tearDown(){
        //Ensure cleanup happens if lookups fail.
        TypedQuery<Department> q = em.createNamedQuery("Department.findAll", Department.class);
        q.setMaxResults(1000);
        List<Department> departments;
        try{
            do{
                departments = q.getResultList();
                em.getTransaction().begin();
                for(Department d : departments)
                    em.remove(d);
                em.getTransaction().commit();
            }while(departments.size() > 0);
        }catch(Exception e){
            e.printStackTrace();
            if(em.getTransaction().isActive())
                em.getTransaction().rollback();
        }
        //Finish clean up.
        this.foundResult = null;
        this.departmentId = null;
        em.close();
        this.em = null;
        this.emf = null;
    }
}

The first test, createDepartment() succeeds, and I can view the Department object in the database with the ObjectDB Explorer, including it's embedded DomainId which is filled out with the random UUID strings I generated. However, the second test findDepartment fails to find any results and returns NULL, but does not throw any exception. The third and fourth tests fail, and throw the following exception:

[ObjectDB 2.0.3] Query:  SELECT d FROM Department AS d WHERE d. ==> domainId <== .equals(:id)
javax.persistence.PersistenceException
Field 'domainId' is not found in type 'test.Domain.Department' (error 761)
 (position 38)  at com.objectdb.jpa.JpaQuery.getSingleResult(JpaQuery.java:592)
    at test.test.TestDepartment.queryDepById(TestDepartment.java:58)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
    at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

This causes the final test, simpleCleanUp to fail because the Department instance is not found, and therefore the privately scoped foundDepartment remains null. Am I doing something wrong with the use of @Embedded, @EmbeddedId or with my JPQL queries or is this a bug? Any help would be greatly appreciated.

edit
delete
#2

Hi,

I think that find returns null because different departmentId values are used in persist and find. Notice that setUp is invoked on every test run, generating new departmentId.

I am looking now at the JPQL query issues, which might indicate an ObjectDB bug.

ObjectDB Support
edit
delete
#3

You were absolutely correct about the @Before and @After annotations.  I haven't used JUnit in quite a while and forgot how those worked.  I fixed the test case to use @BeforeClass and @AfterClass and made the changes necessary so the setUp() and tearDown() methods could be static, The em.find() functionality worked as expected.  The query tests still failed however.

edit
delete
#4

I think I found and fixed the bug. Thank you for your bug report.

Please try the new build (2.0.3_01).

ObjectDB Support
edit
delete
#5

There is some definite improvement.  The test queryDepByIdComponents() which invokes the named query "SELECT d FROM test.Domain.Department AS d WHERE d.domainId.accountId = :aid AND d.domainId.objectId = :oid") now succeeds.

However, the test queryDepById() which invokes the named query "SELECT d FROM Department AS d WHERE d.domainId.equals(:id)" does not not succeed.  When I use em.find() as pass in the generated DomainId object, the Department object is found.  I also added an assert to the queryDepById() test to ensure that the equals() method of DomainId, which I overrode does work, so my new test and the resulting error follows.


@Test
public void queryDepById(){
            //Ensures DomainId.equals() performs as expected.
    assertTrue(TestDepartment.departmentId.equals(TestDepartment.departmentId));
    TypedQuery<Department> q =
        em.createNamedQuery("Department.findByPK", Department.class);
    q.setParameter("id", TestDepartment.departmentId);
    Department d = q.getSingleResult();
    assertNotNull(d);
    assertNotNull(d.getDomainId());
    assertEquals(TestDepartment.departmentId, d.getDomainId());
    TestDepartment.foundResult = d;
}
[ObjectDB 2.0.3_01] javax.persistence.NoResultException
No matching results for a unique query (error 782)
    at com.objectdb.jpa.JpaQuery.getSingleResult(JpaQuery.java:592)
    at test.test.TestDepartment.queryDepById(TestDepartment.java:61)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
    at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

 

edit
delete
#6

Please use = instead of equals:

SELECT d FROM Department AS d WHERE d.domainId = :id
ObjectDB Support
edit
delete
#7

Outstanding, that seems to be working.  I think I had the following part of the manual confused.

Instances of user defined classes (entity classes and embeddable classes) can be compared by using the equality operators (=, <>, ==, !=). Note that comparison for these classes follow the logic of == in Java rather than of equals.

edit
delete
#8

You are right. The documentation was wrong (about comparison of embedded objects) - I just fixed it.

By the way, equals should also work - at least in embedded mode. I guess you are using client-server mode, and then your equals method is not available on the server. But if you add your DomainId class to the server classpath it should work. Anyway, = is more efficient than equals, and equals is not JPA portable.

ObjectDB Support
edit
delete
#9

I just read your changes to that part of the documentation and it's much more clear on how comparisons should be made.  Thanks for updating it!

edit
delete
#10

Thank you for your help in improving ObjectDB.

ObjectDB Support
edit
delete

Reply

To post on this website please sign in.