#OneToOne()
#JoinColumn(name="vehicle_id", referencedColumnName="vehicleId")
public Vehicle getVehicle() {
return vehicle;
}
My UserDetails class has a one-to-one mapping with the Entitity class Vehicle. Hibernate creates the 2 tables and assigns a generic Foreign Key, which maps the vehicle_id column (UserDetails table.) to the primary key vehicleId (Vehicle table).
KEY FKB7C889CEAF42C7A1 (vehicle_id),
CONSTRAINT FKB7C889CEAF42C7A1 FOREIGN KEY (vehicle_id) REFERENCES vehicle (vehicleId)
My question is : how do we change this generated foreign key, into something meaningful, like Fk_userdetails_vehicle for example.
Since JPA 2.1, you can use the #javax.persistence.ForeignKey annotation:
#OneToOne()
#JoinColumn(name="vehicle_id", referencedColumnName="vehicleId", foreignKey=#ForeignKey(name = "Fk_userdetails_vehicle"))
public Vehicle getVehicle() {
return vehicle;
}
Prior to JPA 2.1, you could use Hibernate’s #org.hibernate.annotations.ForeignKey annotation, but this is now deprecated:
#OneToOne()
#JoinColumn(name="vehicle_id", referencedColumnName="vehicleId")
#ForeignKey(name="Fk_userdetails_vehicle")
public Vehicle getVehicle() {
return vehicle;
}
Also you can use #ForeignKey embedded in #JoinColumn like this:
#JoinColumn(name = "BAR_ID", foreignKey = #ForeignKey(name = FK_BAR_OF_FOO))
for #ManyToMany relations you can use foreignKey and inverseForeignKey embedded in #JoinTable like this:
#JoinTable(name = "ARC_EMPLOYEE_OF_BAR"
, joinColumns = {#JoinColumn(name = "BAR_ID")}
, inverseJoinColumns = {#JoinColumn(name = "EMPLOYEE_ID")}
, uniqueConstraints = {#UniqueConstraint(name = "ARC_UK_EMPLOYEE_OF_BAR", columnNames = {"EMPLOYEE_ID", "BAR_ID"})}
, foreignKey = #ForeignKey(name = "ARC_FK_BAR_OF_EMPLOYEE")
, inverseForeignKey = #ForeignKey(name = "ARC_FK_EMPLOYEE_OF_BAR"))
You can do it also by implementing ImplicitNamingStrategy.determineForeignKeyName and using
configuration.setImplicitNamingStrategy(
new MyImplicitNamingStrategy())
which is nice as you don't have to do it manually again and again. However, it may be hard to put the relevant information there. I tried to concat everything I got (using three underscore to separate the parts) and ended up with
FK_ACCESS_TEMPLATE____TEMPLATE____TEMPLATE_ID____TEMPLATE_ID__INDEX_B
which isn't really better than
FKG2JM5OO91HT64EWUACF7TJCFN_INDEX_B
I guess, using just the referenced table and column names together with a number for uniqueness would be just fine.
Note also that this seems to be legacy Hibernate stuff, unsupported by JPA.
OTOH it works with Hibernate 5.0.1 (just one week old).
May be you should try this, adding #ForeignKey annotation :
#ManyToOne
#ForeignKey(name="FK_some_model")
#JoinColumn(name="some_model_id")
private SomeModel someModel
Related
The Problem
I have a 1:n relation, but the n side shouldnt rely on constraints. So i actually wanna insert a EntityPojo via its future id, when its not saved yet ( Lets ignore that its a bad practice ). This looks kinda like this.
var relation = new RelationshipPojo();
.
.
.
relation.targets.add(session.getReference(futureID, EntityPojo.class));
session.save(relation);
// A few frames later
session.save(theEntityPojoWithTheSpecificId);
Cascading is not possible here, i only have its future ID, not a reference to the object i wanna save. Only its id it will have in the future.
#Entity
#Table(name = "relationship")
#Access(AccessType.FIELD)
public class RelationshipPojo {
.
.
.
#ManyToMany(cascade = {}, fetch = FetchType.EAGER)
public Set<EntityPojo> targets = new LinkedHashSet<>();
}
Question
How do we tell hibernate that it should ignore the constraints for this 1:n "target" relation ? It should just insert the given ID into the database, ignoring if that EntityPojo really exists yet.
Glad for any help on this topic, thanks !
For a much simpler solution, see the EDIT below
If the goal is to insert rows into the join table, without affecting the ENTITY_POJO table, you could model the many-to-many association as an entity itself:
#Entity
#Table(name = "relationship")
#Access(AccessType.FIELD)
public class RelationshipPojo {
#OneToMany(cascade = PERSIST, fetch = EAGER, mappedBy = "relationship")
public Set<RelationShipEntityPojo> targets = new LinkedHashSet<>();
}
#Entity
public class RelationShipEntityPojo {
#Column(name = "entity_id")
private Long entityId;
#ManyToOne
private RelationshipPojo relationship;
#ManyToOne
#NotFound(action = IGNORE)
#JoinColumn(insertable = false, updatable = false)
private EntityPojo entity;
}
This way, you'll be able to set a value to the entityId property to a non-existent id, and if an EntityPojo by that id is later inserted, Hibernate will know how to populate relationship properly. The caveat is a more complicated domain model, and the fact that you will need to control the association between RelationshipEntityPojo and EntityPojo using the entityId property, not entity.
EDIT Actually, disregard the above answer, it's overly complicated. Turing85 is right in that you should simply remove the constraint. You can prevent Hibernate from generating it in the first place using:
#ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#JoinTable(inverseJoinColumns = #JoinColumn(name = "target_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)))
public Set<EntityPojo> targets = new LinkedHashSet<>();
The only caveat is that when you try to load RelationshipPojo.targets before inserting the missing EntityPojo, Hibernate will complain about the missing entity, as apparently #NotFound is ignored for #ManyToMany.
Please consider the following mySQL tables:
CREATE TABLE storeman.user (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(50) NOT NULL,
display_name VARCHAR(50),
password CHAR(32),
...
PRIMARY KEY (id),
UNIQUE INDEX (email)
);
CREATE TABLE storeman.user_preferences (
id INT NOT NULL AUTO_INCREMENT,
notify_login BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (id),
CONSTRAINT fk_user_preferences FOREIGN KEY (id) REFERENCES user (id) ON DELETE CASCADE
);
As you may see there's a one-to-one relationship between the two tables.
When running the Hibernate Code Generator, I get the follwoing (relevant part only)
#Entity
#Table(name = "user", catalog = "storeman", uniqueConstraints = #UniqueConstraint(columnNames = "email"))
public class User implements java.io.Serializable {
...
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
public UserPreferences getUserPreferences(){
return this.userPreferences;
};
...
}
The issue with that is when I save the User entity, the linked UserPreferences is not saved automartically. Solving this is easy:
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
#Cascade({CascadeType.ALL})
public UserPreferences getUserPreferences(){
return this.userPreferences;
};
However, If for any reason I will have to re run the Hibernate Code Generator again, the #Cascade({CascadeType.ALL}) will be gone, and this is dangerous as I'm relying on the fact that linkled tables are automatically saved in my code.
So the question is: is there a way to modify mySQL script on top so that, while running hibernate reverse engineering code generation, the #Cascade annotation is automatically added?
mySql physical table doesn't say anything about cascading other then the foreign key. Only it can add ON DELETE CASCADE ON DELETE UPDATE.
Then you run Hibernate Code Generator, CascadeType and #Cascade definitions are not translated into DDL
Try to use jpa annotations much as possible.
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user",cascade= CascadeType.ALL)
public UserPreferences getUserPreferences(){
return this.userPreferences;
};
This is more of a general 'understanding' question rather than a specific senario question.
I have been lookiing at the ways in which JPA maps tables together and found two examples here that seem to work in different ways.
One has a Set of Phone objects using #JoinTable to join STUDENT_PHONE to STUDENT by STUDENT_ID
The other has a Set of StockDailyRecord but seems to just use mappedby stock and in the stock_detail table object have the #PrimaryKeyJoinColumn annotation.
Simply trying to get an understanding of which method would be the prefered way and why?
Method 1:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "STUDENT_PHONE", joinColumns = { #JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { #JoinColumn(name = "PHONE_ID") })
public Set<Phone> getStudentPhoneNumbers() {
return this.studentPhoneNumbers;
}
Method 2:
#Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
#UniqueConstraint(columnNames = "STOCK_NAME"),
#UniqueConstraint(columnNames = "STOCK_CODE") })
#OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
#Table(name = "stock_detail", catalog = "mkyongdb")
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
public Stock getStock() {
return this.stock;
}
Method #2:
It uses an extra column to build the OneToMany relation. This column is a Foreign key column of the other table. Before building the relation if these data needs to be added to the database then this foreign key column needs to be defined as nullable. This breaks the efficiency and cannot provide a normalized schema.
Method #1:
It uses a third table and is the efficient way to store data in a relational database and provides a normalized schema. So where possible its better to use this approach, if the data needs to be existed before building the relation.
I'm working on migrating some code that has two entities (Progress and PerformanceRating) that are related by a many-to-many relationship. Each PerformanceRating has multiple Progress and each Progress type can be assigned to several PerformanceRatings. Additionally, each PerformanceRating->Progress has an additional "amount" value that relates to the progress.
Current the PerformanceRating object contains a Map representing the "amount" of progress for each Progress type assigned to the PerformanceRating object.
It is coded as follows:
#Entity
class PerformanceRating{
....
....
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "performance_rating_progress_reward", joinColumns = { #JoinColumn(name = "id", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "amount", nullable = false, updatable = false) })
public Map<Progress, Integer> getRewardAmountByProgressMap() {
return this.rewardAmountByProgressMap;
}
However, when I start JBoss (with hibernate 3.6/JTA/JPA), I get the following error:
Use of #OneToMany or #ManyToMany targeting an unmapped class: fwl.domain.model.PerformanceRating.rewardAmountByProgressMap[java.lang.Integer]
I found a similar thread (Persist a Map<Integer,Float> with JPA), but that one seems to deal with non-entity types. In a case such as mine, where I am looking for an Entity/value type mapping, what is the correct syntax?
Is there a way to do this in Hibernate/JPA2?
Thanks,
Eric
The error you get:
Use of #OneToMany or #ManyToMany targeting an unmapped class: fwl.domain.model.PerformanceRating.rewardAmountByProgressMap[java.lang.Integer]
relates to the fact that with annotations like #OneToMany and #ManyToMany you say that the declaring class (PerformanceRating) is in a many-to-many relation with your map's value, Integer which is silly.
The value of your map should be an entity, its key should be the id with which you can identify one of those entities that your map contains (actually the key have to be only unique, I think, it needn't be an actual id).
I really don't know how your table looks like, but if your PerformanceRating (lets call it just Rating for ease's sake) looks like this:
rating
============
id int(11) PK
and your Progress table like this:
progress
============
id int(11) PK
amount int(11)
with a table connecting these like this:
progress_has_rating
============
progress_id int(11) PK
rating_id int(11) PK
then you can map these in the following way:
#Entity #Table class Rating {
#Id long id;
#ManyToMany(targetEntity = Progress.class,
cascade = CascadeType.ALL,
fetch = FetchType.EAGER)
#JoinTable(name = "progress_has_rating",
joinColumns = {#JoinColumn(name = "rating_id",
referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "progress_id",
referencedColumnName = "id")})
Set<Progress> progresses;
}
#Entity class Progress {
#Id long id;
#Basic long amount;
}
(It's quite possible that I switched up the column names in the #JoinColumn annotations that would actually work; I always switch 'em up.)
(Edit: yes, I've switched them—fixed.)
If your amount property is in your join table, then you'll need to create an entity class for that also. I think it isn't possible to get around that.
If you really want to use maps, then Hibernate can manage that. See the Collection mapping section (section 7.2.2.2 particularly) on how to map maps. Still, values in your map would need to be entities of some kind.
I have mapped a bidirectional many-to-many exception between the entities Course and Trainee in the following manner:
Course
{
...
private Collection<Trainee> students;
...
#ManyToMany(targetEntity = lesson.domain.Trainee.class,
cascade = {CascadeType.All}, fetch = {FetchType.EAGER})
#Jointable(name="COURSE_TRAINEE",
joincolumns = #JoinColumn(name="COURSE_ID"),
inverseJoinColumns = #JoinColumn(name = "TRAINEE_ID"))
#CollectionOfElements
public Collection<Trainee> getStudents() {
return students;
}
...
}
Trainee
{
...
private Collection<Course> authCourses;
...
#ManyToMany(cascade = {CascadeType.All}, fetch = {FetchType.EAGER},
mappedBy = "students", targetEntity = lesson.domain.Course.class)
#CollectionOfElements
public Collection<Course> getAuthCourses() {
return authCourses;
}
...
}
Instead of creating a table where the Primary Key is made of the two foreign keys (imported from the table of the related two entities), the system generates the table "COURSE_TRAINEE" with the following schema:
I am working on MySQL 5.1 and my App. Server is JBoss 5.1.
Does anyone guess why?
In addition to Bence Olah: the primary key constraint for (COURSE_ID, TRAINEE_ID) pair is not created because your mapping doesn't say that these pairs are unique. You need to change Collections to Sets in your code in order to express this restriction, and primary key will be created.
Use either #CollectionOfElements OR #ManyToMany. Don't use both of them at the same time!
Be aware that #CollectionOfElements is Hibernate specific, while #ManyToMany is based on JPA standard.
Futher reading:
http://docs.jboss.org/ejb3/app-server/HibernateAnnotations/reference/en/html_single/index.html