1

Though this seems less like a software engineering problem, IMHO it is actually about programming paradigms, instead of specific "how to solve this bug" questions...

Say we have article, author and tag. article---author is M:1, and article---tag is N:M. Table:

TABLE article: id, author_id, text
TABLE author: id, name
TABLE tag: id, tagname
TABLE article_tag_pivot: article_id, tag_id

I have two ways of writing down the "models":

Way 1: More like the Hibernate way.

@Data
class Article {
  long id; String text;
  Author author;
  List<Tag> tags;
}
// updated: add my original thought of implementation of these two classes
class Author {
  long id; String name;
  List<Article> articles;
}
class Tag {
  long id; String tagname;
  List<Article> articles;
}

Way 2: The fields in class is exactly one-to-one with the table columns.

@Data
class Article {
  long id; String text;
  long authorId;
  // no List<Tag> since not in article table
}
@Data
class ArticleTagPivot {
  long articleId;
  long tagId;
}
// updated: add my original thought of implementation of these two classes
class Author {
  long id; String text;
}
class Tag {
  long id; String tagname;
}

I wonder which way is better? I am using MyBatis. I see some pros and cons for each method so cannot decide...

Thanks very much for any replies!

2 Answers 2

2

The best way is to include lists of ids rather than full objects.

If you include full objects things become clunky

You often want a list of all the sub objects, rather than having it split amongst parents.

You can end up with multiple copies of the same object, where its shared among more than one parent.

If the sub objects are large or have their own sub objects things become even more heavy and complicated.

However, you dont want the joining table reflected in your objects, its just an artifact of the database.

So, do the join in your select, but only add the list of sub object Ids to the parent.

Your client app can then retrieve the list of all objects and link to the parent as required. Avoiding the above complications.

so:

class Article
    string id
    List<string> tagIds
    List<string> authorIds

class Author
    string Id
    string Name
    List<string> articleIds

On your repository have dual methods which support your use case

repo.GetArticlesById
repo.GetAuthorsByArticleId

repo.GetArticlesByDateRange
repo.GetAuthorsByArticleDateRange

etc

If the number of items is small, you are likely to just want to get all of them every time.

repo.GetAllTags

Including a back link from the sub object isnt always required/desirable.

5
  • Thanks very much! So, e.g., when I want to show "my" articles and show tags beneath them, then what should I do? Shall I have List<Article> myArticles = repository.getSth(); List<Map<Article, Tag>> = ???? Or shall I have List<Pair<Article, List<Tag>>> listOfArticlesWithTags = repo.getsth()?
    – ch271828n
    Feb 11, 2020 at 13:02
  • 1
    updated with more detail
    – Ewan
    Feb 11, 2020 at 13:09
  • Thanks! Sorry I do not quite understand... So say I want to "by date range", then I need to: List<Article> articles = repo.GetArticlesByDateRange; List<Author> authors = repo.GetAuthorsByArticleDateRange; foreach(article in articles) { List<Author> authorsOfThisArticle = authors.filter(whose article_id == article.id); }?
    – ch271828n
    Feb 11, 2020 at 13:35
  • And (1) it seems that I need to create two methods whenever I have one more filter requirement (e.g. if I need "by fancy_field range", then need GetArticlesByFancy && GetAuthorsByFancy)... (2) it seems that the GetArticlesByDateRange needs to have JOIN because we need article.authorIds, and then GetAuthorsByArticleDateRange is using exactly the same SQL (?) with only difference in returning what...?
    – ch271828n
    Feb 11, 2020 at 13:37
  • Shall I do this: (0) The class Article has no List<Integer> authors; field. (1) List<Article> articles=repo.GetArticlesByDateRange just query the article table without JOIN without anything like "article.authors". (2) List<Author> repo.GetAuthorsWhereArticleIdIn(articles.ids).
    – ch271828n
    Feb 11, 2020 at 13:40
2

As you already noticed, there are pros and cons for each alternative, so it looks like neither is inherently better than the other. You need to choose what serves your application best. Intuitively I would favor the first option because articles are relatively heavy-weight things compared to tags, therefore it makes sense for an article to have a list of tags, while it feels a bit weird to have a list of articles associated with each tag. But this depends on your application, for some relatedness analysis it might be the right model.

Keeping the pivot table as a separate entity within your object model would force you to deal with details of the ORM mapping in the domain layer, where they don't belong.

3
  • "while it feels a bit weird to have a list of articles associated with each tag" --> Suppose we want to "show the articles tagged with XXX", then we may need that :)
    – ch271828n
    Feb 11, 2020 at 9:38
  • Er it seems that our thought of "Way 1: class Author & Tag" is a little bit different? So I update my question to include my original thought of implementations. Could you please have a look, thanks!
    – ch271828n
    Feb 11, 2020 at 9:42
  • 1
    Thanks for clarifying! Now the data model looks very straightforward, and option 1 looks clearly preferable to me. If there is a requirement to look up articles with a given tag, having the list of articles in the tag object makes sense, of course. Another option would be to use the ORM query mechanisms. Both are valid, and which is best in a given case depends on the actual usage. Feb 11, 2020 at 9:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.