There's an Entity Class 'A' (supposed to be a Person),There's another Entity Class 'B' (supposed to be a Contract).
Entity 'A' has a relation #OneToMany to Class 'B' ( a person can sign alot of contracts). Entity 'B' also has a relation #OneToMany to Class 'A' (a contract can have many person signing it).
In this case, there's gonna be 2 JoinTable in database, but actually they both are somehow the same.
Is there anyway that i make them just using One JoinTable?
tnx for any help!
Looks like a #ManyToMany relation to me...
in class Person
#ManyToMany
#JoinTable(name="PERS_CONTRACTS")
public Set<Contract> getContracts() { return contracts; }
in class Contract
#ManyToMany(mappedBy="contracts")
public Set<Person> getSigners() { return signers; }
By using two #OneToMany there is no JoinTable.
you can use #ManyToMany like this
#ManyToMany
#JoinTable(
name="AB",
joinColumns=#JoinColumn(name="A_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="B_ID", referencedColumnName="ID"))
private List<B> bS;
Its a kind of Many to Many relationships. So it need just one junction table like person_contract in database. It will contains columns like:
Person_id
Contract_id
where both person_id & contract_id will be a composite unique key.
In hibernate it will be:
1. In Person table
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "person_contract ", joinColumns = {
#JoinColumn(name = "person_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "contract_id",
nullable = false, updatable = false) })
public Set<Contract> contracts;
In Contract table
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "contracts")
public Set<Person> persons;
Situation:
You are complicating things here, the appropriate relationship between your Entities would be ManyToMany, because :
A person can sign many contracts.
And a contract can be signed by many persons.
And one JoinTable in this relationship is sufficient to give you all the requested details:
Who signed a given Contract.
Which Contracts have a Person signed.
Mapping:
So your mapping will be like this:
In your Person class:
#ManyToMany(mappedBy = "persons")
private Set<Contract> contracts= new HashSet<Contract>();
And in your Contract class:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "PERSONS_CONTRACTS",
joinColumns = #JoinColumn(name = "CONTRACT_ID"),
inverseJoinColumns = #JoinColumn(name = "PERSON_ID")
)
private Set<Person> persons= new HashSet<Person>();
You can check this Hibernate Many-to-Many Association Annotations Example for further details.
Related
I have this mapping at Person entity:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "PersonAddress", joinColumns = { #JoinColumn(name = "personId") }, inverseJoinColumns = {
#JoinColumn(name = "addressId") })
private Set<Address> addresses;
And this at Address entity:
#ManyToMany(mappedBy = "addresses")
private Set<Person> owners;
I tried all cascade options available, but everytime when I save a Person entity it removes all relations with addresses. Is it possible to keep addresses when saving Person?
its not recommended to use CascadeType.REMOVE or CascadeType.ALL in to-many associations, you should implement the removal of child entities yourself.
Here the link it explains with details how to do the implementation.
https://thorben-janssen.com/avoid-cascadetype-delete-many-assocations/
I have these two classes :
public class ClassA extends [...] implements [...] {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = JOIN_TABLE_NAME,
joinColumns = #JoinColumn(name = COLUMN_REF_A, referencedColumnName = COLUMN_ID_A),
inverseJoinColumns = #JoinColumn(name = COLUMN_REF_B, referencedColumnName = COLUMN_ID_B))
private List<ClassB> fieldClassB;
}
public class ClassB extends [...] implements [...] {
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "fieldClassB", cascade = CascadeType.ALL)
private List<ClassA> fieldClassA;
}
When I delete ClassB (via the spring data jpa repository), Hibernate also deletes instances of ClassA, whereas I just want the rows in the JOIN_TABLE_NAME table to be deleted (the other issue is that, due to the cascade mode, deleting the ClassA entities also delete other ClassB referenced by these ClassA).
Is there any way to handle this without having to create the join entity and to replace the #ManyToMany annotations by #OneToMany referencing the new join entity ?
Cascade Remove in a manyToMany it's not only applied to the link table, but to the other side of the association as well.
So Cascade.ALL which inherit remove too is almost always a bad thing to have on a manyToMany as it ends up deleting things not only from association table.
What you want is to have add and remove method in your entities to do the work and keep both list synchronized:
public class ClassA extends [...] implements [...] {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = JOIN_TABLE_NAME,
joinColumns = #JoinColumn(name = COLUMN_REF_A, referencedColumnName = COLUMN_ID_A),
inverseJoinColumns = #JoinColumn(name = COLUMN_REF_B, referencedColumnName = COLUMN_ID_B))
private List<ClassB> fieldClassB;
public void addClassB(ClassB b) {
fieldClassB.add(b);
b.fieldClassA().add(this);
}
public void removeClassB(ClassB b) {
fieldClassB.remove(b);
b.fieldClassA().remove(this);
}
}
I wish to have a one to one association with a join table in unidirectional way. -
Tables :
A (A_id, D_id, A_Data)
B (A_id, C_id) // Join table just contain relation between A and C
C (C_id, C_Data)
Class A {
.
.
#OneToOne(cascade = CascadeType.ALL)
#JoinTable(name = "B",
joinColumns = #JoinColumn(name = "A_id", referencedColumnName = "A_id"),
inverseJoinColumns = #JoinColumn(name = "C_id", referencedColumnName = "C_id"))
private C c;
}
I am using hibernate with jpa 2.0.
Entity D is not important in the model hence ignored.
I only wish to read data ,hence insert/update/delete use cases should not be concern, but one can suggest best practice in that case also.
This setup does not work. Can some one suggest how to do it in correct way?
It gives following exception
org.hibernate.MappingException: Unable to find column with logical name: A_id in org.hibernate.mapping.Table(A) and its related supertables and secondary tables
In order to get your desired schema:
// Given the following C entity
#Entity
public class C {
#Id
#Column(name = "C_ID")
private long id;
private String C_Data;
//...
}
// A Entity should be
#Entity
public class A {
#Id
#Column(name = "A_ID")
private long id;
private String A_Data;
#OneToOne(cascade = CascadeType.ALL )
#JoinTable(name = "B", joinColumns = #JoinColumn(name = "A_id"), inverseJoinColumns = #JoinColumn(name = "C_id", unique = true))
private C c;
//...
}
I've omitted referencedColumnName, so hibernate will map it to the entity primary key.
Note also that A_id column of B table will be the primary key.
I have 2 entities - User and Role which have following relations: User has a manytomany relation to itself and a manytomany relation with the Role entity.
#Entity
public class UserEntity implements Serializable {
#Id
#Column(length = 12, columnDefinition = "BINARY(12)", name = "Id", unique = true)
private byte[] id;
#Column(name = "Login", unique = true, nullable = false)
private String login;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Login") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "User_User",
joinColumns = { #JoinColumn(name = "UserParent") },
inverseJoinColumns = { #JoinColumn(name = "UserChild") })
private Collection<UserEntity> children;
...
}
and Role:
public class RoleEntity implements Serializable{
#Id
#Column(name = "Id", unique = true, nullable = false)
private String id;
...
}
The strange thing about the setup of DB is that the User_User relation is based on the binary Id keys
create table if not exists User_User (
UserParent binary,
UserChild binary
);
and the user-role is based on varchars
create table if not exists KNUser_UserRole (
UserLogin varchar,
RoleId varchar,
);
Now, when it runs, the user-user relationship work well. However, when I try to access the collection returned for roles, I get a ClassCastException:
java.lang.ClassCastException: **.entity.UserEntity cannot be cast to [B
at org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor.extractHashCode(PrimitiveByteArrayTypeDescriptor.java:41)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:201)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:205)
at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:114)
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:79)
at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:240)
at org.hibernate.engine.internal.StatefulPersistenceContext.getCollectionOwner(StatefulPersistenceContext.java:740)
at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1181)
at org.hibernate.loader.Loader.readCollectionElements(Loader.java:800)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:651)
at org.hibernate.loader.Loader.doQuery(Loader.java:856)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2175)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:622)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:82)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1606)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:379)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
It looks like the UserEntity is being cast to some binary(?) thing. However, the first relation between users themselves works fine, but the one with another table is wrong.
I am using different columns of different types to join tables. Is it allowed to do it this way?
Another strange thing is that when I switch the #Id annotation to be on the login field, the roles work fine, no issue, but then of course the self-join PersistentBag key is the Login instead of Id, which breaks the relation and no results are retrieved. But the conversion from UserEntity to the "[B" is not done.
Also if I leave things as in example and change the Id type to String (and the DB to varchar) it also starts working (of course not consistently with the User_User table).
What am I doing wrong? What is the reason for getting the classcastexception in this case? Why it work when I change the byte[] to String? Please let me know if you have any ideas. I do not want to change the DB design cause this would lead to lots migration and compatibility issues for clients already using the DB.
Just a note: the #Id has to be on the Id binary field as otherwise I wouldn't be able to make a self-join (I was unable to point twice to a column not being a primary key see: Is Hibernate ManyToMany self-join possible for non-key columns? getting mappingException).
Cheers
Adam
the referred column in your join table must be unique entry, here if you put #Id on login field then it works fine,but when you change it to different other than #Id column you cant be sure about the entries will be unique.what you can do is,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
I think it should work.
In my DB I have three table USER, PERMISSION and a junction table USER_PERMISSIONS. As you can see I have clear many-to-many relationship.
And here is the question. I'm using a Hibernate. Is it possible to use only two classes, User and Permissions without creating a junction class UserPermissions? I mean can I use some kind of annotations to include my junction table USER_PERMISSIONS?
Yes you can. You need to use the #JoinTable and #JoinColumn annotation. Example below:
#Entity
#Table(name = "USER")
public class User {
#Id
#Column(name = "ID")
private Long id;
#ManyToMany
#JoinTable(name = "USER_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "PERM_ID", referencedColumnName = "ID"))
private Collection<Permissions> permissions;
A full working example with bidirectionaly many to many can be found in here http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany