My code is as below. I am using the spring boot with jpa and postgresql database
I need user friendly name as foreign key.
#Entity
#Table(name="course_table")
public class Course extends BaseAuditingEntity {
#ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
#JoinTable(name = "course_program_table", joinColumns = #JoinColumn(name = "course_id", referencedColumnName = "course_id", foreignKey = #ForeignKey(name = "fk_program_id")), inverseJoinColumns = #JoinColumn(name = "program_id", referencedColumnName = "program_id", foreignKey = #ForeignKey(name = "fk_course_id")))
private List programs;
}
I have given the name of foreign key using the #ForeignKey annotation but when I see db it is showing the randomly created foreignkey name.
CREATE TABLE course_program_table
(
course_id integer NOT NULL,
program_id integer NOT NULL,
CONSTRAINT fk_28c95hl4nqclyvyxuduei5nbf FOREIGN KEY (program_id)
REFERENCES public.program_table (program_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fk_5sainywquv8yyu24pjk3jptn7 FOREIGN KEY (course_id)
REFERENCES public.course_table (course_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I need foreign key as mentioned in the annotation like fk_program_id and fk_course_id.
Thanks in advance.
With a join table this is how you should specify it
#ManyToMany
#JoinTable(name = "course_program_table",
joinColumns = #JoinColumn(name = "course_id", ...)
foreignKey = #ForeignKey(name = "fk_program_id"),
inverseJoinColumns = #JoinColumn(name = "program_id", ...)
inverseForeignKey = #ForeignKey(name = "fk_course_id"))
private List programs;
This is how I do it with the JPA provider I use (not Hibernate), and that is why the #JoinTable has the "foreignKey"/"inverseForeignKey" attributes (the FKs are on/owned by the join table).
If that doesn't work then you need to be looking at raising a bug on your chosen JPA provider.
#ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
#JoinTable(name = "tten_courseservice_course_program_table", joinColumns = #JoinColumn(name = "course_id", referencedColumnName = "course_id"), inverseJoinColumns = #JoinColumn(name = "program_id", referencedColumnName = "program_id"))
#ForeignKey(name="fk_tten_courseservice_course_table_course_id",inverseName="fk_tten_courseservice_program_table_program_id")
private List<ProgramEntity> programs;``
I have tried this and now I am able to generate foreign key name properly.
Hope it will help other.
Related
I have list that is mapped by a join table. What I need to do is make the combinations of "layouts" and 'views' not unique and also each with an index. What I thought of trying to do is making a Map<Integer, View> and somehow make the join table have a third column 'id'. What would happen is get the views of that layout and populate them with the id from the join table as a key in the map.
Any idea how to do that, or maybe a better idea for what I need?
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "layout_view",
joinColumns = {#JoinColumn(name = "layout_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "view_id", nullable = false)}
)
private List<View> views;
What I imagine:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "layout_view",
joinColumns = {#JoinColumn(name = "layout_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "view_id", nullable = false)}
//some code for third column and populating it as keys
)
private Map<Integer, View> views;
You can already have multiple same views for a layout by using a List, as you're doing.
To preserve (and be able to change) the order, see OrderColumn and the Hibernate documentation about ordered lists​.
The same documentation also describes how to implement your original idea with a map, if you really want that.
I found here a good article about designing the many-to-many schema with JPA.
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "student_subject", joinColumns = #JoinColumn(name = "student_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "subject_id", referencedColumnName = "id"))
private Set<Subject> subjects;
My question is, can I map a start schema, for example on JoinColumn I want to add multiple tables. It is possible?
#Entity
#IdClass(MyPK.class)
public class SubEntity {
#Id private int id;
#Id private String name;
}
#Entity
public class ParentEntity {
#OneToOne(cascade = ALL)
#JoinColumn(name="fk_sub_id", foreignKey = #ForeignKey(name="fk_sub"))
private SubEntity sub;
}
Caused by: org.hibernate.AnnotationException: A Foreign key refering SubEntity from ParentEntity has the wrong number of column. should be 2
This fails due to the #JoinColumn annotation. But how do I have to write it for a composite PK? I have to use this annotation to set the foreignkey constraint name during hibernate auto generation.
For composite keys you should use #JoinColumns
#JoinColumns(value = {
#JoinColumn(name = "fk_sub_id", referencedColumnName = "id"),
#JoinColumn(name = "fk_name", referencedColumnName = "name")},
foreignKey = #ForeignKey(name = "fk_sub"))
Update:
Hibernate seems to not have followed the JPA 2.1 specification in this instance. You'll need to additionally include hibernates own annotation which is deprecated but still functions. This should be added like:
#JoinColumns(value = {
#JoinColumn(name = "fk_sub_id", referencedColumnName = "id"),
#JoinColumn(name = "fk_name", referencedColumnName = "name")},
foreignKey = #ForeignKey(name = "fk_sub"))
#org.hibernate.annotations.ForeignKey(name = "fk_sub")
Given 3 tables:
student (id)
student_to_class (student_id, class_id)
class (id)
I'd like to apply a where clause on the student_to_class where student_id = :studentId. I've found many examples that apply where clause on "class" table or "student" table, but not the many-to-many table.
The student table has a #ManyToMany
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "student_to_class",
joinColumns = { #JoinColumn(name = "student_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "class_id", nullable = false) }
)
private Set<ClassEntity> classes;
The class table has a #ManyToMany
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "student_to_class",
joinColumns = { #JoinColumn(name = "class_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "student_id", nullable = false) }
)
private Set<StudentEntity> students;
Here is the query I'm trying to translate into Criteria:
select * from student, student_to_class where student_to_class.student_id = 1 and student.id = student_to_class.class_id
I'm trying to figure out how to reference the many-to-many table since I don't have an actual class representing this table.
Criteria c = sessionFactory.getCurrentSession().createCriteria(ClassEntity.class);
c.createAlias("student_to_class", "entities"); // how to reference the student_to_class ?
c.add(Restrictions.eq("entities.user_id", studentEntity.getId()));
But I get an error, which makes sense to me, but I haven't had much luck fixing it:
could not resolve property: student_to_class
Since the studentid in the Student table will be the same as the studentid in the student_to_class table there is no need to filter by the join table. Simply run the where clause against student.student_id
I have a table: DocumentType:
#ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
#JoinTable(
name = "document_type_property_type",
joinColumns = #JoinColumn(name = "document_type"),
inverseJoinColumns = #JoinColumn(name = "property_type")
)
#Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
#ForeignKey(name = "FK_DOCUMENT_TYPE_PROPERTY_TYPE__DOCUMENT_TYPE", inverseName = "FK_DOCUMENT_TYPE_PROPERTY_TYPE__PROPERTY_TYPE")
#Sort(type = SortType.NATURAL)
private SortedSet<PropertyType> propertyTypes = new TreeSet<PropertyType>();
and PropertyType:
#ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
#JoinTable(
name = "document_type_property_type",
joinColumns = #JoinColumn(name = "property_type"),
inverseJoinColumns = #JoinColumn(name = "document_type")
)
#Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
#Sort(type = SortType.NATURAL)
protected SortedSet<DocumentType> documentTypes = new TreeSet<DocumentType>();
As you see the bridge table for ManyToMany is: document_type_property_type.
I do not understand why if i remove a property type from a doc type it not only deletes it from bridge table (as i want/expect) but also deletes it from property type itself (which i want to avoid!).
Can you give me a work-around?
Thanks.
Edit: code for deleting a property type - doc type relation:
public void removePropertyType(final PropertyType propertyType) {
super.performDAudit(propertyType);
final DocumentType currentInstance = getInstance();
currentInstance.getPropertyTypes().remove(propertyType);
getEntityManager().persist(propertyType);
FacesMessages.instance().add(StatusMessage.Severity.INFO, "Property Type was succesfully removed from this document type");
}
I notice that you have the cascade type set to DELETE_ORPHAN on both sides of the relationship. I think you may either have to set it on one side or none. I am not sure that DELETE_ORPHAN is relevant in your scenario.
As I understand it, only one side of the relationship actually "owns" the relationship. That is the side that should manage all cascades and so on and the inverse side should do nothing.