i need to get the column names for foreign keys for all mapped hibernate entities. Does anyone know how to do that?
I tried by sessionFactory.getClassMetadata - i can see all the properties names and types for all entities there, but i cannot find information about foreign keys.
Does anyone have any idea? I may not use direct database query - i must extract it from hibernate metadata.
You can use Java Reflection:
// Loop through joined columns that has #JoinColumn annotation
for (Method method : testClass.getMethods())
{
if (method.isAnnotationPresent(JoinColumn.class))
{
// name parameter is foreign key
String foreignKey = method.getAnnotation(JoinColumn.class).name;
// if the referencedColumnName is explicitly defined
String foreignKey = method.getAnnotation(JoinColumn.class).referencedColumnName;
}
}
Related
Hibernate doesn't let me do a join table with a unique field "docket", no matter if I specify the "referencedColumnName = "docket"" (i thought the idea of this property was to tell Hibernate which field to use, in case it is not the primary key).
Database tables:
student
---------------
id (PK) | docket (UNIQUE)
inscription
---------------
course_id | docket
inscription's PK is (course_id, docket)
course
---------------
id (PK)
The above problems raise with the following configuration:
On Student Entity:
#ManyToMany
#JoinTable(
name="inscription",
joinColumns=#JoinColumn(referencedColumnName = "docket", name="docket"),
inverseJoinColumns=#JoinColumn(name="course_id", referencedColumnName = "id")
)
private List<Course> studentCourses;
On Course Entity:
#ManyToMany(mappedBy = "studentCourses")
private List<Student> students;
What causes the problem is that, when project is deployed, Hibernate executes the statement:
alter table public.inscription add constraint FKp625s5r1hmlggpgeq4x2nju91 foreign key (docket) references public.student
which is (of course) incorrect, as it is not specifying that docket is a unique field.
What it should be doing is:
alter table public.inscription add constraint FKp625s5r1hmlggpgeq4x2nju91 foreign key (docket) references public.student(docket)
but I don't know how can I tell it to do so.
Any help?
Thanks in advance.
Found the answer at the official documentation.
From JPA 2.0 documentation: http://download.oracle.com/otndocs/jcp/persistence-2.0-fr-oth-JSpec/:
11.1.21 JoinColumn Annotation
The JoinColumn annotation is used to specify a column for joining an entity association or element
collection.
...
The name annotation element defines the name of the foreign key column. The remaining annotation
elements (other than referencedColumnName) refer to this column and have the same semantics as
for the Column annotation.
If the referencedColumnName element is missing, the foreign key is assumed to refer to the primary
key of the referenced table.
Support for referenced columns that are not primary key columns of the referenced table is optional.
Applications that use such mappings will not be portable.
So, perhaps, what was going on was that Hibernate does not have this feature implemented, as it is not mandatory.
What I did to fix it was to modify the inscription table, replacing each field by the corresponding primary key.
(i thought the idea of this property was to tell Hibernate which field to use, in case it is not the primary key)
Your assumption is in contradiction with the JPA 2.0 specification provided you are using Hibernate as an implementation of the JPA because the following extract states that you have to join on primary keys. It doesn't say anything about unique fields:
2.10.4 Bidirectional ManyToMany Relationships
Assuming that:
Entity A references a collection of Entity B.
Entity B references a collection of Entity A.
Entity A is the owner of the relationship.
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
There is a join table that is named A_B (owner name first). This join table has two foreign key columns. One foreign key column refers to table A and has the same type as the primary key of table A. The name of this foreign key column is formed as the concatenation of the following:
the name of the relationship property or field of entity B; "_"; the name of the primary key column in table A.
The other foreign key column refers to table B and has the same type as the primary key of table B. The name of this foreign key column is formed as the concatenation of the following: the name of the relationship property or field of entity A; "_"; the name of the primary key column in table B.
(I added the format; the text is the original quotation from the specification.)
docket is not primary key in your case and therefore you cannot join on it.
i need to get the column names for foreign keys for all mapped hibernate entities. Does anyone know how to do that?
I tried by sessionFactory.getClassMetadata - i can see all the properties names and types for all entities there, but i cannot find information about foreign keys.
Does anyone have any idea? I may not use direct database query - i must extract it from hibernate metadata.
You can use Java Reflection:
// Loop through joined columns that has #JoinColumn annotation
for (Method method : testClass.getMethods())
{
if (method.isAnnotationPresent(JoinColumn.class))
{
// name parameter is foreign key
String foreignKey = method.getAnnotation(JoinColumn.class).name;
// if the referencedColumnName is explicitly defined
String foreignKey = method.getAnnotation(JoinColumn.class).referencedColumnName;
}
}
I have two classes: TranscriptionService and TranscriptionConfig.
TranscriptionConfig has a serviceName variable, that is a string, with setters and getters.
TranscriptionService has no variables or references in the class to TranscriptionConfig.
There are two database tables: transcriptionConfig and transcriptionServices. transcriptionConfig has a foreign key between its field serviceName, and name inside transcriptionServices.
An admin should be able to set the string value of serviceName inside transcriptionConfig. This then references the equivalent string inside name in transcriptionServices. The transcriptionServices entries in the database are pre defined manually, so they never need to be set using an object.
My current hibernate code inside transcriptionConfig for the serviceName is as follows:
#ManyToOne
#JoinColumn(name = "serviceName", nullable = false)
private String transcriptionService;
However, it will not allow me to do this, as a String is not an entity. I have tried adding target-entity to no avail.
It seems to have a manyToOne relationship, it would need to have an instance of the TranscriptionService class, but I do not want transcriptionConfig to contain this object. It just needs a reference with the names.
How I can use this ManyToOne relationship, but just pass around the string for the name?
If you treat this column as a string value, you don't need to define mapping #ManyToOne and use #JoinColumn. Just mark it by#Column.
You'll need probably to catch SQLException in your DAO to handle foreign key constraint.
Edit:
You get this exception, because you don't have this key in foreign table. First you need to create row in TranscriptionService table.
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
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.