I've these tables in my Oracle database (which I can't change):
Table_A
Table_B
Table_AB
ID (PK)
ID_B (PK)
ID_A (PK)
COMMON_ID (PK)
COMMON_ID (PK)
B_ID (PK)
...
...
COMMON_ID (PK)
Tables A and B have a many-to-many relationship and Table AB contains two foreign keys:
FK_1: ID_A, COMMON_ID referencing table A (ID, COMMON_ID)
FK_2: B_ID, COMMON_ID referencing table B (ID_B, COMMON_ID)
I tried to map these table but I have a problem in my jointable due the fact column "COMMON_ID" is shared between FK_1 e FK_2. Hibernate in fact tries to create the jointable with two column with the same name, generating this exception:
org.hibernate.MappingException: Repeated column mapping for collection: B.collectionA column: COMMON_ID
These are my entities, please can anyone help me to solve the problem? Thank you in advance!
Entity A:
#Entity
#Table(name = "Table_A")
#IdClass(value = A_PK.class)
public class A implements Serializable {
private static final long serialVersionUID = 2210864155927195752L;
#Id
#SequenceGenerator(name = "TABLE_A_SEQ", sequenceName = "TABLE_A_PKSEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TABLE_A_SEQ")
private long id;
#Id
#Column(name="COMMON_ID")
private Integer commonId;
#ManyToMany
#JoinTable(name = "TABLE_AB",
joinColumns = {
#JoinColumn(name = "ID_A", referencedColumnName = "ID"),
#JoinColumn(name = "COMMON_ID", referencedColumnName = "COMMON_ID")
},
inverseJoinColumns = {
#JoinColumn(name = "B_ID", referencedColumnName = "ID_B"),
#JoinColumn(name = "COMMON_ID", referencedColumnName = "COMMON_ID")
}
)
#LazyCollection(LazyCollectionOption.FALSE)
private List<B> collectionB;
getter, setter, hashcode, equals
Entity A_PK:
public class A_PK implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
private Integer commonId;
getter, setter, hashcode, equals
Entity B:
#Entity
#Table(name="TABLE_B")
#IdClass(value = B_PK.class)
public class B implements Serializable {
private static final long serialVersionUID = -5761968943282358603L;
#Id
#Column(name = "ID_B", unique = true, nullable = false)
#SequenceGenerator(name = "TABLE_B_SEQ", sequenceName = "TABLE_B_PKSEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TABLE_B_SEQ")
private Long id;
#Id
#Column(name="COMMON_ID")
private Integer commonId;
#ManyToMany(mappedBy = "collectionB")
private List<A> collectionA;
getter, setter, hashcode, equals
Entity B_PK:
public class B_PK implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private Integer commonId;
getter, setter, hashcode, equals
Related
#Entity
#Table(name = "A")
public class A {
#Id
#Column(name = "id")
private Long id;
#Id
#Column(name = "name")
private String name;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
#Entity
#Table(name = "Test")
public class Test {
#Id
#Column(name = "test_id")
private Long testId;
}
Error Result is
" JPA trouble with OneToOne relationship: A Foreign key refering has the wrong number of column. should be 2 "
How to specific primary key for join Test table ?
Table A : column id
map with
Table B : column test_id
Since your table A has a composite key, you should separate the columns out into another key class and then join on the individual part of the key of the table.
For instance, create AKey
#Embeddable
public class AKey {
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
//getters and setters
}
Then replace the ids in class A
#Entity
#Table(name = "A")
public class A {
#EmbeddedId
private AKey key;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
Then you can do a join on Test.testId = A.key.id
I am using Hibernate/JPA, and have 3 tables:
Contact
pk contact_id
fk member_id
Employee
pk employee_id
fk member_id
Members
pk member_id
I have:
#Entity(name = "Contact" )
public class Contact implements Serializable {
#Id
#SequenceGenerator(allocationSize = 1, name = "contact_sequence", sequenceName = "contact_ids")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "contact_sequence")
private java.lang.Long contact_id;
#OneToOne(cascade = CascadeType.ALL, targetEntity = Members.class)
#JoinColumn(name = "member_id")
private Members member;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "member", referencedColumnName = "member")
private Employee employee;
and
#Entity(name = "Employee")
public class Employee implements Serializable {
#Id
#SequenceGenerator(allocationSize = 1, name = "employee_sequence", sequenceName = "employee_ids")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "employee_sequence")
private java.lang.Long employee_id;
and
#Entity(name = "Members")
#Table(name = "Members")
public class Members implements Serializable {
#Id
#SequenceGenerator(allocationSize = 1, name = "members_sequence", sequenceName = "member_ids")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "members_sequence")
private java.lang.Long member_id;
My problem is with the Contact entity private Employee employee;. When I start my Jboss server:
StartException in service jboss.persistenceunit.
Question
How do I annotate the private Employee employee; on the Contact entity?
Thank you
You are almost properly defining the private Members member;, so is there a reason for not doing the same for private Employee employee; ?
Like :
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "employee", referencedColumnName = "employee_id")
private Employee employee;
Also a simple link, just to cross-check the overall #JoinColumn functionality
===EDIT===
As it looks there is indeed a data structure issue as from the first place the relation is ManyToOne,
If you could try the below example :
//Employee
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
private Contact contact;
//Contact
#OneToMany(fetch = FetchType.LAZY, mappedBy = "employee_id")
private List<Employee> employees = new ArrayList<>();
Then by selecting the contact.getEmployees() should do the trick
See code below for my 2 entity classes - when I call the findAll() method from my OrigRepository class, it joins these two tables using both primary keys. I want the join to be between the primary key of the Orig table and the foreign key entry in the MsgResponse table ("OrigID") - any sugggestions?
Orig Entity
#Entity
#Table(name = "originator")
public class Orig {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "OrigID")
private int OrigID;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "OrigID")
private MsgResponse responseInfo;
}
MsgResponse Entity
#Entity
#Table(name = "message_response")
public class MsgResponse {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int responseId;
#Column(name = "OrigID")
private int OrigId;
#OneToOne(mappedBy="responseInfo")
private Orig OrigInfo;
}
I suggest you to see the jpa documentation
here.
Example 1 should be your case
Try to swap relation ownership, that is:
#Entity
#Table(name = "originator")
public class Orig {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "OrigID")
private int OrigID;
#OneToOne(mappedBy="origInfo")
private MsgResponse responseInfo;
}
#Entity
#Table(name = "message_response")
public class MsgResponse {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int responseId;
// #Column(name = "OrigID")
// private int origId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "OrigID")
private Orig origInfo;
}
Note that the #JoinColum annotation is in now in the MsgResponse entity. This is because in a #OneToOne the join column refers to the source entity (see here).
Hope this could help.
Why Hibernate tries to insert new record with existing id?
Category table before save (was filled manually with insert)
ID | NAME | IMAGE
1 | 'NAME'| 'IMAGE'
Save code
//Category{id=0, name='NAME', image='IMAGE', parent=null}
getCurrentSession().save(category);
Should be inserted with id 2.
Category Java code
#Entity
#Table(name = "CATEGORY",
indexes = {
#Index(name = "CATEGORY_NAME_INDEX",
columnList = "CATEGORY_NAME")})
public class Category implements NamedModel {
#Id
#Column(name = "CATEGORY_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull
#Size(min = 1, max = 256)
#Column(name = "CATEGORY_NAME", length = 256)
private String name;
#OneToOne(fetch = FetchType.EAGER)
#JoinTable(name = "CATEGORY_RELATIONS",
joinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_CATEGORY_ID",
referencedColumnName = "CATEGORY_ID")},
inverseJoinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_PARENT_ID",
referencedColumnName = "CATEGORY_ID")})
private Category parent;
#OneToMany(cascade = {CascadeType.REMOVE, CascadeType.PERSIST},
fetch = FetchType.LAZY, mappedBy = "parent")
private List<Category> children;
}
SOLUTION
//Category
#GeneratedValue(strategy = GenerationType.IDENTITY)
//CategoryRelations
#Entity
#IdClass(CategoryRelationsPrimaryKey.class)
public static class CategoryRelationsPrimaryKey implements Serializable {
private Long categoryId;
private Long parentId;
instead of long.
This is because you had deleted/added a record from database directly instead from the application i.e. ORM, that's why values in hibernate_sequence is no longer maintained.
Hibernate maintains values in a hibernate_sequence table which would be inserted on creating a new record.
update next_val column value in hibernate_sequence to resolve the problem.
You can use Annotation #GeneratedValue(strategy = GenerationType.IDENTITY) to delegate primary key generation to database.
How fix next error?
ERROR SchemaExport:484 - HHH000389: Unsuccessful: alter table CATEGORY_RELATIONS add constraint FK2bn4xlg661b5xbx2qnwi1aqv0 foreign key (CATEGORY_RELATIONS_PARENT_ID) references CATEGORY
ERROR SchemaExport:485 - incompatible data types in combination in statement [alter table CATEGORY_RELATIONS add constraint FK2bn4xlg661b5xbx2qnwi1aqv0 foreign key (CATEGORY_RELATIONS_PARENT_ID) references CATEGORY]
hibernate.version 5.0.7.Final
hsqldb.version 2.3.3
Property, used for session factory
hibernate.dialect=org.hibernate.dialect.HSQLDialect
Category
#Entity
#Table(name = "CATEGORY",
indexes = {
#Index(name = "CATEGORY_NAME_INDEX",
columnList = "CATEGORY_NAME")})
public class Category extends JsonNamedModel {
#Id
#Column(name = "CATEGORY_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "CATEGORY_NAME")
private String name;
#Column(name = "CATEGORY_IMAGE")
private String image;
#OneToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
#JoinTable(name = "CATEGORY_RELATIONS",
joinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_CATEGORY_ID", referencedColumnName = "CATEGORY_ID")},
inverseJoinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_PARENT_ID", referencedColumnName = "CATEGORY_ID")})
private Category parent;
#OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "parent")
private List<Category> children;//...
}
CategoryRelations
#Entity
#Table(name = "CATEGORY_RELATIONS")
#IdClass(CategoryRelations.CategoryRelationsPrimaryKey.class)
public class CategoryRelations implements Serializable {
#Id
#Column(name = "CATEGORY_RELATIONS_CATEGORY_ID")
private String categoryId;
#Id
#Column(name = "CATEGORY_RELATIONS_PARENT_ID")
private String parentId;
#Entity
#IdClass(CategoryRelationsPrimaryKey.class)
public static class CategoryRelationsPrimaryKey implements Serializable {
private long categoryId;
private long parentId;
}//...
}
I think it's complaining because your types don't match up. In CATEGORY_RELATIONS you have the key types as String but in CATEGORY you have the primary key as an int. While in actual practice you might only store integer data in both fields, the DB engine can't prove that. There's nothing stopping somebody from putting a non-integer in CATEGORY_RELATIONS.categoryId and making it so the FK could never be satisfied.
Try changing CATEGORY_RELATIONS.categoryId to an int.
And now that I look at it, your PK class shows them as longs. Try switching all of the types in your CategoryRelations object (and possibly the CATEGORY_RELATIONS table) to all be the same types.