I am trying to map entities so I'll have following or similar effect (preferably without OrderItem.quantity) :
Here is my entity :
public class Orders implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private UserCreds user;
#OneToMany
private List<Item> orderedItems;
I end up with OrdersItem join table mapped with just 2 columns :
item_id and order_id, both are keys
thus it wont let me persist order with repeating items. Adding id column for OrderItems should do the trick:
OrdersItem table that I except:
| ID | ORDER_ID | ITEM_ID
1 25 31
2 25 31
3 25 12
4 25 12
5 25 62
etc..
But I just couldnt get that working, or maybe my solution is completely wrong?
Using an element collection here might be a better option. An element collection works well for providing relationships with attributes.
It could look like this in your Orders class:
#ElementCollection
private Map<Item, Integer> itemQuantities = new HashMap<Item, Integer>();
EDIT: You can use #ElementCollection when the value type of the Map is a basic type or an Embeddable. Integer is a basic type.
If you decide to use an entity as the value type of the Map, you have to use #OneToMany or #ManyToMany
The key type has no influence on the annotation selection in this case, so you could use an Item or a Long as key, without having to change the annotation. This does however have an impact on the physical mapping annotations you can use.
Related
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
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;
Model O has an element collection of an enum type.
The abbreviated version
#Entity
class O {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ElementCollection(fetch=FetchType.EAGER)
#JoinTable(name = "o_s", joinColumns = { #JoinColumn(name = "o_id") })
#Column
private Set<SomeEnum> ss;
}
I am querying for all instances of O as follows
List<O> ret=session.createCriteria(O.class).list();
Now the result list contains duplicate entries.
If, there are 3 values in SS field, then the corresponding entry for O will appear 3 times in the result.
If there are 2 values, then the corresponding entry for O will appear 2 times in the result.
However, the database does not contain duplicate entries.
I have verified this behavior empirically.
What am I doing wrong?
That's caused by your eagerly loaded collection. You need to set the DictinctRootEntityResultTransformer to the criteria.
A better alternative, IMO, would be to use HQL:
select distinct o from O o
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.
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();