azpublic
azpublic

Reputation: 1404

Multiple ForeignCollection of same type in ORMLite

I'm using OrmLite on android for a structure simmilar to the following :

The Book class has a collection of primary BookArticle and a collection of secondary BookArticle :

    @DatabaseTable(tableName = "BookV1", daoClass = BookDaoImplV1.class)
public class Book implements IBook {

    @DatabaseField(id = true)
    private String id;

    @ForeignCollectionField(eager = false)
    private ForeignCollection<BookArticle> primaryArticles;

    @ForeignCollectionField(eager = false)
    private ForeignCollection<BookArticle> secondaryArticles;

    // constructor getters setters etc...

}

Then the BookArticle is declared as follow :

    @DatabaseTable(tableName = "BookArticleV1", daoClass = BookArticleDaoImplV1.class)
public class BookArticle implements IBookArticle {

    @DatabaseField(id = true)
    private String id;

    @DatabaseField
    private String title;

    @DatabaseField
    private String summary;

    // for ORM mapping only
    @DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = BOOK_FIELDNAME, index = true)
    private Book book;

    // constructor getters setters etc..

}

now saving the BookArticles and Book with DAO works fine however when I want to fetch my Book entity from database and access either primary or secondary articles (after a refresh() ) I have a problem because both collections hold ALL the articles (both primary and secondary) that have the book's id in their 'book' column.

Obviously I would need these primary and secondary articles to be separated when fetched from DB.

I would have expected "foreignFieldName" to be the answer to my problem.

@ForeignCollectionField(eager = false, foreignFieldName = "secondaryArticles")
private ForeignCollection<BookArticle> secondaryArticles;

but apparently it does not work that way.

Is there a way for me to differenciate between these two collections ? Maybe using a simple annotation argument such as "owningFieldName" or similar that would get persisted in DB alongside the BookArticle data?

Many thanks for your help.

Alex

Upvotes: 3

Views: 1156

Answers (3)

Codenix
Codenix

Reputation: 36

if you want to add multible ForeignerCollections form the same typ to one class, you have to add 2 fields for ORM-Mapping in the BookArticle class and link this in the Book class to the ORM-Mapping fields.

e.g

Book class

@ForeignCollectionField(eager = false, foreignFieldName = "primaryBook")
private ForeignCollection<BookArticle> primaryArticles;

@ForeignCollectionField(eager = false, foreignFieldName = "secondaryBook")
private ForeignCollection<BookArticle> secondaryArticles;

BookArticle class

// for ORM mapping primary Books
@DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = BOOK_FIELDNAME, index = true)
private Book primaryBook;

// for ORM mapping secondary Books
@DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = BOOK_FIELDNAME, index = true)
private Book secondaryBook;

When you call now bookDao.queryAll() and access the secondary Articles list, you will see the articles are correctly linked.

The disadvantage is, that you have 2 columes in the BookArticle Table for foreigner keys.

Greeting, Codenix

Upvotes: 1

gio
gio

Reputation: 5020

From official doc Annotation Type ForeignCollectionField

Annotation that identifies a ForeignCollection field in a class that corresponds to objects in a foreign table that match the foreign-id of the current class.

Back to your code of Book class

@ForeignCollectionField(eager = false)
private ForeignCollection<BookArticle> primaryArticles;

@ForeignCollectionField(eager = false)
private ForeignCollection<BookArticle> secondaryArticles;

When you refresh you collections, ormlite just does simple query

SELECT * FROM article WHERE book_id = 'id of book'

and after converts result into BookArticlecollection

If you still want to have one-to-one relationship between book and article. Then simplest way to implement your initial requirement is next:

Book class

    @DatabaseTable(tableName = "BookV1", daoClass = BookDaoImplV1.class)
public class Book implements IBook {

    @DatabaseField(id = true)
    private String id;

    @ForeignCollectionField(eager = false)
    private ForeignCollection<BookArticle> articles;

    public Collection<BookArticle> getArticles(boolean primary) throws SQLException {
        articles.refreshCollection();
        Collection<BookArticle> result = new ArrayList<BookArticle>();
        for (BookArticle article : articles) {
            if (article.isPrimary() == primary) result.add(article)
        }
        return result;
    }
}

BookArticle class

@DatabaseTable(tableName = "BookArticleV1", daoClass = BookArticleDaoImplV1.class)
public class BookArticle implements IBookArticle {

    private static final String BOOK_FIELDNAME = "book_id";
    @DatabaseField(id = true)
    private String id;

    @DatabaseField
    private String title;

    @DatabaseField
    private String summary;

    @DatabaseField
    private boolean primary;

    public boolean isPrimary() {
        return primary;
    }

    // for ORM mapping only
    @DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = BOOK_FIELDNAME, index = true)
    private Book book;

    // constructor getters setters etc..

}

To do article primary you need:

  • link it to book;
  • set primary property to true;
  • update DAO;

To get book's primary articles :

  • call getArticles(true) of needed book entity;

For use case 'secondary' use same steps with false value.

Upvotes: 0

Estar
Estar

Reputation: 121

I ran into the same problem.

Not a real solution but you can try to use only one list with a boolean for switching between primary/secondary articles like this:

@DatabaseTable(tableName = "BookV1", daoClass = BookDaoImplV1.class)
public class Book implements IBook {

    @DatabaseField(id = true)
    private String id;

    @ForeignCollectionField(eager = false)
    private ForeignCollection<BookArticle> articles;

    // constructor getters setters etc...

}

And then for the BookArticle class

@DatabaseTable(tableName = "BookArticleV1", daoClass = BookArticleDaoImplV1.class)
public class BookArticle implements IBookArticle {

    @DatabaseField(id = true)
    private String id;

    @DatabaseField
    private String title;

    @DatabaseField
    private String summary;

    @DatabaseField
    private boolean isSecondary;

    // for ORM mapping only
    @DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = BOOK_FIELDNAME, index = true)
    private Book book;

    // constructor getters setters etc..

}

Hoping to see a real answer

Upvotes: 3

Related Questions