How to set the column name of the foreign key when setting up a one-to-many relationship in JPA?
I would like to change the name of "items_id" to "item_id"
#OneToMany
private List<Item> items;
I tried the following annotations with no success:
#JoinColumn(name="item_id") // join table is not created
#Column(name="item_id") // no effect
You want to override the mappings of the default values of the join table, so the #JoinTable annotation is the one to use. You want to override the name of the inverseJoinColumn from items_id to item_id:
#OneToMany
#JoinTable(inverseJoinColumns=#JoinColumn(name="item_id"))
List<Item> items;
#OneToMany and #JoinColumn makes a different mapping, a join table is not created and a foreign key is created in the referenced entity table(in the Item table in your case).
#Column is used to override the names for entity attributes, not for relationships.
Related
I'm puzzled with JPA behaviour. Underlying DB is H2, I'm using SpringBoot and
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.H2Dialect
Here is my entity:
#Entity
class ChildEntity{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Long = _
#OneToMany(fetch = FetchType.EAGER)
var parentEntities: java.util.Set[ParentEntity] = _
}
Here is what's created in DB:
CREATE INDEX "PUBLIC"."INDEX_C" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("CHILD_ENTITY_ID");
CREATE PRIMARY KEY "PUBLIC"."PRIMARY_KEY_CA" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("CHILD_ENTITY_ID", "PARENT_ENTITIES_ID");
-- why?
CREATE UNIQUE INDEX "PUBLIC"."UK_INDEX_C" ON "PUBLIC"."CHILD_ENTITY_PARENT_ENTITIES"("PARENT_ENTITIES_ID");
Why does it create unique index for PARENT_ENTITIES_ID on join table?
Jpa create join table automatically.
You can avoid extra join table issue using #JoinColumn .
The #JoinColumn annotation helps Hibernate to figure out that there is a Foreign Key column in parent.
This is to be expected, a one to many relationship connects two different entities together. JPA by default implements this when you don't do it explicitly. That is why you have a join table with the id for both entities.
I annotated my fields with only #OneToOne and when I check the database (generated using liquibase) saw that there are unique constraints on database columns.
Does this mean #OneToOne implies uniqueness just by itself, eg. one Building can only be in one city, and no other Buildings can be in the same city?
What do I do when I want to tell that there may be other other buildings in the same city?
add #JoinColumn(unique = false),
only use #JoinColumn(unique = false) without #oneToOne,
or use #ManyToOne?
or leave it without any annotations?
I don't want to put a Buildings field in the city class, because I wouldn't ever call city.getBuildings();. Does any of the below require a bidirectional reference?
class Building {
#OneToOne(optional = false)
City city;
}
class Building {
#OneToOne(optional = false)
#JoinColumn(unique = false)
City city;
}
class Building {
#JoinColumn(unique = true)
City city;
}
class Building {
#ManyToOne
City city;
}
The JPA specification says that for a bidirectional OneToOne relationship (2.10.1 Bidirectional OneToOne Relationships):
Assuming that:
Entity A references a single instance of Entity B.
Entity B references a single instance of Entity A.
Entity A is specified as 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.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
In case of unidirectional OneToOne relationship (2.10.3.1 Unidirectional OneToOne Relationships):
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
If you have a City-Building relationship, then for any reasonable city it would be a OneToMany/ManyToOne relationship, since a given city can have multiple buildings, but a given building can be only in one city.
I have the following Schema:
User Table: (Primary Key)
UserId
CustomerId
Role Table: (Primary Key)
UserId
CustomerId
UserRole Table:
UserRoleId (UNIQUEIDENTIFIER (newsequentialid)) Primary Key
UserId
Customerid
RoleId
Those tables participate in many to many relationship (UserRole). I am using Entity Framework code first with mapping classes to define the database tables. So, In my mapping class for User Table, I have the following:
this.HasMany(u => u.Roles)
.WithMany()
.Map(m =>
{
m.MapLeftKey(new string[] { "CustomerID", "UserID" });
m.MapRightKey(new string[] {"CustomerID", "RoleID"});
m.ToTable("UserRoles");
}
);
Entity framework is failing with this message:
"One or more validation errors were detected during model generation:
CustomerID: Name: Each property name in a type must be unique. Property name 'CustomerID' is already defined.
UserRole: EntityType: EntitySet 'UserRole' is based on type 'UserRole' that has no keys defined.
is it possible to tell Code First that the Primary Key for my "UserRole" is UserRoleId?
The issue is when Entity Framework tries to create the UserRole Table, it would use all columns of MapLeftKey and MapRightKey to Create UserRole with PrimaryKey that has all those columns.
Any suggestions?
You need to model your classes similar to your DB, why don't you simply add the association tables? I mocked up your DB and there is no problem as long as you model all the tables.
Test it for yourself, create an EF project .edmx using code first from existing DB, I think the answer will be obvious.
Maybe this is a question with an easy answer ... but I don't get it running. At persist() I get the exception that the referential key in the child table is null (which of course is not allowed by the database). I have a recipe and some steps for preparation.
I'm using EclipseLink 2.4.1
Recipe.java (rcpid is autoset by JPA)
#Entity
public class Recipe {
#Id
long rcpid;
List<Recipestep> recipesteps = new ArrayList<>();
#OneToMany(
cascade=CascadeType.ALL,
fetch=FetchType.EAGER,
mappedBy="recipe",
targetEntity=Recipestep.class )
// This does NOT work. Following line tries to access a join-table !!!
// #JoinColumn(name="rcpid", referencedColumnName="rcpid")
public List<Recipestep> getRecipesteps() { return recipesteps; }
// some more attributes, getters and setters
}
Recipestep.java (rpsid is autoset by JPA)
#Entity
public class Recipestep {
#Id
long rpsid;
Recipe recipe;
#ManyToOne( targetEntity=Recipe.class )
#JoinColumn( name="rcpid" )
public Recipe getRecipe() { return recipe; }
// some more attributes, getters and setters
}
The code above is a valid workaround. However to have clean (and supportable) code, the relationship should be only one-way with a collection in the parent which references all its children.
You have mapped this as a unidirectional one to many, but have two mappings for the recipestep rcpid database column. Try changing the long rcpid to
#ManyTOne
Recipe rcp;
And then remove the joincolumn definition from the oneToMany and make it bidirectional by marking it as mappedby the rcp manyToOne relation. An example is posted here http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Relationship_Mappings/Collection_Mappings/OneToMany
Eclipselink will always insert nulls on unidirectional oneToMany relations using a joincolumn when first inserting the target entity, and then update it later when it processes the Recipe entity. Your rcpid mapping in Recipestep is also likely null, which means you have two write able mappings for the same field which is bad especially when they conflict like this.
You are experiencing the default JPA behaviour. Adding an entity to the recipesteps list is not sufficient to create a bidirectional relation.
To solve the issue you need to set the rcpid explicitly on every element in the list.
EDIT: I think the issue is that JPA does not know where to store the id of the Recipe in the Recipestep table. It assumes a name ("recipebo_rcpid"), but your table seems to lack it.
Try adding the column "recipe_id" to the Recipestep table and a mappedBy attribute to the #OneToMany annotation:
#OneToMany(
cascade=CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "recipe" )
You probably do not need the targetEntity attribute in the annotation- the List is typed already.
I am learning JPA (2.0) and I would like to understand how to properly map multiple attributes of the same class type. For instance, pretend I have a model:
#Entity
class Person {
String name;
int age;
// getters/setters
}
#Entity
class Family {
Person dad;
Person mom;
List<Person> children;
// getters/setters
}
How can I properly map mom and dad attributes?
Thanks and sorry if it is too basic. Couldn't find an answer anywhere.
The fact that you have two instances doesn't change anything.
You map dad and mom each as a ManyToOne association, and there will be two join columns in the family table: one for dad and one for mom.
You map children as a OneToMany (assuming a child can only be a child in one family), and there will be either a join table between Family and Person (the default for a unidirectional OneToMany), or a join column in the Person table referencing the family table (the default for a OneToMany bidirectional association).