Hibernate how delete rows with null colums in join table - java

i have a problem in Hibernate.
I have 2 classes:
#Entity
#Table(name="grupos")
public class Group implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String nombreGrupo;
#ManyToMany
#JoinTable(name="grupos_usuarios",joinColumns=#JoinColumn(name="grupo_id"),inverseJoinColumns=#JoinColumn(name="usuario_id"))
private Set<Usuario> usuarios = new HashSet<Usuario>();
and the other class..
#Entity
#Table(name="usuarios")
public class User implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String nombreUsuario;
#ManyToMany(fetch = FetchType.LAZY ,mappedBy="usuarios")
private Set<Grupo> grupos = new HashSet<Grupo>();
So im using a join table.A group can have multiple users and one user can have multiple groups.The problem is when i try to delete a group
ERROR: Cannot delete or update a parent row: a foreign key constraint fails (`pfc`.`grupos_usuarios`, CONSTRAINT `FK_2pjiv067qnbhmbgjt35vogy93` FOREIGN KEY (`grupo_id`) REFERENCES `grupos` (`id`))
For example,if I delete a group,i dont have to delete the users(is the reason why im not using cascade.remove).In this case,if I delete a group, there are rows in the join table with null colums.I suppose this is the reason of the error.Is possible to delete this rows ?
Thanks

The reason for the error is explained in the exception message:
a foreign key constraint fails (pfc.grupos_usuarios, CONSTRAINT FK_2pjiv067qnbhmbgjt35vogy93 FOREIGN KEY (grupo_id) REFERENCES grupos (id))
You're trying to delete a group to which users belong. So either you decide this should not be possible (and the error is a good thing), or you decide that the users should first be removed from the group, and you have to explicitely do it before removing the group:
group.getUsuarios().clear(); // removes all the users of this group
session.delete(group);
EDIT:
To delete a user, you also need to remove the association. Since group.usuarios is the owner side of the association, that's the side that must be modified. Clearing User.grupos is not sufficient:
for (Group group : user.getGrupos()) {
group.getUsuarios().remove(user); // removes all the associations between the user and its groups
}
user.getGrupos().clear();
session.delete(user);

sounds good.but i got the same error.
maybe i will try to create two one#many relationship instead of many#many relationship.or i can use xml instead of annotations(with xml many#many works better?)i can read in internet some persons with the same problem

Related

JPA 2: how to declare primary-key columns in #ElementCollection tables

in JPA2 when we are using Embed-able (Basic Type like String.. etc ) object in Entity using with #ElementCollection and #CollectionTable annotation , the new table is created , but in new table how to declare primary-key contraint in column ? following is my code
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
private String salary;
#Transient
private String phnNum;
#Enumerated(EnumType.STRING)
private EmployeeType type;
#ElementCollection
#CollectionTable(name="vacations" , joinColumns=#JoinColumn(name="Emp_Id"))
private Collection<Vacation> vacationBooking;
#ElementCollection
private Set<String> nickNames;
...................
with this code the "vacation" and "employee_nickname" two tables are created in schema. but i want to declare the one primary-key column in both table . what i do for this?
It looks like a primary key per se is not supported by JPA 2.0:
From Wikibooks:
The JPA 2.0 specification does not provide a way to define the Id in the Embeddable. However, to delete or update an element of the ElementCollection mapping, some unique key is normally required. Otherwise, on every update the JPA provider would need to delete everything from the CollectionTable for the Entity, and then insert the values back. So, the JPA provider will most likely assume that the combination of all of the fields in the Embeddable are unique, in combination with the foreign key (JoinColumn(s)). This however could be inefficient, or just not feasible if the Embeddable is big, or complex.
Some JPA providers may allow the Id to be specified in the Embeddable, to resolve this issue. Note in this case the Id only needs to be unique for the collection, not the table, as the foreign key is included. Some may also allow the unique option on the CollectionTable to be used for this. Otherwise, if your Embeddable is complex, you may consider making it an Entity and use a OneToMany instead.
Do you mean that you want to assign 'id' from Employee table as foreign key to the Vacation table?
In that case, you should use #OneToMany instead of #ElementCollection

Getting Hibernate to cascade on save, but not delete?

So I'm having a problem with my hibernate implementation. When I try to delete a parent class, I receive a foreign key constraint exception on a class deep within the cascade hierarchy. Before I go into specifics, I'll first describe the relationships of the classes, as it has a bearing on how they need to be saved and deleted.
At the top level, I have a Customer class, which contains a list of DefaultMask objects. This is the master list, in that these default masks are used by other classes in my object hierarchy, but always from this list. Masks are only created into this list and deleted from this list.
Further down the hierarchy, I have a Column class, which can (optionally) have a DefaultMask set on it. To describe the relationship more succinctly;
A Customer OWNS zero to many DefaultMasks.
A Customer OWNS zero to many Columns.
A Column may have one DefaultMask.
In my application, when I attempt to delete a Customer, the exception comes from the foreign-key constraint on the Column class to the DefaultMask class, and I believe the problem is incorrect settings with CascadeType. I have researched the problem and found information on an attribute called mappedBy and on using Hibernate's own CascadeType.SAVE_UPDATE (in order to prevent Hibernate trying to delete a DefaultMask held by a Column), but I will admit I am a bit lost here and could use some direct guidance. Relevant code for the classes and the actual exception message are below.
Customer:
#Entity
public class Customer {
#Id
private String id;
#OneToMany(cascade = CascadeType.ALL)
private List<DefaultMask> masks;
//(Columns are held further down in hierarchy)
Column:
#Entity
#Table(name = "WarehouseColumn")
public class Column implements Comparable<Column> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int hibernateID;
#OneToOne
private DefaultMask mask;
DefaultMask:
#Entity
public class DefaultMask implements Mask {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int hibernateID;
private String type;
private String mask;
Exception message:
org.hibernate.exception.ConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (hibernate.WarehouseColumn, CONSTRAINT FK8BB153D994AD57D3 FOREIGN KEY (mask_hibernateID) REFERENCES DefaultMask (hibernateID))
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (hibernate.WarehouseColumn, CONSTRAINT FK8BB153D994AD57D3 FOREIGN KEY (mask_hibernateID) REFERENCES DefaultMask (hibernateID))
You're rying to delete a customer, which automatically deletes its list of default masks. But one of these masks is referenced by a column. So the database (and thus Hibernate) refuses to execute the deletion, because it would leave the column in an inconsistent state: it would reference a default mask that doesn't exist anymore.
So you have several functional choices:
leave it as it is: the customer can't be deleted because one of its masks is still referenced by a column
remove the cascade: deleting the customer will delete the customer but not its masks.
find all the columns which reference any of the default masks of the user to be deleted, and remove these columns. Then, remove the user and its default masks
find all the columns which reference any of the default masks of the user to be deleted, and set their mask fild to null. Then, remove the user and its default masks
Cascading is closely related to the concept of logical ownership.
Basically, you need to choose one of the following options:
Customer logically owns its DefaultMasks. In this case you want DefaultMasks to be deleted when you delete a Customer, therefore you need to use CascadeType.ALL. Since Column references DefaultMask, it's probably owned by Customer as well, and should be deleted to
It can be achieved by using bidirectional relationship between DefaultMask and Column with appropriate cascading, as follows:
#Entity
public class DefaultMask implements Mask {
#OneToOne(mappedBy = "mask", cascade = CascadeType.ALL)
Column column;
...
}
DefaultMasks are entities on its own, and Customer just references existing DefaultMasks. In this case you probably don't need to use cascading for this relationship at all.

Joining a legacy table with no PK with Hibernate

I have 2 legacy tables:
CREATE TABLE A (
ID NUMBER PRIMARY KEY ,
DATA NUMBER
)
CREATE TABLE A_CONF (
A_ID NUMBER, // FK to A
INFO VARCHAR2(256)
)
Creating the JPA entity for A is straightforward. Yet, what can I do retrieve the multiple INFO fields that can be associated to an instance of A since there is no PK in A_CONF and therefore cannot create an entity for it?
Thanks for helping.
Seems like you are looking for what JPA calls an "element collection":
#Entity
public class A {
#Id
private Long id;
private Long data;
#ElementCollection
#CollectionTable(name="A_CONF", joinColumns=#JoinColumn(name="A_ID")) // A_ID would be the default join column
#Column(name="INFO")
private Set<String> infos; // using Set assuming unique values
}
You can define a primary key in your model class even if your table doesn't have one, just pick one or some columns in your model and put them as ids.

JPA composite key #OneToMany

I have the following existing DB schema, which I'd like to recreate with Java and plain JPA annotations (using hibernate as provider, so hibernate specific annotations would work as a last resort):
CREATE TABLE users (
user_id NUMBER NOT NULL -- pk
);
CREATE TABLE userdata_keys (
userdata_key_id NUMBER NOT NULL, -- pk
key VARCHAR2(128) NOT NULL
);
CREATE TABLE users_userdata (
user_id NUMBER NOT NULL, -- fk users.user_id
userdata_key_id NUMBER NOT NULL, -- fk userdata_keys.userdata_key_id
value VARCHAR2(256)
);
I've thus created the following classes and annotations:
class User {
#Id
Long id;
#OneToMany
Set<Userdata> userdata;
}
class UserdataKey {
#Id
Long id;
String key;
}
class Userdata {
String value;
#EmbeddedId
UserdataId userdataId;
}
#Embeddable
class UserdataId {
User user;
UserdataKey userdataKey;
}
I left out columnName attributes and other attributes of the entities here.
It does however not quite work as intended. If I do not specify a mappedBy attribute for User.userdata, hibernate will automatically create a table USERS_USERS_USERDATA, but as far as I've seen does not use it. It does however use the table which I specified for the Userdata class.
Since I'm rather new to Java and hibernate as well, all I do to test this currently is looking at the DB schema hibernate creates when persisting a few sample entries.
As a result, I'm entirely puzzled as to whether I'm doing this the right way at all. I read the hibernate documentation and quite a bunch of Google results, but none of them seemed to deal with what I want to do (composite key with "subclasses" with their own primary key).
The mappedBy attribute is mandatory at one of the sides of every bidirectional association. When the association is a one-to-many, the mappedBy attribute is placed ot the one- side (i.e. on the User's userdata field in your case).
That's because when an association is bidirectional, one side of the association is always the inverse of the other, so there's no need to tell twice to Hibernate how the association is mapped (i.e. which join column or join table to use).
If you're ready to recreate the schema, I would do it right (and easier), and use a surrogate auto-generated key in users_userdata rather than a composite one. This will be much easier to handle, in all the layers of your application.

How to add a separate Primary Key to a Join Table in Hibernate

I have a question about Hibernate ManyToMany mappings. I have two classes A and B and the mapping between them is a ManyToMany mapping resolved by Hibernate:
#Entity
#Table(name="A")
public class A {
#Id
#GeneratedValue
private Long id;
#ManyToMany
#JoinTable(name="C", joinColumns=#JoinColumn(name="a_id"), inverseJoinColumns=#JoinColumn(name="b_id"))
private Set bs;
}
#Entity
#Table(name="B")
public class B {
#Id
#GeneratedValue
private Long id;
#ManyToMany(mappedBy="bs")
private Set bs;
}
As you can see, the Join Table I use is C. The foreign keys to A and B are "a_id" and "b_id". My understanding is, that Hibernate creates a composed Primary Key with a_id and b_id for table C.
I don't want to have an entity C in my model. But instead of a composed primary key on table C, I would like to have a generated ID and a unique constraint on the fields a_id and b_id.
Is it possible to tell Hibernate to use a separate primary key? Without adding an entity C?
I would appreciate any help.
Thanks a lot!
You should do iyt like this. But it can be appled only for list (not for sets)
#Entity
#TableGenerator(name="ids_generator", table="IDS")
public class Passport {
...
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="PASSPORT_VISASTAMP")
#CollectionId(
columns = #Column(name="COLLECTION_ID"),
type=#Type(type="long"),
generator = "ids_generator"
)
private Collection<Stamp> visaStamp = new ArrayList();
...
}
I don't think it is possible. And I don't see a problem in defining a C entity.
If you have any additional information ind the join-table, it will not be accessible to you, because your Set contains the target entity - A or B.
Also, your Sets would better make use of generics - i.e. Set<A> and Set<B>.
Btw, Hibernate might not be alarmed by the fact that the table creates another entity - using your current mapping might work (disregarding completely the id column). When you said "Hibernate creates", I assumed you are generating your schema from your entity model. Now it seems it's the opposite, so give it a try.
But instead of a composed primary key on table C, I would like to have a generated ID and a unique constraint on the fields a_id and b_id.
Normally the primary key of the JoinTable is made of the combination of both foreign keys. At least, this is what JPA would generate. But if you don't use the JPA provider to generate the model and if the PK can be generated by the database (using an IDENTITY column, a trigger, etc), then you should be able to use the C table for your ManyToMany association (without having to introduce an extra entity and to transform the relation in two OneToMany). Did you actually try?

Categories

Resources