I have 3 classes corresponding to 3 tables V, D and P. D had a FK to V (FK_V) and is join using OneToMany relationship. Also their exits a 4th table V_D_P which has the relationship of these V, D and P.
Following is what the data model for these looks like:
#Entity
#Table(name = "V")
public class V {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_V", referencedColumnName="Id", nullable = false)
private Set<D> d;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_V", referencedColumnName="Id", nullable = false)
private Set<V_D_P> vdp;
//Getters Setters etc.
}
#Entity
#Table(name = "V_D_P")
public class V_D_P {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_D", nullable = false)
private D d;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_P", nullable = false)
private P p;
//Getters Setters etc.
}
#Entity
#Table(name = "D")
public class D {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
//Getters Setters etc.
}
#Entity
#Table(name = "P")
public class P {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
//Getters Setters etc.
}
Now I want to persist V, D and P along with their relationship. I am
V v = new V();
D d = new D();
Set<D> dSet = new HashSet<>();
dSet.add(d);
v.setD(dSet); // Adding d to V ....(1)
P p = new P();
V_D_P vdp = new V_D_P();
vdp.setD(d); // Adding d to V_D_P ....(2)
vdp.setP(p);
Set<V_D_P> vdpSet = new HashSet<V_D_P>();
vdpSet.add(vdp);
v.setVdp(vdpSet);
entityManager.persist(v);
Now you can see the I am adding the same d object twice. Once to P and once to V_D_P. However since these are the same objects, only once of them should persist. However based on the hibernate logs I see that hibernate is trying to insert 2 different objects.
I also see the following exception: ORA-00001: unique constraint
Is there a way to let hibernate that these are the same objects and to persis them only once ?
Objects are uniquely identified by id. Therefore they have to have the same id.
Either provide the id before,
or persist once and refresh d object before saving the other (so it has the id set).
Try save() instead of persist(). As noted in this post, persist doesn't necessarily update the ID of the object immediately. That means that hibernate may encounter your 'd' object twice as is traverses the object graph. Both times it might say "the id is null, I need to insert a new one!".
Apparently this behavior isn't well defined, so it may not happen in all cases.
Do you really need the VDP.d field: can't you just use a bi-directional (mapped) relation between V and V(D)P (V(D)P would have a v field) and just navigate V(D)P.v.d?
Related
I have two model in One to One relation, but when t try to save its provide an ID Error,
#Entity
#Table(name = "app_a_table")
class A {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#OneToOne(mappedBy = "a", cascade = CascadeType.ALL)
private B b;
...
// Constructor
// Getter & Setter
}
#Entity
#Table(name = "app_b_table")
class B {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#OneToOne()
#JoinColumn(name = "A_ID", referencedColumnName = "ID")
private A a;
...
// Constructor
// Getter & Setter
}
When I tried to save like below
A newA = new A()
B newB = New B()
newB.setProperties().....
newA.setB(newB);
aRepository.save(newA);
An Exception thrown Column 'A_ID' cannot be null
How I can avoid the conflict
Your mapping is bidirectional, thus you also have to maintain it that way:
A newA = new A()
B newB = New B()
newB.setProperties().....
newA.setB(newB);
// add this:
newB.setA(newA);
aRepository.save(newA);
I have two entity classes.
Order.java
#Entity
#Table(name = "order_table")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, insertable=false, updatable=false)
private Set<Item> items;
// getters & setters & toString
Item.java
#Entity
#Table(name = "item")
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "order_id", nullable = false)
private Long orderId;
// getters & setters && toString
I created a test class like this:
#Test
public void createOrderWithItems() {
Item item = new Item();
item.setName("Iron Man");
Order order = new Order();
order.setName("Toy");
order.getItems().add(item);
Order created = service.createOrder(order);
Order orderById = service.getOrderById(order.getId());
System.out.println("Created Order: " + orderById);
Item itemById = service.getItemById(item.getId());
System.out.println("Created item: " + itemById);
Assert.notNull(created.getId(), "Order ID is Null");
}
Test is green but if you check output, you'll see that orderId field in the Item class is null.
Created Order: Order{id=1, name='Toy', items=[Item{id=2, name='Iron Man', orderId=null}]}
Created item: Item{id=2, name='Iron Man', orderId=null}
Does JPA not update this column in the db automatically? Is this column is redundant? If so, how can I retrieve this information from test code?
You need to set orderId explicitly.
item.setOrderId(order.getId());
order.getItems().add(item);
You can create a method addItem(Item item) in your Order class and hide this logic within it.
Cascading will create an entry in db but it won't initialize field. JPA annotations just indicate to JPA provider how to perform mapping between entity and table.
Moreover, check your annotations. #JoinColumn should be used in the entity which owns the relationship (the corresponding table has column as a foreign key). Check the top answer for this question for detailed explanations: What's the difference between #JoinColumn and mappedBy when using a JPA #OneToMany association
we are developing an application in which we have a tree-structure. Using Java EE and JPA (EclipseLink) and MySQL.
My Problem:
Saving the parent causes saving all childs.
I removed the CascadeType PERSIST and MERGE but still it cascades.
Example Code:
Parent Node:
#Entity
#Table(name = "node")
#NamedQuery(name = "node.findAll", query = "SELECT n FROM Node n")
public class Node implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "NAME", nullable = false, unique = true)
private String name;
#OneToMany(fetch = FetchType.LAZY)
#MapKey(name = "name")
#MapKeyColumn(name = "name")
#MapKeyJoinColumn(name = "name")
#JoinColumn(name = "NODE_ID", referencedColumnName = "id")
private Map<String, Data> datas = new HashMap<String, Data>();
And the Child Node:
#Entity
#Table(name = "data")
#NamedQuery(name = "data.findAll", query = "SELECT d FROM Data d")
#IdClass(DataId.class)
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Id
#Column(name = "NAME", nullable = false)
private String name;
The parent node "Node" has also a parent called System. The code is similar to Node and Data. There are 8 Levels, but all are similar, using OneToMany relation to build the tree.
Is it possible to avoid saving the Data (Child) objects when im saving the Node (Parent). Or for example i want to save an object from level 6 (System Node) so it should not persist or merge the child nodes.
Because im receiving some Nodes and Datas from a Client per REST and i want to save the Node immidiately but the Data objects later using a thread which checks which Datas are new and save only them.
Suppose we have entity A that contains a list of entities with type B (with lazy initialization).
Entity B has one BLOB field and some other, that doesn't contain much data.
How can I, using hibernate criteria query, get entity A with it's fields and each A-entity with list of Bs, but every B-entity without the BLOB field ?
Also, I do not want to extract As and iterate them to get Bs. (I now, how to use 'Projections' to extract just Bs with required fields).
Here is some code sample:
A-entity:
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
private List<B> list = new LinkedList<>();
}
B-entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "data", nullable = false)
#Lob
private byte[] data;
#ManyToOne(targetEntity = A.class, fetch = FetchType.EAGER)
#JoinColumn(name = "A_id", nullable = false)
private A a;
}
I'm new to JPA so I have a question about a many to many relationship (with a zero to more implementation) :
If you have a relationship like :
Like stated a product can excist without a order (normally they will be added as new products arrive) later on it can be used on 1 or more orders. An order must contain at least 1 or more products.
you must state the relationship
#Entity(name = "ORDERS")
public class Order {
#Id
#Column(name = "ORDER_ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long orderId;
#Column(name = "CUST_ID")
private long custId;
#Column(name = "TOTAL_PRICE", precision = 2)
private double totPrice;
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="ORDER_DETAIL",
joinColumns=
#JoinColumn(name="ORDER_ID", referencedColumnName="ORDER_ID"),
inverseJoinColumns=
#JoinColumn(name="PROD_ID", referencedColumnName="PROD_ID")
)
private List<Product> productList;
...............
The other attributes and getters and setters goes here
}
and
#Entity(name = "PRODUCT")
public class Product {
#Id
#Column(name = "PROD_ID", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long prodId;
#Column(name = "PROD_NAME", nullable = false,length = 50)
private String prodName;
#Column(name = "PROD_DESC", length = 200)
private String prodDescription;
#Column(name = "REGULAR_PRICE", precision = 2)
private String price;
#Column(name = "LAST_UPDATED_TIME")
private Date updatedTime;
#ManyToMany(mappedBy="productList",fetch=FetchType.EAGER)
private List<Order> orderList;
...............
The other attributes and getters and setters goes here
}
I wonder the zero to many relation is it still possible to just persist products who aren't linked (at the moment) to a order?
But when a order uses a product the orderlist in product should be updated and the productlist in orde also. How do I enforce this or does JPA this for me?
You can still persist a product without order.
You have to update the other side of the relationship by hand. JPA won't do that for you. (of course if you save one order and then refetch the product your collection of order will be updated)
EDIT
To explain second point:
Product persitentProduct = ... //some product
Order newOrder = new Order();
newOrder.getProducts().add(persitentProduct);
//at this point : persistentProduct.getOrders().contains(newOrder)==false
entityManager.persist(newOrder);
//at this point nothing has changed on the other side of the relationship:
// i.e. : persistentProduct.getOrders().contains(newOrder)==false
You would then get something like :
public class Order {
private List products;
...
public void addProduct(Product product) {
this.products.add(product);
if !(product.getOrders().contains(product) {
product.getOrders().add(this);
}
}
...
}
public class Product {
private List orders;
...
...
}
right? Or do I see this wrong