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;
Related
Here is my scenario: i have person entity which looks like below.
#Entity
public class Person{
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<PhoneNumber> phoneNumbers = new HashSet<>(0);
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "AGENCY_ID")
private Agency agency;
I am unable to retrieve correct data,when i query for persons.
Problems i have :
1. duplicate records.
2. person with no agency not returning .
3. Bad performance
Here is what i tried, and see combination of above problems
query.from(qPerson).leftJoin(qPerson.phoneNumbers, telecommNumber).leftJoin(qPerson.agency,qAgency);
I have problem 1: which is obvious(in one-to-many relationship) and this can be solved in direct hibernate by using distinct(). I tried distinct in queryDsl and that doesnt seem to work well.
query.from(qPerson).leftJoin(qPerson.phoneNumbers, telecommNumber).fetch().leftJoin(qPerson.agency,qAgency).fetch();
I have problem 3 in this case: returns results correctly but performance is really bad.(Cartesian product problem, i guess).
query.from(qPerson).fetchAll();
I have problem 2 in this case :This one performs well, but doesnt return person without agency when i try to sort on agency field for example. But returns that person if i dont add below to the query.
query.orderBy(person.agency.agencyIdentifierDescription.asc());
I am trying to arrive at a solution that solves above three problems. Thanks for your help.
Well, you should define your entities as following:
"In JPA a ManyToOne relationship is always (well almost always) required to define a OneToMany relationship, the ManyToOne always defines the foreign key (JoinColumn) and the OneToMany must use a mappedBy to define its inverse ManyToOne."
from Wiki:
ManyToOne
OneToMany
example:
public class Person {
#ID
private Integer id;
#OneToMany(mappedBy = "person")
private Set<PhoneNumber> = phoneNumbers;
#ManyToOne
#JoinTable(name="agency_person", joinColumns={#JoinColumn(name="person_id", referencedColumnName="id")}, inverseJoinColumns={#JoinColumn(name="agency_id", referencedColumnName="id")})
private Agency agency;
//Getters & Setters
}
//---------------------------------------------------
public class PhoneNumber {
#ID
private Integer id;
#ManyToOne
#JoinTable(name="phonenumber_person", joinColumns={#JoinColumn(name="phone_id", referencedColumnName="id")}, inverseJoinColumns={#JoinColumn(name="person_id", referencedColumnName="id")})
private Person person;
//Getters & Setters
}
//---------------------------------------------------
public class Agency {
#ID
private Integer id;
#OneToMany(mappedBy = "agency")
private Set<Person> persons;
//Getters & Setters
}
I have table Animal with OneToMany mapping to table EventAnimal:
#OneToMany(mappedBy = "animal")
public Set<EventAnimal> getEventAnimals() {
return eventAnimals;
}
Table EventAnimal looks like this
#Entity
#Table(name = "eventAnimal")
public class EventAnimal {
#Id
int id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "userEvent_id")
UserEvent userEvent;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "animal_id", nullable=false)
Animal animal;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "eventAnimalClass_id")
EventAnimalClass eventAnimalClass;
}
When I add Event animal to animal and save animal, database is not being updated:
//Create EventAnimal object, set properties
eventAnimal.setUserEvent(newEvent);
eventAnimal.setAnimal(animal);
animal.getEventAnimals().add(eventAnimal);
animalPersistenceService.saveAnimal(animal);
What am I doing wrong?
When I try inserting Event animal, like eventAnimalDao.insert(eventAnimal);
instead of
animalPersistenceService.saveAnimal(animal);
I get exception that "animal_id" does not have default value even though I set it.
What is your ID generation strategy? Are you generating the ids by yourself or you will leave this to the DB? If it will be database put:
#GeneratedValue(strategy=GenerationType.AUTO)
You may want to update the key to Integer instead of int too but don`t think this is the problem.
Also if you want to add event in animal and expect to persist it update your mapping to:
#OneToMany(mappedBy = "animal", cascade = CascadeType.ALL)
I have table Company with OneToMany mapping to table Customer:
//bi-directional many-to-one association to Customer
#OneToMany(mappedBy="company")
private Set<Customer> customer;
I have mapped from tis format:
//bi-directional many-to-one association to Company
#ManyToOne
#JoinColumn(name="company_id")
private Company company;
I have use this code, Sucessfully add,edit and delete function is working.
Add function :
Customer customer = new Customer();
Company company = new Company();
company.setCompanyId(intCompanyId);
customer.setCompany(company);
I resolved the problem. It was rather silly mistake - script didn't remove one foreign key from table and I didn't care to look into SQL table.
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;
I have two entities, User and Event. Each event can have multiple users associated with it, so its a one to many between Event and User.
The way its being stored in the database, is that I have 3 tables, user, event, and event_user. event_user contains 3 fields, id, eventId, userId. So I can do a query like select userId from event_user where eventId = ? to get all the users which are associated with the event.
My question is, how can I map this relationship between the events and users in Hibernate, to get it to auto save/load the users associated with the event? I want to have the following field in the Event class:
Set<User> users = new HashSet<>();
and have hibernate auto load / save the users to this set.
How can I map this (using annotations)?
Use the #ManyToMany annotation.
class Event{
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "EVENT_USER",
joinColumns = { #JoinColumn(name = "EVENT_ID") },
inverseJoinColumns = { #JoinColumn(name = "USER_ID") })
private Set<Users> users = new HashSet<Users>();
}
For more information on many to many associations in JPA check out this video tutorial at my blog.
Hibernate doc on the Bidirectional mapping using annotations should help
Basically you need to do something like this
#Entity
public class User implements Serializable {
#ManyToMany(
targetEntity=org.hibernate.test.metadata.manytomany.Event.class,
cascade={CascadeType.ALL}
)
#JoinTable(
name="USER_EVENT",
joinColumns=#JoinColumn(name="USER_ID"),
inverseJoinColumns=#JoinColumn(name="EVENT_ID")
)
public Set<Event> getEvents() {
return events;
}
...
}
#Entity
public class Event implements Serializable {
#ManyToMany(
cascade = {CascadeType.ALL},
mappedBy = "events",
targetEntity = User.class
)
public Set<User> getUsers() {
return users;
}
}
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 |
|_________|_________|
| | |
|_________|_________|