Maybe this is a stupid question but it's bugging me.
I have a bi-directional one to many relationship of Employee to Vehicles. When I persist an Employee in the database for the first time (i.e. it has no assigned ID) I also want its associated Vehicles to be persisted.
This works fine for me at the moment, except that my saved Vehicle entity is not getting the associated Employee mapped automatically, and in the database the employee_id foreign key column in the Vehicle table is null.
My question is, is it possible to have the Vehicle's employee persisted at the same time the Employee itself is being persisted? I realise that the Employee would need to be saved first, then the Vehicle saved afterwards. Can JPA do this automatically for me? Or do I have to do something like the following:
Vehicle vehicle1 = new Vehicle();
Set<Vehicle> vehicles = new HashSet<Vehicle>();
vehicles.add(vehicle1);
Employee newEmployee = new Employee("matt");
newEmployee.setVehicles(vehicles);
Employee savedEmployee = employeeDao.persistOrMerge(newEmployee);
vehicle1.setAssociatedEmployee(savedEmployee);
vehicleDao.persistOrMerge(vehicle1);
Thanks!
Edit: As requested, here's my mappings (without all the other methods etc.)
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="employee_id")
private Long id;
#OneToMany(mappedBy="associatedEmployee", cascade=CascadeType.ALL)
private Set<Vehicle> vehicles;
...
}
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="vehicle_id")
private Long id;
#ManyToOne
#JoinColumn(name="employee_id")
private Employee associatedEmployee;
...
}
I just realised I should have had the following method defined on my Employee class:
public void addVehicle(Vehicle vehicle) {
vehicle.setAssociatedEmployee(this);
vehicles.add(vehicle);
}
Now the code above will look like this:
Vehicle vehicle1 = new Vehicle();
Employee newEmployee = new Employee("matt");
newEmployee.addVehicle(vehicle1);
Employee savedEmployee = employeeDao.persistOrMerge(newEmployee);
Much simpler and cleaner. Thanks for your help everyone!
You have to set the associatedEmployee on the Vehicle before persisting the Employee.
Employee newEmployee = new Employee("matt");
vehicle1.setAssociatedEmployee(newEmployee);
vehicles.add(vehicle1);
newEmployee.setVehicles(vehicles);
Employee savedEmployee = employeeDao.persistOrMerge(newEmployee);
One way to do that is to set the cascade option on you "One" side of relationship:
class Employee {
//
#OneToMany(cascade = {CascadeType.PERSIST})
private Set<Vehicles> vehicles = new HashSet<Vehicles>();
//
}
by this, when you call
Employee savedEmployee = employeeDao.persistOrMerge(newEmployee);
it will save the vehicles too.
Related
I want to select random employee as employee of the month. It works fine. But I want to save that selected employee to another table which name is EmployeeOfMonth. How can I do this, do I need to use query ? Do I need another repository ? What should be the approach to do this here?
EmployeeRepository :
EmployeeDTO selectEmployee(Employee employee);
EmployeeService:
EmployeeDTO selectEmployee(Employee employee);
EmployeeServiceImp:
#Override
public EmployeeDTO selectEmployee(Employee employee) {
List<Employee> all = employeeRepository.findAll();
Random randomizer = new Random();
Employee random = all.get(randomizer.nextInt(all.size()));
return EmployeeMapper.toDto(random);
}
Controller :
#GetMapping(path="/month")
public EmployeeDTO selectEmployee(Employee employee){
return employeeService.selectEmployee(employee);
}
Create another Entity or Table with name EmployeeOfMonths Add attrs like employee id and month. Once you select your employee populate this entity and then save.
So we are not duplicating employee data instead we are referencing it to employee table. To get more details about the employee of the month you can query employee table with employee id from EmployeeofMonths table.
#Entity
public class EmployeeOfMonths{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private employeeId;
#Column(name = "created_at", nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private Date created_at;
}
EDIT
I think one employee can become many times Employee of month.
In that case it will be one to many.
#OneToMany(mappedBy = "employee", cascade=CascadeType.ALL)
private List<EmployeeOfMonths> employeeOfMonths;
And for employeemonths.
#ManyToOne
#JoinColumn(name="id", referncedColumnName="employeeId")
private Employee employee;
I want to store Persons with their Cars in the database. I use Spring 5.
The problem is that when I want to add a person with cars in the database, the 'person_id' field from the 'car' table is set to null instead of being set on the id of the created person.
That is weird because I even set that nullable=false in Car class.
#Entity
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#OneToMany(mappedBy = "person",cascade = CascadeType.ALL)
List<Car> cars;
}
#Entity
public class Car {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer carId;
#ManyToOne
#JoinColumn(name = "person_id",nullable=false)
private Person person;
}
CREATE TABLE person (
id INT PRIMARY KEY AUTO_INCREMENT,
...
);
CREATE TABLE car (
id INT PRIMARY KEY AUTO_INCREMENT,
person_id INT,
ADD CONSTRAINT person_id REFERENCES person(id)
);
I use the JpaRepository to save a person:
Person p = new Person();
List<Car> cars = new ArrayList<>();
Car c1 = new Car();
Car c2 = new Car();
cars.add(c1);
cars.add(c2);
p.setCars(cars);
personJpaRepository.save(p);
If you want to take advantage of the Cascade option you must remember to set the dependencies on both sides of the relationship.
Otherwise the persistence provider will not consider that as a relationship:
cars.add(c1);
cars.add(c2);
c1.setPerson(p);
c2.setPerson(p);
p.setCars(cars);
personJpaRepository.save(p);
How can I let Hibernate save the "parent" entity e.g. if I have CarDescription that comes from a client via CarDescriptionDTO.
If I now want to save that e.g.
Session session = HibernateSession.openSession();
session.beginTransaction();
CarDescription carDescription = ConvertDTO.convertCarDescription(carDescriptionDto);
session.save(carDescription);
How can I make sure that a new Car entry gets created if carDescription does not have a Car set at this point? Or is this something I would not want Hibernate to do for me?
Car.java
#Entity
#Table(name = "car")
public class Car extends AbstractTimestampEntity implements Serializable {
private static final long serialVersionUID = -5041816842632017838L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
// ..
}
how can I make sure that this also creates a Car entry?
CarDescription.java
#Entity
#Table(name = "car_description")
public class CarDescription extends AbstractTimestampEntity implements Serializable {
private static final long serialVersionUID = 2840651722666001938L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#NotNull
#ManyToOne
private Car car;
// ..
}
That's not good practice. You shouldn't cascade entity state transitions from Child to Parents.
A better approach is to always check for the existing entities prior to populating the incoming data.
The flow will go like this:
Car car = session.get(Car.class, carId);
if(car == null) {
car = new Car();
session.persist(car);
}
CarDescription carDescription = session.get(CarDescription.class, carDescriptionId);
if(carDescription == null) {
carDescription = new CarDescription();
carDescription.setCar(car);
session.persist(carDescription);
}
CarDescription carDescription = ConvertDTO.convertCarDescription(carDescriptionDto, car, carDescription);
This way you make sure the car and carDescription are associated with the current Session prior to populating the incoming data. Just calling persist will not trigger a database row insert, as it needs a flush for that. If you don't issue any query prior to populating your data, the entities will just be scheduled for flushing and propagated to the DB when the transaction commits.
There are two ways to achieve this.
1) You can create the Car in the CarDescription's field initializer:
#NotNull
#ManyToOne(cascade = CascadeType.PERSIST)
private Car car = new Car();
You also define CascadeType.PERSIST so that the car is persisted together with its CarDescription. This way a CarDescription will always have an empty car by default.
2) You can explicitly create and save the car:
Session session = HibernateSession.openSession();
session.beginTransaction();
CarDescription carDescription = ConvertDTO.convertCarDescription(carDescriptionDto);
Car car = new Car();
carDescription.setCar(car);
session.save(car);
session.save(carDescription);
I'm new to Ebean's world, and I encounter some difficulties to set some relationships between entities.
I have basically two classes, User and Car.
A user can have several cars (so I guess OneToMany) and a car can belongs to one User (so I guess OneToOne).
How can I link these two entities? Here it is what I've done so far
User
#Entity
public class User extends Model{
#Id
#GeneratedValue
public int id;
public String name;
#ManyToMany(cascade=CascadeType.ALL)
public List<Car> car = new ArrayList<Car>();
}
Car
#Entity
public class Car extends Model{
#Id
#GeneratedValue
public int id;
#OneToOne(cascade = CascadeType.ALL)
public User user;
}
And I get the following error
PersistenceException: Error on models.User.car Can not find mappedBy
property [users] in [models.Car]
Can someone explain me clearly how to use annotations the correct way (very poor documentation), and tell me why I get this error?
You guessed wrong :)
Your User should have a #OneToMany relationship with cars so:
#OneToMany(mappedBy = "user", cascade=CascadeType.ALL)
public List<Car> car = new ArrayList<Car>();
while your Car should have a #ManyToOne relationship :
#ManyToOne(cascade = CascadeType.ALL)
public User user;
Take care on the mappedBy property in the #OneToMany annotation: you need to tell Ebean where the foreign key lies in the related class.
User
#Entity
public class User extends Model{
#Id
#GeneratedValue
public int id;
public String name;
#OneToMany(cascade=CascadeType.ALL)
public List<Car> car = new ArrayList<Car>();
}
Car
#Entity
public class Car extends Model{
#Id
#GeneratedValue
public int id;
#ManyToOne(mappedBy="car") //will give you an error
public User user;
}
mappedBy here represents the owner of relation which is important in bidirectional relation.
Think in normal condition can a car exist without the User which owns it means User is the owner in a relation.So in your case User is the owner of relation.Mapped By
But the above code will not work The attribute mappedBy is undefined for the annotation type ManyToOne
In that case #JoinColumn come into picture.Join Column
Well I am a little confused because I just can't find any solution on my problem (maybe some kind of a blackout)
Lets say I have the following (simplified) Entities:
#Entity
public class Employee {
#Id
private long id;
#OneToMany(mappedBy="owner")
private Collection<Phone> phones;
}
#Entity
public class Phone {
#Id
private long id;
#ManyToOne
private Employee owner;
}
and I have a phone object managed by JPA. And now i want to change the Employee of that phone object with something like that:
phone.setEmployee(otherEmployee);
I thought that JPA would update the corresponding Employee would be updated too. Am I wrong?
I experimented with the annotations #ManyToOne and #OneToMany (cascade = CascadeType.ALL, orphanRemoval = true) but it doesn't work.
Do I have to update the Employee object myself? Unfortunatly I didn't find that case in any tutorial or other example, so I hope you can tell me more, I am confused. Thanks in advance.
Edit to make it even clearer:
Phone phone = new Phone();
Employee employee = new Employee();
phone.setEmployee(employee);
em.persist(employee);
em.persist(phone);
employee.getPhones(); //the phone object should be included here.
This link is the answer to your question, you should manage relationships by yourself, i. e.:
class Phone {
...
void setEmployee(Employee employee) {
owner = employee;
employee.addPhone(phone);
}
...
}