I have the following entities:
Project: id:int, name:String, workers:Set
Worker: id:int, name:String, projects:Set
Project_Worker: id:int,
project_id:int, worker_id:int
So, I have my own table to 'solve' the many to many relation. <-- ya I need it - can't use just many to many and let JPA create the table cause I need the extra entity.
Now I tried a lot. Just so that JPA wont create an own table and extra columns and so on, but it wont work. My code so far:
#Entity
#Table("Worker")
public class Worker implements Serializable {
...
#ManyToMany
#JoinTable(name = "Project_Worker",
joinColumns = {#JoinColumn(name = "Worker_id", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name = "project_id", referencedColumnName="id")})
private Set<Project> projects;
The Project entity looks the same. just with the changed join columns.
My table: Project_Worker columns have at the moment no annotations cause if I use for example:
#JoinTable(name="Project",
joinColumns = {#JoinColumn(name = "id")})
JPA creates in the worker and project table an extra column instead in the Project_Worker table..
So I just want to map the many-to-many relation over my own table with my own attributes.
I also tried the solution - which worked - that just the project_worker table will have OneToMany annotations, but so the project and worker entity won't be able to use the Set and i have to look every time in the project_worker table for the right row...
If you want to map it as a many-to-many Project<->Worker via your join table you will need to ditch the id column in the project_worker table and it should work as you expect.
If you can't do this then you will need ProjectWorker as an entity in code then the relationship should be a one-to-many from Worker and Project to ProjectWorker.
#Entity
#Table("Worker")
public class Worker implements Serializable {
...
#OneToMany(mappedBy = "worker")
private Set<ProjectWorker> projectWorker;
#Entity
#Table("Project")
public class Project implements Serializable {
...
#OneToMany(mappedBy = "project")
private Set<ProjectWorker> projectWorker;
#Entity
#Table("ProjectWorker")
public class ProjectWorker implements Serializable {
...
#ManyToOne
#JoinColumn(name = "worker_id")
private Worker worker;
#ManyToOne
#JoinColumn(name = "project_id")
private Project project;
Related
I have a project that uses Hibernate and is divided into multiple modules.
I have the following modules:
device (defines entity Device)
appstore (defines entity Application)
device-appstore-integration (adds many-to-many relationship between Device and Application).
Entities look like this:
#Entity
#Table(name = "devices")
public class Device extends AbstractAuditingEntity implements Serializable
{
#NotNull
#EmbeddedId
private DeviceIdentity identity;
// ...
}
#Entity
#Table(name = "apps")
public class App extends AbstractAuditingEntity implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// ...
}
Now I want device-appstore-integration module to add many-to-many relationship between the two entities above.
I thought about adding entity DeviceWithInstalledApps to define this many2many relationship and used the following code:
#Entity
#Table(name = "devices")
public class DeviceWithInstalledApps extends Device
{
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "devices_installed_apps",
joinColumns = {/*...*/},
inverseJoinColumns = {/*...*/}
)
private Set<App> installedApps = new HashSet<>();
// ...
}
The problem is that Hibernate requires devices table to include dtype column and treats DeviceWithInstalledApps as a separate entity that inherits from Device but I don't actually want it to be a separate entity. It's still the same entity, just with many-to-many relationship added so that I can actually access this relationship, no columns are added so there is no actual need to provide dtype column, it simply doesn't make sense in this context.
Is there any other way to define many-to-many relationship in JPA/Hibernate so that I can actually implement business logic without getting into issues mentioned above?
I have a small problem regarding hibernate mappings:
What I need is the following :
I have Class Car, and class Worker.
I have mapped them as #ManyToOne. Where #ManyToOne is on car side:
//this is my field in car class i use javafx properties
private ObjectProperty<Monter> monter= new SimpleObjectProperty<>();
#ManyToOne
#JoinColumn(name = "worker_id")
public Worker getWorker() {
return worker.get();
}
and in the worker side is following
#OneToMany(mappedBy ="worker",cascade=CascadeType.ALL)
public List<Car> getCar() {
return car;
}
And this works, I have no issues, however, I need to have 3 fields for worker, workerIn, workerOut,workerMain.
So how can I make more fields in my Car class, so I can store my workers in them, since when i try to create new ObjectProperty<Worker> i get error from hibernate that it cannot find out field type.
So, i am not sure, am i using the wrong mapping ? should i be using manyToMany,
or should i annotate every single field i create in Car class?
Basicly user would be prompted to choose, workerIn, workerOut,workerMain, from list of workers, and i could not achive this, and creating 3 lists for this would be insane.
I would create a join table as this looks like a many to many mapping scenario.
So the table would have columns (watch out for the worker_type discriminator):
worker_id
car_id
worker_type
In the Car class you would have:
#ManyToMany
#JoinTable(
name="car_worker",
joinColumns=#JoinColumn(name="CAR_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="WORKER_ID", referencedColumnName="ID"))
#WhereJoinTable(clause = "worker_type = 1")
private Set<Worker> workerIn;
#ManyToMany
#JoinTable(
name="car_worker",
joinColumns=#JoinColumn(name="CAR_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="WORKER_ID", referencedColumnName="ID"))
#WhereJoinTable(clause = "worker_type = 2")
private Set<Worker> workerOut;
#ManyToMany
#JoinTable(
name="car_worker",
joinColumns=#JoinColumn(name="CAR_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="WORKER_ID", referencedColumnName="ID"))
#WhereJoinTable(clause = "worker_type = 3")
private Set<Worker> workerMain;
The Worker class would have:
#ManyToMany
#JoinTable(
name="car_worker",
joinColumns=#JoinColumn(name="WORKER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="CAR_ID", referencedColumnName="ID"))
private Set<Car> cars;
I'm starting my first project with Hibernate 4.2.21 and first with JPA 2.0, I want to create a relationship OneToMany Unidirectional. I saw a lot examples in version of Hibernate 3 but not much in 4.2.21 This example works perfectly but I don't know if is a good practice, I want to know the Opinion from another members about that?
Relationship One To Many:
-Parent Template:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "template_id")
private Set<Variable> variables = new LinkedHashSet<Variable>();
-Child: Variable
#Column(name = "template_id", nullable = false)
Integer templateId;
According with this another post's.
Hibernate unidirectional one to many association - why is a join table better?
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-association-collections
A unidirectional one to many using a foreign key column in the owned entity is not that common and not really recommended. We strongly advise you to use a join table for this kind of association (as explained in the next section). This kind of association is described through a #JoinColumn
#Entity
public class Customer implements Serializable {
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="CUST_ID")
public Set<Ticket> getTickets() {
...
}
#Entity
public class Ticket implements Serializable {
... //no bidir
}
Unidirectional with join table
A unidirectional one to many with join table is much preferred. This association is described through an #JoinTable.
#Entity
public class Trainer {
#OneToMany
#JoinTable(
name="TrainedMonkeys",
joinColumns = #JoinColumn( name="trainer_id"),
inverseJoinColumns = #JoinColumn( name="monkey_id")
)
public Set<Monkey> getTrainedMonkeys() {
...
}
#Entity
public class Monkey {
... //no bidir
}
Finally the only way it's implement the bidirectional method... yes or no?
I tried to create an unidirectional OneToMany relation with a JoinTable in Play Framework 2.1. However, the framework is not generating the 'JoinTable': "transformation_input_files". The strange part is that if I change the relation to ManyToMany the table is generated. Here is the code:
So its about an Transformation class containing multiple S3Files. Here is the Transformation file:
#Entity
#Table(name = "transformations")
public class Transformation extends Model {
#Id
public Long id;
/*...*/
#OneToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name="transformation_input_files",
joinColumns = #JoinColumn( name="transformation_id"),
inverseJoinColumns = #JoinColumn( name="input_file_id")
)
public List<S3File> inputFiles;
}
Here is the S3File:
#Entity
#Table(name="s3files")
public class S3File extends Model {
#Id
public Long id;
/*...*/
}
The S3Files are used in more models, so that can not be a bidirectional relation. If I change #OneToMany in #ManyToMany it does generate the join table, however, I do like to stick with the #OneToMany relation.
How do I solve this problem? Did I missed something?
In one-to-many relationship you have to store the relation in "Many" entity..if you don't want to store the value in the S3File class you have to create another class to join the two classes.
Example:
#Entity
#Table(name="transformations_ s3files")
public class Relation extends Model {
#Id
public Long id;
#ManyToOne
public Transformation transformation;
#OneToOne
public S3File file;
}
In which case do you use the JPA #JoinTable annotation?
EDIT 2017-04-29: As pointed to by some of the commenters, the JoinTable example does not need the mappedBy annotation attribute. In fact, recent versions of Hibernate refuse to start up by printing the following error:
org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like #JoinTable or #JoinColumn
Let's pretend that you have an entity named Project and another entity named Task and each project can have many tasks.
You can design the database schema for this scenario in two ways.
The first solution is to create a table named Project and another table named Task and add a foreign key column to the task table named project_id:
Project Task
------- ----
id id
name name
project_id
This way, it will be possible to determine the project for each row in the task table. If you use this approach, in your entity classes you won't need a join table:
#Entity
public class Project {
#OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
#Entity
public class Task {
#ManyToOne
private Project project;
}
The other solution is to use a third table, e.g. Project_Tasks, and store the relationship between projects and tasks in that table:
Project Task Project_Tasks
------- ---- -------------
id id project_id
name name task_id
The Project_Tasks table is called a "Join Table". To implement this second solution in JPA you need to use the #JoinTable annotation. For example, in order to implement a uni-directional one-to-many association, we can define our entities as such:
Project entity:
#Entity
public class Project {
#Id
#GeneratedValue
private Long pid;
private String name;
#JoinTable
#OneToMany
private List<Task> tasks;
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Task> getTasks() {
return tasks;
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
}
}
Task entity:
#Entity
public class Task {
#Id
#GeneratedValue
private Long tid;
private String name;
public Long getTid() {
return tid;
}
public void setTid(Long tid) {
this.tid = tid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This will create the following database structure:
The #JoinTable annotation also lets you customize various aspects of the join table. For example, had we annotated the tasks property like this:
#JoinTable(
name = "MY_JT",
joinColumns = #JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = #JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
#OneToMany
private List<Task> tasks;
The resulting database would have become:
Finally, if you want to create a schema for a many-to-many association, using a join table is the only available solution.
#ManyToMany associations
Most often, you will need to use #JoinTable annotation to specify the mapping of a many-to-many table relationship:
the name of the link table and
the two Foreign Key columns
So, assuming you have the following database tables:
In the Post entity, you would map this relationship, like this:
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(
name = "post_tag",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
The #JoinTable annotation is used to specify the table name via the name attribute, as well as the Foreign Key column that references the post table (e.g., joinColumns) and the Foreign Key column in the post_tag link table that references the Tag entity via the inverseJoinColumns attribute.
Notice that the cascade attribute of the #ManyToMany annotation is set to PERSIST and MERGE only because cascading REMOVE is a bad idea since we the DELETE statement will be issued for the other parent record, tag in our case, not to the post_tag record.
Unidirectional #OneToMany associations
The unidirectional #OneToMany associations, that lack a #JoinColumn mapping, behave like many-to-many table relationships, rather than one-to-many.
So, assuming you have the following entity mappings:
#Entity(name = "Post")
#Table(name = "post")
public class Post {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
}
#Entity(name = "PostComment")
#Table(name = "post_comment")
public class PostComment {
#Id
#GeneratedValue
private Long id;
private String review;
//Constructors, getters and setters removed for brevity
}
Hibernate will assume the following database schema for the above entity mapping:
As already explained, the unidirectional #OneToMany JPA mapping behaves like a many-to-many association.
To customize the link table, you can also use the #JoinTable annotation:
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JoinTable(
name = "post_comment_ref",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();
And now, the link table is going to be called post_comment_ref and the Foreign Key columns will be post_id, for the post table, and post_comment_id, for the post_comment table.
Unidirectional #OneToMany associations are not efficient, so you are better off using bidirectional #OneToMany associations or just the #ManyToOne side.
It's the only solution to map a ManyToMany association : you need a join table between the two entities tables to map the association.
It's also used for OneToMany (usually unidirectional) associations when you don't want to add a foreign key in the table of the many side and thus keep it independent of the one side.
Search for #JoinTable in the hibernate documentation for explanations and examples.
It's also cleaner to use #JoinTable when an Entity could be the child in several parent/child relationships with different types of parents. To follow up with Behrang's example, imagine a Task can be the child of Project, Person, Department, Study, and Process.
Should the task table have 5 nullable foreign key fields? I think not...
It lets you handle Many to Many relationship. Example:
Table 1: post
post has following columns
____________________
| ID | DATE |
|_________|_________|
| | |
|_________|_________|
Table 2: user
user has the following columns:
____________________
| ID |NAME |
|_________|_________|
| | |
|_________|_________|
Join Table lets you create a mapping using:
#JoinTable(
name="USER_POST",
joinColumns=#JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="POST_ID", referencedColumnName="ID"))
will create a table:
____________________
| USER_ID| POST_ID |
|_________|_________|
| | |
|_________|_________|