njkremer / SqliteORM

A lightweight ORM for SQLite with Java
http://njkremer.github.com/SqliteORM/javadoc/index.html?com/njkremer/Sqlite/SqlStatement.html
Apache License 2.0
23 stars 6 forks source link

OneToMany NullPointerException #1

Open zachtaylor opened 11 years ago

zachtaylor commented 11 years ago

OneToMany relationship will lead to a null List reference (instead of an empty List) when the associated table has no associated values for the given primary key.

OneToMany Relationships should setXXX(new ArrayList()) at minimum

Workaround: initialize all OneToMany relationships

@OneToMany("myfieldkey")
private List<MyField> manyfields = new ArrayList<MyField>();
njkremer commented 11 years ago

Ah.. thanks for pointing this out. I will fix and have a unit test around it soon.

zachtaylor commented 11 years ago

No problem. I forked your repo to solve it myself, but didn't have time to figure out where to make the change.

njkremer commented 11 years ago

So I wrote a test to reproduce your issue, and I can't seem to reproduce it.

Here is the test:

    @Test
    public void testGettingARelationshipObjectWhenNoObjectsExist() throws DataConnectionException {
        createUser("Nick");

        User nick = e.select(User.class).getFirst();
        List<Thing> nicksThings = nick.getThings();

        assertTrue("The collection should be empty and not null", nicksThings != null);
        assertEquals("The collection should be empty", 0, nicksThings.size());

        deleteUser(nick);
    }

    public void createUser(String name) throws DataConnectionException {
        DataConnectionManager.init("test/test.db");
        User user = new User();
        user.setName(name);
        user.setPassword("123456");
        e.insert(user).execute();
    }

And I'm not instantiating the the things list to an empty list or anything. In fact if you look at line 471 in SqlExecutor.java it is instantiating the list that is being returned from the select that gets run when getting a relationship. The line looks like this List<T> objects = new ArrayList<T>();

So am I just misunderstanding your use case?

zachtaylor commented 11 years ago

I suspect that I used the 3-month-old SqliteORM.jar from the repo, instead of rebuilding the jar. Will verify soon.

njkremer commented 11 years ago

Okay, I updated the newest JAR to the repo, so you can use that one to test.

EDIT I've added a "post build" step to the eclipse project that runs ANT which will do the JARing so that when I make changes it'll automatically create the JAR. This way maybe the JAR will be more up-to-date.

zachtaylor commented 11 years ago

I experienced 2 errors when dropping in the update SqliteORM.jar:

  1. The namespace had changed to include "bin," ie, "bin.com.kremerk..."
  2. SqlExecutor is missing from the jar

I experienced a further error from manual building the Jar; class Advice from aopalliance project cannot be found

Finally, when adding the SqliteORM project as a dependency, I was able to eliminate all prior errors. I, however, encountered a fatal error at this point: "$Proxy5 cannot be cast to..."

It seems like any attempt to access any member of the list produces a class cast exception of this nature. In this case, specifically,

java.lang.ClassCastException: $Proxy5 cannot be cast to org.jpokemon.pokemon.move.MoveInfo
at SqlStatement.select(MoveInfo.class).where("number").eq(number).getList().get(0).getName();

EDIT It appears that the applicable one-to-many relationship is not queried. Furthermore, this $Proxy5 object is capable of returning the list of it's one-to-many-relationship (which is incorrectly empty).

That is, while in the process of testing, many MoveInfo, and MoveEffect objects are created. Each query produces output like so:

select moveinfo.* from moveinfo where moveinfo.number = 111;
select moveeffect.* from moveeffect where moveeffect.move_number = 111;

The relevant "select moveeffect.* ..." does still not appear with this update.

njkremer commented 11 years ago

Hmmm... I'll take a look into these issues.

njkremer commented 11 years ago

Okay, I confirmed the issue with the package, whoops. I update the ANT file to build the Jar with the correct path so the package should be fine. Also, it looks like SqlExecutor is indeed in the Jar, it may have been missing from the one committed specifically, but it seems that it is there now, not sure what happened there.

As for the "class Advice from aopalliance project cannot be found" error, all of the jars files in the 3rdParty directory need to added to your project explicitly. This is relatively common with 3rd party dependencies. I have since reworked the 3rdParty to be more user friendly for adding these dependent Jars. You will find all the needed jars in /3rdParty/lib

As for the class cast exception, I'd say try adding the new Jar file and see if that clears it up, if not I can checkout your source and take a further look.

njkremer commented 11 years ago

In response to your edit, can you post the exact line of code you're using to get the relationship?

SqlStatement.select(MoveInfo.class).where("number").eq(number).getList().get(0).getName();

appears to not have any relationship calls, meaning you don't appear to be accessing a list off of the MoveInfo object that's returned.

Can you also postt how your POJOs are setup? Like what does the MoveInfo class and MoveEffect class look like?

zachtaylor commented 11 years ago

I have created a gist here : https://gist.github.com/4449865

njkremer commented 11 years ago

Okay, cool. I'll make a quick demo project with these code snippets and a test db and get back to you. Did you by chance try it with the new jar (just updated today) and adding in the dependent jars from the 3rdParty folder?

njkremer commented 11 years ago

So I created a test class from scratch. Added your two classes you pasted in your gist (had to add an empty Slot class to prevent compiler from complaining), added the latest sqliteorm jar, added the 3rd party dependencies, and create a unit test with only the following:

    @Test
    public void testStuff() {
        MoveInfo.get(1);
    }

This was using your database from your repo, and this was the output:

select moveinfo.* from moveinfo where moveinfo.number = 1;
select moveeffect.* from moveeffect where moveeffect.move_number = 1;
[MoveEffect@106caf16]

I added in a println for moves.get(0).getName() which you mentioned was failing and got the following console output from running the test:

select moveinfo.* from moveinfo where moveinfo.number = 1;
select moveeffect.* from moveeffect where moveeffect.move_number = 1;
[MoveEffect@4f5ad5c6]
Absorb

So as you can see the newest jar and correctly adding the 3rd party jars should be all you need, make sure you also remove SqliteORM project as a dependency, just to make sure your test conditions are the same as mine here.

zachtaylor commented 11 years ago

Thanks for the update. Will test immediately.

EDIT Am conducting test from fully reverted version of JPokemon

zachtaylor commented 11 years ago

I continue to see java.lang.ClassCastException: $Proxy5 cannot be cast to org.jpokemon.pokemon.move.MoveInfo after properly updating and adding dependencies. When testing, I directly copied the files from the gist over the existing files, and copied the test. I only have left to suspect that it is a java compatibility issue. I am using Java 7, and will test with Java 6 soon. I have noticed you are using Java 6.

shortstuffsushi commented 11 years ago

I know this is an old post, but I'm pretty sure I figured out what the issue was. There is a slight variation in Zach's Gist and his actual code, specifically the fact that Zach's actual class was "implementing" an interface. I haven't been able to figure out exactly what the problem with the interface was, though it wasn't really an interface class to begin with, but removing it resulted in everything immediately working.

njkremer commented 11 years ago

We're you able to get the error to occur? Because I though I just checked out his code, and didn't use the gist.

On Apr 24, 2013, at 8:58 PM, Graham Mueller notifications@github.com wrote:

I know this is an old post, but I'm pretty sure I figured out what the issue was. There is a slight variation in Zach's Gist and his actual code, specifically the fact that Zach's actual class was "implementing" an interface. I haven't been able to figure out exactly what the problem with the interface was, though it wasn't really an interface class to begin with, but removing it resulted in everything immediately working.

— Reply to this email directly or view it on GitHub.

shortstuffsushi commented 11 years ago

Yeah, I got it to happen consistently using the PokemonInfo class.