Search code examples
hibernatequarkus-panache

Hibernate: Get duplicates as result out of @OneToMany relationship


I'm new to hibernate and started to try a little bit around. Now I have a problem with duplicates when I try to get "Sub categories" from the Apache Derby DB.

My structure:

Category
|__Sub category(s)
.....|__Product(s)

My problem:

When I try to get the "sub categories" I get the right ones, but they are duplicated. (See below [children]). What I want is just one of the sub categories.

children: Array(9)
0: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
1: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
2: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
3: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
4: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
5: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
6: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
7: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
8: {description: 'A place to collect your single malts.', id: 101, name: 'Single Malt', status: 'verified'}
length: 9
[[Prototype]]: Array(0)
description: "A place to collect your whiskies."
id: 100
name: "Whisky"
products: []
status: "verified"
[[Prototype]]: Object

What I think the problem is:

I think that it has something to do with the @OneToMany annotation and the resulting join table. Because I have 9 "products" which are subordinated to the same "sub category". Unfortunately I have no clue what I did wrong in my code that this happens.

I thought about filter the duplicates after I get them out of the database. But it doesn’t feel right. I would really appreciate if someone can explain me the mistake and the best practice for that!

Code:

CategoryRepository.java

@RequestScoped
@Transactional(value = Transactional.TxType.REQUIRED,
        dontRollbackOn = {SqlException.class},
        rollbackOn = {SecurityException.class})
public class CategoryRepository implements CategoryCatalog {

    @Inject
    protected EntityManager em;

...

@Override
    public Category getCategory(Long id) {
        if (id == null) {
            return null;
        }
        try {
            Category category = this.em.find(Category.class, id);

            if(category != null){
                category.setProducts(this.filterFlaggedOrHiddenUserProducts(category.getProducts()));
            }
            return category;
        } catch (IllegalArgumentException ex){
            return null;
        }
    }

...

Category.java

@Entity(name = "CATEGORIES")
public class Category implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "CATEGORY_ID")
    private Long id;

    @ManyToOne
    private Category parentCategory;

    @OneToMany(mappedBy = "parentCategory", cascade = { CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
    private List<Category> children;

    @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
    private List<Product> products;

    @NotNull
    @Column(name = "CATEGORY_NAME")
    private String name;

...

Database tables with data

Table of categories:
Table of categories

Join table of categories and products:
Join table of categories and products


Solution

  • Solution

    If found a solution by myself. I have to use a query instead of find. But I still not understand why that happened. If someone could explain this behavior I would be thankful for that!

    WORKING - CategoryRepository.java

    @RequestScoped
    @Transactional(value = Transactional.TxType.REQUIRED,
            dontRollbackOn = {SqlException.class},
            rollbackOn = {SecurityException.class})
    public class CategoryRepository implements CategoryCatalog {
    
        @Inject
        protected EntityManager em;
    
    ...
    
    @Override
        public Category getCategory(Long id) {
            if (id == null) {
                return null;
            }
            try {
                Category category = this.em.createQuery("select c from CATEGORIES c WHERE CATEGORY_ID IS " + id, Category.class).getSingleResult();
    
                if(category != null){
                    category.setProducts(this.filterFlaggedOrHiddenUserProducts(category.getProducts()));
                }
                return category;
            } catch (IllegalArgumentException ex){
                return null;
            }
        }
    
    ...