I have a bit of a complex database structure which I want to explain at first. We have the following tables:
Mandant (Id, Name)
Leistungsbereich (Id, Label)
Benutzer (Id, LoginName)
Here we have two association tables:
Mandant_Leistungsbereich (Id, MandantId, LeistungsbereichId)
Benutzer_Mandant_Leistungsbereich (Id, BenutzerId, MandantLeistungsbereichId)
The primary keys are always the 'Id' columns and the foreign keys are obvious I think.
For the first three tables I have the corresponding Domain Java classes that are working with Hibernate.
To get all Leistungsbereich items for the Mandant I have the following code in my Mandant domain class:
#JoinTable(
name = "Mandant_Leistungsbereich",
joinColumns = {
#JoinColumn(
name = "MandantId"
)
},
inverseJoinColumns = {
#JoinColumn(
name = "LeistungsbereichId"
)
}
)
#ManyToMany( fetch = FetchType.LAZY )
private Set<Leistungsbereich> leistungsbereiche;
When I load a Mandant automatically all Leitungsbereich items are loaded in the Java Set. Hibernate knows that it has to search in the Mandant_Leistungsbereich association table, so this works fine so far.
Now comes the strange part. I also want to achieve the same thing for the Benutzer because the Benutzer should have a list (or better to say a set) of Mandant_Leistungsbereich items.
So I do the same in the Benutzer class:
#JoinTable(
name = "Benutzer_Mandant_Leistungsbereich",
joinColumns = {#JoinColumn( name = "BenutzerId" ) },
inverseJoinColumns = {#JoinColumn( name = "MandantLeistungsbereichId" ) }
)
#ManyToMany( fetch = FetchType.LAZY )
private Set<MandantLeistungsbereich> mandantLeistungsbereiche;
But then loading a user the following error gets printed out:
org.hibernate.MappingException: Foreign key (FK_71hckf2bhqrp78gom71l6eq38:Benutzer_Mandant_Leistungsbereich [MandantLeistungsbereichId])) must have same number of columns as the referenced primary key (Mandant_Leistungsbereich [MandantId,LeistungsbereichId])
I really dont know why hibernate thinks that the Benutzer_Mandant_Leistungsbereich table needs two foreign keys for the Mandant_Leistungsbereich table. Both in the database and in the hibernate mapping is ALWAYS ONLY an Id column as primary key so there is no composite key. Why does hibernate tells me that I need two columns for the foreign key?
Related
I have two entities: WorkoutTemplate and ConcreteExercise.
In WorkoutTemplate I have this relationship with ConcreteExercises
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
And in ConcreteExercise I have this relationship with WorkoutTemplate
#ManyToOne
private WorkoutTemplate belongingWorkout;
I would like to insert a WorkoutTemplate into my database...
I make the request to the controller sending a Json like this:
{ "workoutName" : "My Workout",
concreteExercises: [
{
"name" : "Squat"
}
]
}
The DAO insert into my DB the WorkoutTemplate
And insert also in the table of the ConcreteExercise the name.
But not the reference to the WorkoutTemplate...
Practically, the table ConcreteExercise is made of:
id, name, belongin_workout_id
With the request above, we populate the id (auto-increment) and the name, but not the foreign key to the WorkoutTemplate.
How can I solve this ?
I would like to automatically insert the foreign key without sending it in the request or doing it manually into the service
Hi there it's because you are not using #JoinColumn which marks a column for as a join column for an entity association or an element collection.
On your WorkoutTemplate entity - you can retain this:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
But on your ConcreteExercise entity - you need to have this change:
#ManyToOne
#JoinColumn(name = "workout_template_id", nullable = false)
private WorkoutTemplate belongingWorkout;
The above code will create a foreign key linking the ConcreteExercise entity with the primary key from the WorkoutTemplate entity. The name of the foreign key column in the ConcreteExercise entity is specified by name property which for this case is workout_template_id - feel free to change this.
If you have set this up but still not working, it will also be helpful to share a code snippet on how are you saving these.
Okay, so within the database we have a table called distributionCompanies, created like so:
CREATE TABLE `distributionCompanies` (
`distributionCompanyID` INT(11) NOT NULL,
`distributionCompanyName` VARCHAR(255) NOT NULL,
PRIMARY KEY (distributionCompanyID)
);
I'm trying to map this table to a class using Hibernate:
#Entity
#Table(name = "distributionCompanies")
public class DistributionCompany implements DatabaseObject {
#Id
#GeneratedValue
#Column(name = "distributionCompanyID", length = 11, unique = true, nullable = false)
private int distributionCompanyID;
....
However, when running, I hit this issue:
Initial SessionFactory creation failedorg.hibernate.HibernateException: Missing column: distributionCompanyID_distributionCompanyID in database2.distributionCompanies
This isn't the only table in the database, and I've managed to map other classes successfully using the same method, so I'm a little stumped as to why this is causing an issue.
Thank you for your time,
Samuel Smith
EDIT: In response to Xavi's comment, I temporarily removed another mapping for the column, and the error went away, so the bad-egg probably lays in the following code:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinTable(name = "distributionCompanies", joinColumns = { #JoinColumn(name = "distributionCompanyID", nullable = false) })
private int distributionCompanyID;
Hibernate is looking for a column named distributionCompanyID_distributionCompanyID in your distributionCompanies table.
This is probably due to a ToOne association mapping towards this table without #JoinColum.
From Hibernate Documentation:
The #JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id.
If you've got a #ManyToOne or #OneToOne association mapping in another entity, this would explain why Hibernate is looking for such a column.
EDIT Seeing the association mapping you posted, it looks like it should be:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinColumn(name = "distributionCompanyID")
private DistributionCompany distributionCompany;
The #JoinTable annotation is used to specify a join table (that means an intermediate table used to model many-to-many associations). And the point of mapping an association would be to dispose of the mapped object instance (in this case a DistributionCompany, not just a distributionCompanyId).
I have a problem when trying to delete a table with #OneToMany relationship to table created
from the same java class using
Query q = getEntityManager().createQuery("DELETE FROM " + entityClass.getSimpleName());
q.executeUpdate();
Here's the table:
CREATE TABLE p_data_group
(
data_group_id bigint NOT NULL,
description character varying(350),
description_eng character varying(350),
multiplicity_max integer,
multiplicity_min integer,
name character varying(35),
name_db character varying(30),
name_eng character varying(35),
name_xml character varying(35),
path character varying(1024),
"position" integer,
is_root boolean,
parent_group_id bigint,
CONSTRAINT p_data_group_pkey PRIMARY KEY (data_group_id ),
CONSTRAINT fk_data_group_parent_group_id FOREIGN KEY (parent_group_id)
REFERENCES p_data_group (data_group_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE p_data_group
OWNER TO postgres;
And here's the part of the class that represents relations:
#ManyToOne
#JoinColumn(name = "parent_group_id", insertable = false, updatable = false)
#XmlTransient
public DataGroup getParentDataGroup() {
return parentDataGroup;
}
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "parent_group_id")
#ForeignKey(name = "fk_data_group_parent_group_id")
#Index(name = "idx_data_group_parent_group_id")
#LazyCollection(LazyCollectionOption.FALSE)
#XmlElementWrapper
public List<DataGroup> getChildDataGroups() {
return childDataGroups;
}
Now when trying to delete to root table all others should be deleted as well according to CascadeStyle.ALL annotation :
PSQLException: ERROR: update or delete on table "cpdm01" violates foreign key constraint "fk_cpdm01_kodpkd_cpdm01_id" on table
"cpdm01_kodpkd" Detail: Key (cpdm01_id)=(100) is still referenced
from table "cpdm01_kodpkd".
Am I right here?
As per your Error log shows
DETAIL: Key (cpdm01_id)=(100) is still referenced from table "cpdm01_kodpkd".
There is still a record referencing to cpdm01_id 100. You might delete record in cpdm01_kodpkd with cpdm01_id = 100 before , but there might be others as well.
You have to delete all records in cpdm01_kodpkd referencing to cpdm01_id 100 in main_order.
If not, the database protects you from doing harm to your data.
hi I'm getting this "javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: ERROR: duplicate key value violates unique constraint"
I have two tables A and B,
A has id, b_id, B has id,A_id's.
A - oneToMany , B - manyToOne relationship.
on A,
#OneToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "LATEST_VERSION_ID") #Valid
#EntityProperty(type = "GuidKey", relation = "B.id")
on B,
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "A_ID") #Valid
#EntityProperty(type = "Key", relation = "A.id")
when I create Page A I was able to Do so, But when I try to update I get unique constraint violation on table 'B'.
It says the record already exist.
You have to make a bi-directional relation using the mappedBy property.
See:
one to one bidirectional hibernate mapping
http://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example-annotation
http://www.codereye.com/2009/04/hibernate-bi-directional-one-to-one.html
Also, #EntityProperty isn't required for this. The foreign key should be in one table in one-to-one.
I have a sort of exotic mapping for a field:
#ElementCollection
#CollectionTable(name = "studentGradeLevel", joinColumns = #JoinColumn(name = "studentId"))
#MapKeyJoinColumn(name = "schoolYearId")
#Column(name = "gradeLevel", nullable = false)
#ForeignKey(name = "fkStudentGrade2Student")
private Map<SchoolYear, GradeLevel> gradeLevels;
SchoolYear is an entity and GradeLevel is an enum.
I am using Hibernate tools to generate the DDL for the schema. The schema that this generates is below:
create table studentGradeLevel (
studentId numeric(19,0) not null,
gradeLevel int not null,
schoolYearId int not null,
primary key (studentId, schoolYearId)
);
alter table studentGradeLevel
add constraint FK1BCA4A883A97C498
foreign key (schoolYearId)
references schoolYear;
alter table studentGradeLevel
add constraint fkStudentGrade2Student
foreign key (studentId)
references student;
The problem is that I can't seem to change the constraint name for the foreign key between the collection table and the table for the entity used as the map key.
I've used #ForeignKey to specify constraint names for #OneToMany, #ManyToMany and other #ElementCollections with no problem. I've tried #ForiegnKey's "inverseName" attribute but it seems to be ignored. #MapKeyJoinColumn doesn't appear to have any properties that would affect this.
Does anyone know if there is a way to do this?
I had to patch Hibernate to create different foreign key names, because the ones Hibernate created for me weren't really useful.
I took the Hibernate source, and placed the Source of the class org.hibernate.mapping.Table into my source folder, which is a the start of the classpath (the resulting jar in my project starts with a letter lower than the hibernate.jar, so this even works in webapps).
The I replaced the function uniqueColumnString with the following code (Original code at the top of the function):
public String uniqueColumnString(Iterator iterator, String referencedEntityName) {
// int result = 0;
// if ( referencedEntityName != null ) {
// result += referencedEntityName.hashCode();
// }
// while ( iterator.hasNext() ) {
// result += iterator.next().hashCode();
// }
// return ( Integer.toHexString( name.hashCode() ) + Integer.toHexString( result ) ).toUpperCase();
StringBuilder retVal = new StringBuilder();
retVal.append("_").append(referencedEntityName);
while( iterator.hasNext() ) {
Column c = (Column)iterator.next();
retVal.append("_");
retVal.append(c.getName());
}
return retVal.toString();
}
This returns automatically nice strings like "_Entity_attributeName_id", which will be used to create foreign keys like "fk_Entity_attributeName_id"! Never have to specify my names by hand again :)))
I had the same problem and because I couldn't find a way to do it ended up querying the database itself to get that information.
Running this query on SQL SERVER
select kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, tc.CONSTRAINT_TYPE, kcu.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
join INFORMATION_SCHEMA.KEY_COLUMN_USAGE as kcu
on kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
and kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
and kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA
and kcu.TABLE_NAME = tc.TABLE_NAME
You will get tablename, constraint name (like FK1BCA4A883A97C498), type (like UNIQUE constraint) and column name.
That should be enought to return a meaningful error message.
I know is not great because you loose the db portability but apparently there is no way to do what you are asking for at the moment...