I'm a bit confused by the effect of Cascade.ALL.
I have two classes:
class Task {
#OneToOne(mappedBy = "task", cascade = CascadeType.ALL, orphanRemoval = true)
private Status status;
}
class Status {
#OneToOne
#JoinColumn(name = "task_id", updatable = false)
private Task task;
#Column(name="name")
private String name;
}
I thought saving status does not affect class Task since the cascade is applied to class Task, not Status, but it does modify the task object in my database.
So I'm wondering if
Task task = status.getTask();
task.setName(xxx);
taskDao.save(task);
has same effect as
taskDao.save(status.getTask.setName(xxx));
Is it different for
statusDao.save(task.getStatus) and statusDao.save(status) ?
Thanks.
I think your annotations confuse who owns the relationship. On the one hand, you are specifying the #JoinColumn(name = "task_id") making the Status entity the owner of the relationship, while on the other, you specify #OneToOne(mappedBy = "task"... on the Task entity, making it the owner of the relationship.
So status owns the relationship and when you save it, it goes over to task and finds that it also owns the relationship and has a CascadeType.ALL on it, so it applies the cascading on the save (PERSIST).
You should probably decide which entity should own the relationship and either remove the mappedBy or the #JoinColumn
Related
My major problem is to remove (+persist) an entity:
I have an entity Property which has several PropertyAttributes - managed as list, i.e.
#OneToMany(mappedBy = "property", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
#JoinFetch(value = JoinFetchType.INNER)
private List<PropertyAttribute> propertyAttribute;
in the PropertyAttribute I have as well a reference to the Property, i.e.
#Id
#JsonIgnore
#JoinColumn(name = "property_id")
#ManyToOne(cascade = { CascadeType.MERGE })
private Property property;
the main problem arise when when I want to remove some of the attributes, e.g.
getEntityManager().remove(attrEntity);
because this is not found in the entity. If I introduce some code like
if (!getEntityManager().contains(attrEntity)) {
attrEntity = getEntityManager().merge(attrEntity);
}
System.out.println(getEntityManager().contains(attrEntity)); //==> always false
I have no clue why the merge() has no effect on the overall. Any ideas what to check or what to change?
Plus: I tried the workaround to remove the PropertyAttribute from the Property itself and merge(myChangedProperty) it but the removed attribute is not persisted, i.e. still in the database
I'm trying to delete the parent student or parent course and I get this error:
Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "student" violates foreign key constraint "fkeyvuofq5vwdylcf78jar3mxol" on table "registration"
RegistrationId class is a composite key used in Registration class. I'm using Spring data jpa and spring boot.
What am I doing wrong? I know that putting cascadetype.all should also remove the children when the parent is deleted but it is giving me an error instead.
#Embeddable
public class RegistrationId implements Serializable {
#JsonIgnoreProperties("notifications")
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "student_pcn", referencedColumnName="pcn")
private Student student;
#JsonIgnoreProperties({"teachers", "states", "reviews"})
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "course_code", referencedColumnName="code")
private Course course;
Registration class
#Entity(name = "Registration")
#Table(name = "registration")
public class Registration {
#EmbeddedId
private RegistrationId id;
When you're using a relational DB, you are setting entities with relationships between these entities.
The error that you're getting means that:
You're trying to delete a record that its primary key is functioning as a foreign key in another table, thus you can't delete it.
In order to delete that record, first, delete the record with the foreign key, and then delete the original that you wanted to delete.
I made it work by using hibernate #OnDelete annotation. Some how the JPA.persistence CascadeTypes were not working. They had no effect for whichever I chose.
Just like below. Now I can remove the parent Student or the parent Course and all children(Registrations) are deleted with them.
#Embeddable
public class RegistrationId implements Serializable {
#JsonIgnoreProperties("notifications")
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToOne
#JoinColumn(name = "student_pcn", referencedColumnName="pcn")
private Student student;
#JsonIgnoreProperties({"teachers", "states", "reviews"})
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToOne
#JoinColumn(name = "course_code", referencedColumnName="code")
private Course course;
Foreign keys guarantee that an entry will exist in another table. This is a way of ensuring data integrity. SQL will never allow you to delete this entry while it still deletes in the other table. Either (1) this is letting you know you would have made a grave mistake by deleting this thing which is required or (2) you would like to put in a cascading delete so that not only is this entry deleted but so is what is supposed to be referencing it in the other table. Information on cascading deletes can be found here and written fairly easily (https://www.techonthenet.com/sql_server/foreign_keys/foreign_delete.php). If neither of these two descriptions fits you, evaluate why your foreign key relationship exists in the first place because it probably should not.
Try this method too. I got the answer with this method,This is just a test to remove.
Pay attention to the cascade!
MyUser Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String mobile;
#Column(unique = true)
private String email;
private Long date;
private LocalTime localiime;
private LocalTime localiimeend;
#ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinColumn(foreignKey = #ForeignKey(name = "role_fk"))
private Role role;
Role Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#OneToMany(mappedBy = "role", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<MyUser> users;
#ManyToOne (fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinColumn(foreignKey = #ForeignKey(name = "rolecat_fk"))
private rolecat rolecat;
rolecat Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "rolecat", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Role> roles;
See the following relations:
The Table RECIPE_USERCATEGORY_REL has an ON UPDATE CASCADE trigger, so if I would execute the following command in psql console, the value of ruc_ucat_category will also be updated automatically.
update usercategory set ucat_category = 'OldCategory' where ucat_category = 'NewCategory';
This works.
The problem is now Hibernate. I have this method in my service class:
public void renameCategory(String userId, String fromCategory, String toCategory)
{
TypedQuery<UserCategory> query = entityManager.createNamedQuery("UserCategory.findAllCaseSensitiveByUserIdAndPrefix", UserCategory.class);
query.setParameter(ApplicationConstants.PARAM_USER_ID, userId);
query.setParameter("category", fromCategory);
List<UserCategory> resultList = query.getResultList();
if (resultList == null || resultList.isEmpty())
{
return;
}
UserCategory userCategory = resultList.get(0);
userCategory.setCategory(toCategory);
}
I can assure that userCategory has the value 'OldCategory'. In my opinion, the update should work, because the trigger of the database should update the value of the relation table, but nothing happens. Why is this so?
Additional information: In my Entities, there is no #OneToMany and #ManyToOne declaration on the USERCATEGORY <-> RECIPE_USERCATEGORY_REL relationship (only on RECIPE <-> RECIPE_USERCATEGORY_REL relationship it is). This is because RECIPE_USERCATEGORY_REL is not a real join table. USERCATEGORY is similar to a growing lookup table, so Hibernate must not interfere the workflow here. The only relation of USERCATEGORY <-> RECIPE_USERCATEGORY_REL is the referential integrity in the database.
This is what the entity looks like, but as I said, there is no hibernate relation to the category table since Hibernate should not take care about this relation:
#Table(name = "RECIPE_USERCATEGORY_REL")
public class RecipeUserCategoryRel implements Serializable
{
private static final long serialVersionUID = 1L;
#EmbeddedId
private RecipeUserCategoryRelPk recipeUserCategoryRelPk = new RecipeUserCategoryRelPk();
#MapsId("rcpId")
#ManyToOne
#JoinColumn(name = "ruc_rcp_id", referencedColumnName = "rcp_id", insertable = false, updatable = false)
private Recipe recipe;
...
}
and...
#Embeddable
public class RecipeUserCategoryRelPk implements Serializable
{
private static final long serialVersionUID = 1L;
#Column(name = "ruc_rcp_id")
private Long rcpId;
#Column(name = "ruc_ucat_category")
private String category;
#Column(name = "ruc_ucat_acc_identifier")
private Long identifier;
public RecipeUserCategoryRelPk()
{
}
...
//getters, setters, hashcode, equals
}
I read in some other postings that it is not allowed in JPA to change the primary key. However, my use case is definitely changing the primary key, but in my case, it's not a common use case and no 'real' part of the application, but there are cases where users need to modify old data, so I need to provide this functionality.
(As workaround, I made a native update query)
I have 2 tables mapped #OneToMany and #ManyToOne and their setters and getters, so a user can have multiple transactions and a transaction could have only one user.
If I add a transaction in the User's list data goes into the table correctly and transaction can be retrieved by using User's getter. But when I use getter from the Transaction to find its owner, it doesn't find its respectively user from users table.
Reverse is also available, if I set a user for a transaction data is persisted in database and user can be retrieved by using Transaction's getter, but use of User's getter doesn't show any transaction..
The only way is to add a transaction in the User's list and set user for the transaction. Why is this happening? I thought that if I use only one of this actions the reverse should be available automatically.
User
#Entity
#Table(name = "users")
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(mappedBy = "user")
private Collection<Transaction> transactions;
Transaction
#Entity
#Table(name="user_transactions")
public class Transaction{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
I can't see any difference in tables, any action I do both tables have the same structure. How does JPA know how I saved the objects and build them back again...?
As far as I remember there is no default CascadeType.
You must specify one, for example in your case:
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Collection<Transaction> transactions;
you can read about cascade types here :
http://howtodoinjava.com/hibernate/hibernate-jpa-cascade-types/
I have two classes, Customer and CustomerProduct. I want the customerId(from customer) to be mapped in CustomerProduct. I used OneToMany mapping as follows.
Customer.java
#XmlTransient
#OneToMany(fetch = FetchType.EAGER, mappedBy = "customer")
public Set<CustomerProducts> getCustomerProducts() {
return customerProducts;
}
CustomerProduct.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "customer_id", nullable = false)
public Customer getCustomer() {
return customer;
}
I was able to save the datas, but for selecting a customer, I used named query which returned a Customer with CustomerProduct as well. I used restws. I used Response.ok(cust).build(); where cust had all the values in the server side.
When I get the response in the client side, I am getting the customers but not the CustomerProduct.
Did I miss something?
You need to remove the #XmlTransient annotation, it prevents the customerProducts property from serialization to a response.
See: http://docs.oracle.com/javaee/6/api/javax/xml/bind/annotation/XmlTransient.html
And to avoid infinity loop during serialization you either need add #XmlTransient to the customer property of the CustomerProduct entity or fetch this property lazily.