Friday, May 22, 2020

Struggling with Nested Tables and Unit Tests

I am using Spring Boot with Spring Data and attempting to write unit tests.

I have a three tables: Orders have more than one Taco. Tacos have more then one ingredient.

I am attempting to read an Order, with all its Tacos, and the Ingredients for each Taco.

In Order, tacos is a list. In Taco, ingredients is a list.

Here are some good references:
JPQL queries. https://www.objectdb.com/java/jpa/query/jpql/from
EntityGraphs: https://www.radcortez.com/jpa-entity-graphs/
Problems with EntityGraphs: https://stackoverflow.com/questions/31943989/spring-data-jpa-and-namedentitygraphs
JPA LifeCycle: https://www.javabullets.com/jpa-entity-lifecycle/
Baeldung EntityGraph example: https://www.baeldung.com/jpa-entity-graph
Removing old data from cache when testing: https://stackoverflow.com/questions/19057802/junit-hibernate-testing-lazy-associations-fetched
Transactional testing: https://www.marcobehler.com/2014/06/25/should-my-tests-be-transactional 

I have tried many ways to complete the task. The most fruitful was with EntityGraph.

When I attempted the query, I received a Hibernate multiple bag exception.

I researched many articles on the net. One solution is to use a set, but one site cautioned against that, since it must create a Cartesian product of all the tables.
https://vladmihalcea.com/hibernate-multiplebagfetchexception/

I tried to follow the advice, but my example is a little different. I believe the example uses the same approach as nested select statements. I cannot use that idea, since that example is accessing two collections from the same parent table. I want to get all tacos for an order, then get all ingredients for each of those tacos. I cannot perform a join on tacos.ingredients, since tacos is a list. I would have to join on the elements in the list.

I am beginning to think that the best solution is to change to sets instead of lists. Without it, I am forced to do a separate select to get the ingredients for each taco and then add those ingredients to the original result set of the tacos in the order.

Here is a new thought: read all the tacos with their ingredients. Somehow, organize these tacos into orders. Bad idea.

I imagine there is no other way than creating the Cartesian product. I will try with sets.

I attempted to work with sets. The changes were minimal, since many of the methods return Iterable.

I used an EntityGraph with a subgraph for ingredients, but it failed with a null pointer exception.

@NamedEntityGraph(
    name = "Taco_Order.detail",
    attributeNodes = {
        @NamedAttributeNode(value="tacos"
                            ,subgraph="tacos-subgraph"
        )
    }
    ,subgraphs = {
        @NamedSubgraph(
            name="tacos-subgraph",
            attributeNodes= {
                @NamedAttributeNode("ingredients")
            })
    }
    )

If I removed the subgraph, it worked, it even retrieved the ingredients. Confusing.

I removed the entity graph and debugged the code. The tacos were not loaded, as was expected.

I will add the entity graph again and see what happens. It may be a case of stale data in the session. I still got the ingredients. I had added an eager findById to taco, maybe that caused a problem.

I am stuck. I do not know why the ingredients are read.






No comments:

Post a Comment

Followers