I'm developing a logic in the server side of my project that will update an Entity in the database. But this entity has reference to a list of another entity.
So, for example I have an Entity Test like this one:
#Entity
public class Test {
#Id
#Column(name = "idTest", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "idTest", targetEntity = Test2.class, fetch = FetchType.LAZY)
private Collection<Test2> test2Collection;
}
which has references to Test2 Entity.
#Entity
public class Test2 {
#Id
#Column(name = "idTest2", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String field1;
private String field2;
#ManyToOne
#JoinColumn(name = "idTest", referencedColumnName = "idTest")
private Test idTest;
}
In my case, I'm updating the Collection test2Collection of Test entry and eventually I'm removing some of Test2 entries of it.
The simple merge method from EntityManager does not detect that some entries has been deleted when I call it passing the new updated Test entry.
I was thinking to track the removed entries from test2Collection by making a diff between the Test entry before updating the database with the new Test entry to be updated.
But I'm not sure this is the only (and best) way of doing this. Anyone else has another idea?
In your relationship, you have to add orphanRemoval, then if you remove one element of the collection and persist Test, the element deleted on the collection will be automatically delete of the Test2 table.
#OneToMany(orphanRemoval = true, mappedBy = "idTest", targetEntity = Test2.class, fetch = FetchType.LAZY)
private Collection<Test2> test2Collection;
Related
I am facing a weird issue where even though all fields are set in the java object, when I save the object hibernate tries to insert null values in the fields.
When I further debugged, I saw that while merging the new entity at this line hibernate generates an empty object and sets to the target instead of setting given entity to the target. This results in insert query with null values.
Am I missing some configuration here? Below are the example entities having associations similar to my case.
class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
#EqualsAndHashCode.Include
private VehicleType vehicleType;
#OneToOne(mappedBy="vehicle", fetch=FetchType.LAZY)
private Car car;
#OneToOne(mappedBy="vehicle", fetch=FetchType.LAZY)
private Truck truck;
}
class Car {
#Id
private Integer id;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#MapsId
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
...
}
class Truck {
#Id
private Integer id;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#MapsId
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
...
}
I encountered the same problem, in my case I have an application with:
public class Claim extends BaseEntity<Integer> implements Serializable {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "claimdetailsid", referencedColumnName = "id")
private ClaimDetails claimDetails;
#OneToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "beneficiaryid", referencedColumnName = "id")
private Beneficiary beneficiary;
....
}
When I saved the Claim entity, the Claim and ClaimDetails objects were inserted correctly. The other entities had all the fields null, except the id and the creation date.
I tried changing CascadeType.PERSIST to CascadeType.ALL, that solved my insert problem.
But the delete cascade doesn't work now.
I am trying to build a bidirectional one to many relationship with the spring data jpa but the list annotated with #onetomany always return one element.
Here is the code for my entities(setters and getters omitted):
#Entity
#Table(name = "sdk_sdk")
public class SDKEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String version;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "sdk")
#OrderBy("order ASC")
private List<SDKFileEntity> fileEntities;
}
And the second entity:
#Entity
#Table(name = "sdk_file")
public class SDKFileEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String fileType;
private Integer sdkId;
public SDKFileEntity() {
}
#ManyToOne
#JoinColumn(name = "id", insertable = false, updatable = false)
private SDKEntity sdk;
I am trying to have a manytoone mapping where the sdkId corresponds to the id from the SDKEntity class.
Whenever I try to get the sdkfiles from the sdkEntity using spring's repository, the size of the list is always 1.
So for example:
SDKEntity entity=repository.findOne(foo);
List<SDKFileEntity> files=entity.getFileEntities();
here the size of files is 1, I have to delete the first element from the database to obtain the second element.
For me the reason here was that a parent entity implemented equals and hashcode
and unfortunately in a way that all existing entities were equal.
And non of the child entities implemented it herself.
So then the #OneToMany relation returned only the first element.
Took me quite some time.
This part of Code looks suspicious
#ManyToOne
#JoinColumn(name = "id", insertable = false, updatable = false)
private SDKEntity sdk;
name = "id" it should be actual column name as written in database column name like this
#JoinColumn(name = "VISIT_ID", referencedColumnName = "ID")
#ManyToOne
private Visit visitId;
When deleting a parent entity I also want to remove the associated child entities (from the database). I have tried to make use of cascade on remove as seen below but I must be doing something incorrectly.
When calling remove on the parent entity object, I recieve the error message: "The entity is still referenced elsewhere in the database". I can confirm that the only place where the entity is referenced elsewhere in the database is in the two tables below (if I manually delete the child row from the database, the remove call on the parent works fine). I have been reading about entity objects and trying different things for the last 9 hours. What am I doing wrong?
Here is my parent table:
#Entity
#Table(name = "TURTLE_LOOKUP")
public class TurtleLookup implements Serializable
{
#Basic(optional = false)
#Column(name = "TURTLEID")
private int turtleid;
#Basic(optional = false)
#Column(name = "TURTLE")
private String turtle;
#OneToMany(mappedBy = "turtleType", cascade = CascadeType.REMOVE)
List<TurtleReview> turtleReviews;
...
}
Here is my child table:
#Entity
#Table(name = "TURTLE_REVIEW")
public class TurtleReview implements Serializable
{
#Column(name = "TURTLE_REVIEW_ID")
private int turtleReviewId;
#Column(name = "TURTLE_YEAR")
private int turtleYear;
#ManyToOne(cascade = CascadeType.REMOVE, optional = false)
#JoinColumn(name = "TURTLE_ID", referencedColumnName = "TURTLEID")
private TurtleLookup turtleType;
#Column(name = "IS_COMPLETE")
private short isComplete;
...
}
EDIT/UPDATE:
If I change CascadeType.REMOVE to CascadeType.ALL, the TurtleReview entities are successfully deleted from the database when deleting the parent TurtleLookup entity object. However, when calling the below function to create a new TurtleReview entity object, JPA tries to insert a new TurtleLookup entity in to the database, which throws the exception: "Entry already resides within the DB. Transaction rolled back". Below is the code executed when creating a new TurtleReview entity.
public void setDatasetReviewComplete(TurtleLookup turtle, Short year, boolean isComplete)
{
TurtleReview turtleReview = getTurtleReview(turtle, year);
if (turtleReview == null)
{
turtleReview = new TurtleReview();
turtleReview.setTurtleYear(year)
turtleReview.setTurtleType(new a.b.entity.TurtleLookup(turtle.getId(), turtle.getValue()));
}
turtleReview.setIsComplete(isComplete ? (short)1 : 0);
entityManager.persist(turtleReview);
}
try change cascade value to all or all-delete-orphan
#OneToMany(mappedBy = "turtleType", cascade = CascadeType.REMOVE)
List<TurtleReview> turtleReviews;
...
}
There might be an issue with your domain model, a part that is left out in the question. Do you possibly have circular cascades? If you have a circle of cascades and some of them are CascadeType.REMOVE and some are CascadeType.PERSIST, then Hibernate (not sure about other JPA implementation) will just do.... nothing when you call the remove() method. Without an error or exception message.
Try with hibernate #Cascade annotation:
#Cascade(value = CascadeType.ALL)
#OneToOne(mappedBy = "turtleReview") // mappedBy name of TurtleRewiew object field in TurtleLookup entity class
private TurtleLookup turtleType;
If your relationship is oneToOne you can't have oneToMany to the other side and you can't have List<TurtleReview>. If your relationship is oneToMany then your entities will be for example:
#Entity
#Table(name = "TURTLE_LOOKUP")
public class TurtleLookup implements Serializable
{
#Basic(optional = false)
#Column(name = "TURTLEID")
private int turtleid;
#Basic(optional = false)
#Column(name = "TURTLE")
private String turtle;
#OneToMany(mappedBy = "turtleType") // or add cascade = javax.persistence.CascadeType.ALL and remove #Cascade if you are not using hibernate
#Cascade(value = CascadeType.ALL)
List<TurtleReview> turtleReviews;
...
}
#Entity
#Table(name = "TURTLE_REVIEW")
public class TurtleReview implements Serializable
{
#Column(name = "TURTLE_REVIEW_ID")
private int turtleReviewId;
#Column(name = "TURTLE_YEAR")
private int turtleYear;
#ManyToOne
#JoinColumn(name = "TURTLE_ID", referencedColumnName = "TURTLEID")
private TurtleLookup turtleType;
#Column(name = "IS_COMPLETE")
private short isComplete;
...
}
I am trying to solve a problem regarding category with child categories and parent category on same entity. My database is already set and I can't change it. So, I have mapped my entity this way:
public class Category implements Serializable {
private static final long serialVersionUID = -3432724244623524272L;
#Id
#Column(name = "id")
private Long id;
#Column(name = "key", nullable = false)
private String key;
#Column(name = "category_name", nullable = false)
private String name;
#Column(name = "description")
private String description;
#ManyToOne(targetEntity = Category.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "parent_key", referencedColumnName = "key")
private Category parentCategory;
#OneToMany(mappedBy = "parentCategory", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Category> childCategories;
//getters and setters ommited
}
Note, that the child categories and parent category is not mapped using the ID attribute, but the "key" attribute. This "key" is not a FK. When JPA is trying to get the data, my application crash. But this crash is look like an infinite loop. No exception is thrown.
What am I doing wrong?
Possible infinite loop :
You load an object A
This object has a child B, which is loaded as well since you use FetchType.EAGER
B has a parent, which is A, which is loaded again
etc.
Try using FetchType.LAZY.
I have a relationship n/n between Product and Order
So I have a third table ProductOrder, because I need new columns when they are created.
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ORDER_SEQ")
#Column(unique = true, nullable = false, updatable = false)
private Long idOrder;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ProductOrder> productOrder;
//get and setter
here is the ProductOrder:
#Entity
#IdClass(ProductOrderId.class)
public class ProductOrder implements Serializable {
private static final long serialVersionUID = 3943799614725570559L;
#Id
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "product_id")
private Product product;
#Id
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "order_id")
private Order order;
private Integer qtdProduct;
private Double unitValueProductOrder;
//get and setter
also My ProcutOrderId (just in case)
public class ItemCompraId implements Serializable {
private Long compra;
private Long produto;
//get and set
and my Order entity:
#Entity
#SequenceGenerator(name = "ORDER_SEQ", sequenceName = "s_compra", initialValue = 1, allocationSize = 1)
public class Order implements Serializable {
private static final long serialVersionUID = 3943799614725570559L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ORDER_SEQ")
#Column(unique = true, nullable = false, updatable = false)
private Long idOrder;
private Double orderValue;
private Date creatingDate;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ProductOrder> productOrder;
So basically I have any Products ALREADY persisted in db... I just got a list of them when some order is about to be ordered. So I wanna persist a new object (Order) based on some already persisted object (products). This is the method invoked on managedbean to persist an Order.
public String doOrder() throws Exception {
try {
Order order = new Order();
compra.setCreatingDate(new Date());
compra.setOrderValue(null);
if (compra.getProductOrder() == null)
compra.setProductOrder(new HashSet<ProductOrder>());
for (Product product : listOfMyCartOfProducts) {
ProductOrder productOrder = new ProductOrder();
productOrder.setQtdProduct(100);
productOrder.unitValueProductOrder(null);
productOrder.setOrder(order);
productOrder.setProduct(product); //I THINK THAT THE PROBLEM IT'S HERE
order.getOrderProduct().add(productOrder);
}
ejbInvoke.persist(order); //tryed .merge and it doesn't work aswell
return "stuff";
Any ideas?
I'm desperate.. I need this working for yesterday..
Any help please??
Btw I'm using JSF 2.0, Hibernate with JPA 2.0 and Postgres.
Regards,
You have set the order->productOrder->product relationships to cascade the persist (included in cascadeType.ALL). When you call persist on order, you are in effect calling persist on ProductOrder and Product as well, which is expected to throw an exception if any of them already exist in the database.
Either
1) remove the cascade persist option on the productOrder->product relationship so that persist is not getting called - with the draw back that you will have to manually call persist if you ever associate new Products through a new Order.
2) Call em.find using the product pk, and associate the instance returned to the productOrder->product relationship
3) use em.merge instead which will cascade over each relationship and decide on its own if the entity exists or needs to be inserted. This will cause changes made within the product instance though to be merged as well.