ManyToMany in Play Framework on a same entity - java

I've googled a lot since two days now and I can't find the same problem like the one I'm currently facing regarding hibernate notation.
I want to represent a tree hierarchy based on items. Each item may have zero or more parent items and may have zero or more child items.
My current items class is defined as follow:
#Entity
public class Items extends Model {
#Id
public Integer id;
#...
public List<Items> parents;
public String name;
}
Of course, I would like to have the hierarchy built when I do things like:
item1.parents.add (item2);
item2.parents.add (item3);
I assume that some kind of bridge table (for items & parents relationships) will be automatically built in SQL database.
I would like to have a solution without adding a child field in Items class.
What should I add in #... in my above example please ?
Thx

That should do the trick
#ManyToMany
#JoinTable(name = "items_parents", joinColumns = #JoinColumn(name = "items_id"), inverseJoinColumns = #JoinColumn(name = "parent_id"))
public List<Items> parents = new ArrayList<>();
#ManyToMany(mappedBy = "parents")
public List<Items> children = new ArrayList<>();
It creates join table like:
create table items_parents (
items_id integer not null,
parent_id integer not null,
constraint pk_items_parents primary key (items_id, parent_id)
);
Notabene models should use singular form as name i.e. Item not Items
Edit:
To save ManyToMany relations, you need of course save the item you are adding, otherwise it won't have an id, sample:
Items parent = new Items();
parent.save(); // <- here
Items child = new Items();
child.parents.add(parent);
child.save();

Related

Backward compatibility of #orderColumn: hibernate

I have 2 entities with oneToMany relationship. I want to maintain the insertion order for child entity. I used #orderColumn for that. Code:
Parent Class:
#Entity
public class Order{
private String orderId;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
#NotEmpty
#OrderColumn
private List<OrderItem> orderItems = new ArrayList<>();
}
Child class:
#Entity
public class OrderItem{
#Id
private String orderItemId;
#ManyToOne
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
The issue that I'm facing here is orderColumn is not backward compatible. i.e. it adds an column in the child table with name "order_item_order". It works fine for the records that are getting created after this change but for the previous records, the column is null and it results in below exception:
org.hibernate.HibernateException: null index column for collection
I have tried setting the default value to 0 for the column. In that case it returns only one record for child.
Suggestions please.
You have two solutions :
Proceed with the #OrderColumn but fill it with the right values : index starting at 0, incrementing by 1 (migrate your data thanks to a sql scripts or a two steps migration from java)
Proceed with #OrderBy annotation : add a creation_date column, fill it when you store the object (like in the create(ModelClass model) method of your repository) and set it to a default value in the past

DTO projection on a recursive structure

In my data model there is an entity "location" which is recursively. Furthermore there are relations to other entities.
The corresponding JPA (Spring Data JPA) entity looks like:
#Entity
#Table(name = "location")
class Location{
#OneToMany(mappedBy = "parent", orphanRemoval = true)
#OrderBy("name ASC")
Set<Location> children = null
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "parent_id")
Location parent = null
#Column(name = "name")
String name = null
#OneToMany(mappedBy = "location", fetch = FetchType.EAGER)
Stops stops = null
...
What is the most performant way to do a read only query? I just need the information inside the entity (table location) with the complete recursive structure but no information from the related entities.
I've read the phrase DTO projection, but nothing about what to do with a recursive structure.
Reading a recursive structure is usually done by making use of what SQL calls a recuresive CTE. JPA does not support that out of the box, because not all RDBMS support it. If you know that your DBMS supports it, you can make use of the following SQL to do this:
WITH RECURSIVE nodes(id, parent_id) AS (
SELECT id, parent_id FROM location l where id = ?
UNION ALL
SELECT l.id, l.parent_id FROM nodes n JOIN location l ON n.parent_id = l.id
)
SELECT id, parent_id FROM nodes
With that you get a list of a specific and all parent location ids as well as their respective parents which is flat. You will have to bring structure into this.
List<Object[]> result = //get the result of the query
Map<Integer, LocationDto> locationMap = new HashMap<>();
result.forEach(r -> locationMap.put(result.get(0), new LocationDto(result[0], result[1])));
locationMap.values().forEach(l -> l.setParent(locaitonMap.get(l.getParentId())));
If you don't want to make use of plain SQL because of portability concerns or just because you don't want to give up on your abstraction, you can make use of Blaze-Persistence which works on top of JPA and adds support for CTEs. Your query with blaze-persistence would look like this
List<LocationCte> result = criteriaBuilderFactory.create(entityManager, LocationCte.class)
.withRecursive(LocationCte.class)
.from(Location.class, "l")
.bind("id").select("l.id")
.bind("parent").select("l.parent.id")
.where("id").eq(initialId)
.unionAll()
.from(Location.class, "l")
.innerJoinOn(LocationCte.class, "cte")
.on("cte.parent").eqExpression("l.id)
.end()
.bind("id").select("l.id")
.bind("parent").select("l.parent.id")
.end()
.from(LocationCte.class)
.getResultList();
You will also need this special entity class
#CTE
#Entity
public class LocationCte {
#Id Integer id;
Integer parent;
}

How to represent a Map<EntityType, Integer> relation using JPA?

I have a Order entity, and a Product entity. An order may have a number of pairs, representing the product and the number sold. What is an approprate relation in JPA to represent it?
(So far I have only found methods to associate a collection of EntityA with EntityB. e.g. EntityA contains a List<EntityB>. )
If the quantity is all there is to this association and you do not need to navigate from Product→Order, you can consider the Integer quantity as an element collection and do the following - Product stays the same:
public class Order {
#ElementCollection // 1
#CollectionTable(name="ORDER_PRODUCT_QTY") // 2
#MapKeyJoinColumn(name="PRODUCT_ID") // 3
#Column(name="QUANTITY") // 4
private Map<Product, Integer> quantities;
}
It is a collection of basic types (integers for the quantity), keyed by the entity
It is multivalued, so needs a separate table; you optionally want to specify its name
The separate collection table will contain column(s) pointing to the Order entity, column(s) pointing to the Product and a column for the quantity value itself. This lets you set the name of the FK referencing the Product table and is optional.
This lets you specify the name of the column holding the quantity value. Optional too.
If you have reasons to believe that this is not enough then you may want to create a distinct entity representing the association, like:
Order ← OrderItem → Product
Order has many OrderItems, Product has many OrderItems, Order has many Products indirectly through OrderItem, Product can be found in many Orders, indirectly through OrderItem and the quantity is in the OrderItem. Representing this kind of "relation with value" as an entity is more flexible than collection mapping.
you have to map entity like
In Order.jave
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "order_Id")
#JsonBackReference
private List<Product> product = new ArrayList<Product>();
In Product.jave
#ManyToOne
#JoinColumn(name = "product_Id", referencedColumnName = "product_Id", nullable = false, insertable = false, updatable = false)
#JsonBackReference
private Order order;
I am also using the above code but it is not working for me.
Below is my code.
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name="content_package_component_level_languages_language_assessment_map", joinColumns=#JoinColumn(name="id"))
#MapKeyJoinColumn(name="language_assessment_map_key", referencedColumnName = "id")
#Column(name="language_assessment_map")
private Map<Lang, Integer> languageAssessmentMap;

Hibernate One to Many and Many to One Relation

These two questions answered many of my questions, but I am still struggling to think about in real scenario!
Taking an example from the references.
Assume I have one Order and Multiple Items associated with it.
Now assume One Item can have one Returns but one Returns can have multiple Items.
What I understood is, Order to Items will be One to Many Relation.
Since I need to get Order of an Item, I will create column 'order_fk' in Item table to get it.
//Order entity
#OneToMany
#JoinColumn(name = "order_fk")
private List<Items> items;
//item entity
#Column(name = "order_fk")
private Long orderId;
Return to Items is One to Many mapping. One Return can have multiple Items. But one Item can have only one return id
//Return entity
#OneToMany
#JoinColumn(name = "return_fk")
private List<Items> items;
//item entity
#Column(name = "return_fk")
private Long returnId;
Am I thinking in the right direction? Please make me understand this relations and uni/bi-directional relationships.
Overall, I should get Items for an Order. Get Orderid of given Item. Get Items of Returns and get returnId of given Item.
Reference:
Difference Between One-to-Many, Many-to-One and Many-to-Many?
Hibernate/JPA ManyToOne vs OneToMany
This should be the correct mapping of the entities (database tables and columns are ok)
//Order entity
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Items> items;
//item entity
#ManyToOne
#Column(name = "order_fk")
private Order order;
//Return entity
#OneToMany(mappedBy = "return")
private List<Items> items;
//item entity
#ManyToOne
#Column(name = "return_fk")
private Return return;
cascade = CascadeType.ALL in first mapping means whenever you save/update/delete an order, its items will also be saved/updated/deleted, so adjust it to your needs, on other mapping as well.
Unidirectional relations mean only one side of the relation is aware of the other side. On your examples, if you removed items from Return entity you would have an unidirectional relation between Item and Return. With items present, you have a bidirectional relation.
I think you should use OneToMany in another way:
// Order entity
#OneToMany(mappedBy = "columnInItemsPointingAtOrders")
private List<Items> items;
Please check the docs: http://docs.oracle.com/javaee/6/api/javax/persistence/OneToMany.html.
And one more thing - you are not geting the IDs but entities:
//item entity
#Column(name = "order_fk")
private Order order;
I'm also new to this subject. What helped me to understand the relations is drawing the EER diagram and then synchronizing it to the test DB and experimenting. This does not answer your question but may give a direction.

Hibernate Parent/Child SELECT N+1 issue

I jave the following mapped superclass that provides a basic implementation for a parent/child self relationship to create a parent/child list for unlimited nesting of items (i.e. Categories)
#MappedSuperclass
public abstract class ParentChildPathEntity<N extends ParentChild> implements MaterializedPath<N> {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "parent_id")
private N parent;
#Column(name = "name", unique = true)
private String name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<N> children = new HashSet<N>();
If I load the entire table with fetch join on the parent and children, a single select loads all the records and i can happily traverse the tree. my problem comes in when i specify to retrieve a node on the tree. i want the node and all its children in a single select. below is the hql for loading the entire table:
hql.append(String.format("tree from %s tree ", tableName));
hql.append("left join fetch tree.parent ");
hql.append("left join fetch tree.children ");
if i specify the node name, i.e.:
where tree.name = :name
then hibernate retrieves the node, but when i access the children i get the SELECT N+1 issue. I realize why this is happening, (because of the tree.name = :name) but is there a way to write the HQL so it loads the specified node and all its children?
I'm just trying to figure out a way to support a simple nested item's list where i can retrieve any parent node and its children with a single select
thanks in advance,
Have you tried using the #BatchSize annotation?
#BatchSize(size = 20)
Ex:
#OneToMany(mappedBy = ..., fetch = FetchType.LAZY)
#BatchSize(size = 20)
public SortedSet<Item> getItems() { ... }
Then, if you specify the join to children in your HQL, you should be able to avoid n+1 select. I am not sure, offhand, if there is a way to specify the batch size in the HQL statement.

Categories

Resources