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);
Related
Consider I have 2 entity
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#OneToMany(cascade = CascadeType.ALL)
public List<Child> children = new ArrayList<>();
}
and
#Entity
class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#ManyToOne
public Parent parent;
}
how can I create Child instance with parentId
without call findById(Long parentId) i.e.
Child createChild(parentId) {
Child child = new Child();
child.parent = //parent.findById(parentId); I don't wanna go to database
//for nothing if in this spot anyway will be parentId in database
return child;
}
I thought it can be done with quare but hql don't have
INSERT .... VALUE .., so I'm here, appreciate any help.
If it's don't have any sense due to architecture,
please explain, it's be a great help.
No need to create new object in
public List<Child> children = new ArrayList<>();
just write
#OneToMany(targetEntity = Child.class, cascade = CascadeType.ALL)
public List<Child> children;
It is not a big problem that you will call parent.findById(parentId);
Hibernate caches some of the requests especially when used findById. You can see this answer link
The only thing to note is that you should not override findById in the repository, or if you do you should added it to the jpa cache.
EntityManager#getReference(Class<T> entityClass, Object primaryKey) is your friend here.
entityManager.getReference(Parent.class, parentId); returns an entity proxy. It can be used to improve the performance of the write operations since there will be no database call unless you access the fields of the returned entity.
In my opinion, if you want to save children entities, you must find their parent firstly. But it's not true. Following is an example.
Person table
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private List<Book> books;
}
Book table
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "person_id", nullable = false)
private Person person;
}
BookRepository
public interface BookRepository extends CrudRepository<Book, Long> {}
persist process
// the person(id=1) is in the database.
Person p = new Person();
p.setId(1);
Book book = new Book();
book.setPerson(p);
book.setName("book");
bookRepository.save(book); // no error throws
I want to know why the last statement is success. The person instance is created manually and is not retrieved from database by Spring Data JPA, I think it means the state of person instance is transient.
Based on your logic Book and person are new then you have to create those objects. Why you think it should come from database. If you want retrieve the user by findbyId then set the newly created book in List and save the user object behind the scenes it will do the updation in the database for that user.
You can use personRepository.getOne(1) which will return a proxy for a person with id 1. Setting this object then as person on the book will do what you are looking for.
I'm writing a simple Order-Payment one-to-one association:
Order class relevant code:
#Entity
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
(...)
#OneToOne(cascade=CascadeType.ALL, mappedBy="order")
private Payment payment;
Payment class relevant code:
#Entity
public class Payment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne
#MapsId
private Order order;
Then I want to implement a basic method to seed my test database using Spring Data JPA repositories, but if I try the following code, I get a PersistentObjectException saying detached entity passed to persist:
Order o1 = new Order(null, Instant.parse("2019-06-20T19:53:07Z"));
orderRepository.save(o1);
Payment p1 = new Payment(null, Instant.parse("2019-06-23T13:02:55Z"), o1);
paymentRepository.save(p1);
Same problem happens if I try this way:
Order o1 = new Order(null, Instant.parse("2019-06-20T19:53:07Z"));
Payment p1 = new Payment(null, Instant.parse("2019-06-23T13:02:55Z"), o1);
orderRepository.save(o1);
paymentRepository.save(p1);
The only way it worked was as follows:
Order o1 = new Order(null, Instant.parse("2019-06-20T19:53:07Z"));
Payment p1 = new Payment(null, Instant.parse("2019-06-23T13:02:55Z"), o1);
o1.setPayment(p1);
orderRepository.save(o1);
paymentRepository.save(p1);
I'd like to understand why only the last approach works. In a many-to-one association I don't have to set that two-way association before saving in order to work. What am I missing about detached entities behavior this case? Thanks.
Because we have to set the Child Entity for Parent Entity as you have done using
o1.setPayment(p1);
No need to pass the id as null as it will be #GeneratedValue. You may have to create an appropriate constructor for them.
As you are using cascade=CascadeType.ALL you may save the Parent and Child
as below no need use save() for both entity
//save Parent-Child at same
#PostMapping(value = "/onetoone")
public String OneToOne(#RequestBody ParentOne parent)
{
ChildOne childOne = parent.getChildOne();
childOne.setParentOne(parent);
parent.setChildOne(childOne);
parentOneRepository.save(parent);
return "saved";
/*{
"parentName":"Parent Name",
"childOne":{
"childName":"Child Name"
}
}*/
}
Discussion
Try this one,
Order o1 = new Order(null, Instant.parse("2019-06-20T19:53:07Z"));
Payment p1 = new Payment(null, Instant.parse("2019-06-23T13:02:55Z"), o1);
o1.setPayment(p1);
orderRepository.save(o1);
No need the save method for Payment. Because you used the cascade type like #OneToOne(cascade=CascadeType.ALL, mappedBy="order") Whenever you save the Order then Payment also saved.
i want to implement bi-directional One-To-Many relationship in my app engine application. I have two entities - entity A (parent) and entity B(child).
Parent :
#Entity
public class A implements Serializable {
#Id
private String aId;
#OneToMany(mappedBy = "objA", cascade = CascadeType.ALL)
List<B> bList;
getter().........setter()
}
Child :
#Entity
public class B implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key bId;
#ManyToOne(fetch = FetchType.LAZY)
private A parentA;
getter().........setter()
}
Here,at first i persist object of class A and at that time i don't set its bList property. After that i create object of B and update setbList() of A which will automatically persist object of type B in datastore (as here i have set cascade=CascadeType.ALL).Now i want to access A from B objects.(parent from child) in my endpoint class which i am using in my android client. How can i achieve this?
You just need to add a reference to A in your B class
private A parentA;
parentA = //whatever you use when you update setbList()
Good luck!
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.