ManyToOne cannot resolved associated instances #189

Closed
eld0 opened this Issue Feb 28, 2013 · 25 comments

Comments

Projects
None yet
3 participants

eld0 commented Feb 28, 2013

HI All,
I've found a problem with ManyToOne relation in Kundera (I'm using Cassandra). The main reason is that an associated instance succesfully persists into database but when I try to fetch it it always null. I investigated records in DB and found that child object always sets a "link" to himself instead of setting link to parent , thats why it couldn't be resolved from DB.
So, I've checkout Kundera sources and found the place where links are set and found something like an issue. After code modifications ManyToOne works properly and child has a right link in cassandra.
Here is an annotation examples from my code:

@Entity
@Table(name = "tokens", schema = "myapp@myapp_pu")
@XmlRootElement(name = "Token")
public class Token
{
    @Id
    @Column(name = "token_id")
    private String id;
    @ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
    @JoinColumn(name = "client_id")
    private Client client;
.....
}

@Entity
@Table(name = "clients", schema = "myapp@myapp_pu")
@XmlRootElement(name = "Client")
public class Client
{
    @Id
    @Column(name = "client_id")
    private String id;
    @Column(name = "client_name")
    private String clientName;
}

And here is the code changed by me. (I can make a patch if you wish)

com.impetus.kundera.persistence.PersistenceDelegator
if (parents != null && !parents.isEmpty())
{
    for (NodeLink parentNodeLink : parents.keySet())
    {
        if (!parentNodeLink.getMultiplicity().equals(ForeignKey.MANY_TO_MANY))
            parentNodeLink.addLinkProperty(LinkProperty.LINK_VALUE, node.getEntityId());
    }
}

if (children != null && !children.isEmpty())
{
    for (Entry<NodeLink,Node> childNodeLinkEntry : children.entrySet())
    {
        NodeLink childNodeLink = childNodeLinkEntry.getKey();
        Node childNode = childNodeLinkEntry.getValue();
        if (!childNodeLink.getMultiplicity().equals(ForeignKey.MANY_TO_MANY))
            childNodeLink.addLinkProperty(LinkProperty.LINK_VALUE, childNode.getEntityId());
    }
}
previously it was (I got sources from 2.3.1)
if (parents != null && !parents.isEmpty())
{
    for (NodeLink parentNodeLink : parents.keySet())
    {
        if (!parentNodeLink.getMultiplicity().equals(ForeignKey.MANY_TO_MANY))
            parentNodeLink.addLinkProperty(LinkProperty.LINK_VALUE, node.getEntityId());
    }
}

if (children != null && !children.isEmpty())
{
    for (NodeLink childNodeLink : children.keySet())
    {
        if (!childNodeLink.getMultiplicity().equals(ForeignKey.MANY_TO_MANY))
            childNodeLink.addLinkProperty(LinkProperty.LINK_VALUE, node.getEntityId());
    }
}

Could you please help me? Is this an issue or I'm making something wrong?

Collaborator

mevivs commented Feb 28, 2013

KK,
Can you please look into this?

IT is same as :
https://github.com/impetus-opensource/Kundera/blob/trunk/kundera-tests/src/test/java/com/impetus/kundera/tests/crossdatastore/useraddress/MTOUniAssociationTest.java

If it is an issue, then @eld0 might need to raise a patch for this.
Thanks @eld0 for pointing this issue.

-Vivek

Collaborator

mevivs commented Feb 28, 2013

Possible to share CRUD code snippet?

@mevivs mevivs added a commit that referenced this issue Mar 1, 2013

@mevivs mevivs Fix for issue #189 b74f83e
Collaborator

mevivs commented Mar 1, 2013

This has been fixed in current trunk branch. Issue was data population was incorrect in case owning side of assication is not having any other column exception associated one.
A test case is added, please refer above mentioned commit revision number.

-Vivek

mevivs was assigned Mar 1, 2013

Is this similar to this error for kundera-mongo as well?
java.lang.NullPointerException
at com.impetus.kundera.query.KunderaQuery.initEntityClass(KunderaQuery.java:358)
at com.impetus.kundera.query.KunderaQuery.postParsingInit(KunderaQuery.java:333)
at com.impetus.kundera.query.QueryResolver.getQueryImplementation(QueryResolver.java:75)
at com.impetus.kundera.persistence.PersistenceDelegator.createQuery(PersistenceDelegator.java:576)
at com.impetus.kundera.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:485)

Collaborator

mevivs commented Mar 1, 2013

I guess, this is different. It must be something to do with invalid query syntax. As per above exception trace looks like query syntax is not correct and "From" keyword is missing from query. However, we are adding more validation around this.

-Vivek

I'm glad you're already working on improving the validations. The entities and query was working when I used hibernate. The query is "select e from ErrorMessages e where e.languages.languageCode= :langCode and e.errorCode= :errorCode".

Collaborator

mevivs commented Mar 1, 2013

Please share your entity definition(s).

/**

  • ErrorMessages generated by hbm2java
    */
    @entity
    @table(name = "ERROR_MESSAGES")
    @namedqueries({
    @namedquery(name=ErrorMessages.FETCH_BY_LANG_ERRCODE,
    query="select e from ErrorMessages e where e.languages.languageCode= :langCode and e.errorCode= :errorCode " )
    })
    public class ErrorMessages implements java.io.Serializable {
    public final static String FETCH_BY_LANG_ERRCODE = "ErrorMessages.FETCH_BY_LANG_ERRCODE";

    @id
    @generatedvalue(strategy = IDENTITY)
    @column(name = "error_id", unique = true, nullable = false)
    private Integer errorId;

    @manytoone(fetch = FetchType.LAZY)
    @joincolumn(name = "lang_id", nullable = false)
    private Languages languages;

    @column(name = "error_code", nullable = false, length = 20)
    private String errorCode;

    @column(name = "error_message", nullable = false, length = 1000)
    private String errorMessage;

    public ErrorMessages() {
    }

    public ErrorMessages(Languages languages, String errorCode,
    String errorMessage) {
    this.languages = languages;
    this.errorCode = errorCode;
    this.errorMessage = errorMessage;
    }

    public Integer getErrorId() {
    return this.errorId;
    }

    public void setErrorId(Integer errorId) {
    this.errorId = errorId;
    }

    public Languages getLanguages() {
    return this.languages;
    }

    public void setLanguages(Languages languages) {
    this.languages = languages;
    }

    public String getErrorCode() {
    return this.errorCode;
    }

    public void setErrorCode(String errorCode) {
    this.errorCode = errorCode;
    }

    public String getErrorMessage() {
    return this.errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
    }

}
//-----------------------------------------------
/**

  • Languages generated by hbm2java
    */
    @entity
    @table(name = "LANGUAGES")
    @namedqueries({
    @namedquery(name=Languages.FETCH_ALL,
    query="select L from Languages L" )
    ,@namedquery(name=Languages.FETCH_BY_CODE,
    query="select L from Languages L where L.languageCode = :languageCode" )
    })
    public class Languages implements java.io.Serializable {
    public final static String FETCH_ALL = "Languages.FETCH_ALL";
    public final static String FETCH_BY_CODE = "Languages.FETCH_BY_CODE";

    @id
    @generatedvalue(strategy = IDENTITY)
    @column(name = "lang_id", unique = true, nullable = false)
    private Integer langId;

    @column(name = "language", length = 100)
    private String language;

    @column(name = "language_code", length = 100)
    private String languageCode;

    @onetomany(fetch = FetchType.LAZY, mappedBy = "languages")
    private Set errorMessageses = new HashSet(0);

    public Languages() {
    }

    public Languages(String language, String languageCode) {
    this.language = language;
    this.languageCode = languageCode;
    }

    @jsonignore
    public Integer getLangId() {
    return this.langId;
    }

    public void setLangId(Integer langId) {
    this.langId = langId;
    }

    @jsonproperty("lang")
    public String getLanguage() {
    return this.language;
    }

    public void setLanguage(String language) {
    this.language = language;
    }

    @jsonproperty("code")
    public String getLanguageCode() {
    return this.languageCode;
    }

    public void setLanguageCode(String languageCode) {
    this.languageCode = languageCode;
    }

    @jsonignore
    public Set getErrorMessageses() {
    return this.errorMessageses;
    }

    public void setErrorMessageses(Set errorMessageses) {
    this.errorMessageses = errorMessageses;
    }

}

Collaborator

mevivs commented Mar 1, 2013

In case "e.languages.languageCode", if languages is a reference to associated entity, then such queries will not work and not currently supported by Kundera. However if you query over Owning entity's field, it will work.

-Vivek

Collaborator

mevivs commented Mar 1, 2013

Also your entity definition is not correct and you need to add schema attribute to @table annotation. Have a look at:
https://github.com/impetus-opensource/Kundera/blob/trunk/kundera-mongo/src/test/java/com/impetus/client/crud/PersonMongo.java

for reference.

eld0 commented Mar 1, 2013

Thanks alot for fix! Works as expected all my tests are green :)
But I have another issue with bidirectional associations on Cassandra.
So, when I'll add :

@OneToMany(mappedBy = "client", fetch = FetchType.LAZY)
private Set<Token> tokens;

to Tokens class, collection will always null. (there are related records in DB)
Is this an issue? (I'll raise a new ticket if needed)

Collaborator

mevivs commented Mar 1, 2013

Can you please share your CRUD code snippet? As it is working fine for me if i add

    @OneToMany(mappedBy = "client", fetch = FetchType.LAZY)
    private Set<Token> tokens;

it is returning as expected

Collaborator

mevivs commented Mar 1, 2013

This is what i have tried with existing test case.

     query="Select t from TokenClient t";
        q = em.createQuery(query);
        List<TokenClient> resultClient = q.getResultList();
        Assert.assertNotNull(resultClient);
        Assert.assertEquals(1, resultClient.size());
        Assert.assertEquals(2,resultClient.get(0).getTokens().size());

Please see, if we are in line with this.

eld0 commented Mar 1, 2013

Hi,
Looks like your patch didn't resolve an issue with @manytoone relation. My bad, I was run unit tests on database with correctly persisted relations (that were persisted with my patch). I made a short example test which failed on my environment. It's succesfully persists into DB (with wrong link to client) and when I try to fetch it it always null.
How I can attach my project to this ticket? (It says that only images supported)

And I'm using Cassandra 1.2. Maybe it will help...

Collaborator

mevivs commented Mar 1, 2013

Drop box link should be fine?

-Vivek

eld0 commented Mar 4, 2013

Hi,
Any news? Are you checked my example code?

Collaborator

mevivs commented Mar 4, 2013

Hi,
Yes. i am looking into it. Will get back to you by today.

-Vivek

Collaborator

mevivs commented Mar 5, 2013

Hi,
Changes suggested by you is a partial fix and will update you on this with proper fix.

-Vivek

eld0 commented Mar 5, 2013

Thanks for your quick response, I'll wait for updates.

@mevivs mevivs added a commit that referenced this issue Mar 6, 2013

@mevivs mevivs Fix Added for issue #189 553ae80
Collaborator

mevivs commented Mar 6, 2013

I have added a fix for this. Please verify it should be fine now.

-Vivek

eld0 commented Mar 6, 2013

Works like a charm. Thanks!
(bidirectional associations are working too)
Thanks a lot for your quick fix! I'm very appreciate this.

Collaborator

mevivs commented Mar 6, 2013

Great. Just to share one more fix is added and in case trouble in future you may take an update.
2.4(releasing in next 2-3 days) should work for you for all such unwanted issues!

Cheers,
-Vivek

Collaborator

mevivs commented Mar 6, 2013

Are we good to close on this?

-Vivek

eld0 commented Mar 6, 2013

yes, thanks!

mevivs closed this Mar 6, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment