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);
Related
table: emps
(
id int primary key,
name varchar(50)
);
table: emps_sal
(
emps_sal_id int primary key auto_increment,
ids int,
salary int,
foreign key(ids) references emps(id)
);
Entity Classes:
#Entity
public class Emps {
#Id
#Column(name="id")
int id;
String name;
#OneToOne(mappedBy = "e")
Emps_sal esal;
--
}
#Entity
public class Emps_sal {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
int emps_sal_id;
int ids ;
int salary;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name="ids")
Emps e;
--
}
Now when I am doing simple select query its working fine. But when I am trying to add entry its working weirdly:
Emps e=new Emps();
e.setId(100);
e.setName("Johnson");
Emps_sal es=new Emps_sal();
es.setIds(100);
es.setSalary(5000);
es.setE(e);
e.setEsal(es);
Firstly in "emps" table it added a extra column "esal_emps_sal_id".
Though it added "100,Johnson" properly. But in Emps_sal nothing is
added. I was expecting "7, 100, 5000".
Change CascadeType to persist in Emps_sal entity class
#OneToOne(cascade = CascadeType.PERSIST)
#PrimaryKeyJoinColumn(name="ids")
Emps e;
The persist operation makes a transient instance persistent. CascadeType PERSIST propagates the persist operation from a parent to a child entity. When we save the Emps_sal entity, the Emp entity will also get saved.
Here is how you need to set the data
Emps e=new Emps();
e.setId(100);
e.setName("Johnson");
Emps_sal es=new Emps_sal();
es.setIds(100);
es.setSalary(5000);
es.setE(e);
entityManager.persist(es);
My model (exemplified) is the following:
CREATE TABLE person (
id INT PRIMARY KEY,
name TEXT
...
);
CREATE TABLE team (
id INT PRIMARY KEY,
name TEXT
....
);
CREATE TABLE team_reference_persons (
team_id INT NOT NULL,
person_id INT NOT NULL,
uses_telephone BOOLEAN,
PRIMARY KEY (team_id, person_id),
FOREIGN KEY (team_id) REFERENCES team(id),
FOREIGN KEY (person_id) REFERENCES person(id)
);
And my JPA defintion:
#Entity
#Table(name = "team")
public class Team {
#Id
private Integer id;
#OneToMany
#JoinTable(name = "team_reference_persons", joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "person_id", referencedColumnName = "id"))
private List<Person> teamReferencePersons;
...
}
#Entity
#Table(name = "person")
public class Person {
#Id
private UUID id;
private String name;
...
}
So far, so good, when all you need is the person list on the team. But now I need to add the team_reference_persons.uses_telephone property from the join table in my person domain, So I am looking for a way to keep the persons logic, while I create a new subclass.
private class TeamIndividual extends Person {
boolean uses_telephone;
}
Then changing List<Person> on Team entity by List<TeamIndividual>. Is that possible someway? JPA should be indicated in such smart way that it adds the join table property to the final target entity (on both read and save).
No need to extend TeamIndividual to Person.
Annotate TeamIndividual with #Table(name = "team_reference_persons")
Define fields(teamId,personId,uses_telephone) inside TeamIndividual
Annotate fields teamId and PersonId with #ManyToOne and #JoinColumn
Add List to Team without annotation
Try this,It will work..!!
I have the following relationship with person and transaction (one to one in my case). I want to be able to save a Person with a Transaction attached resulting in two inserts. One in tbl_person and one in tbl_Transaction. But the following only generates one insert instead of two. The one insert is in tbl_Transaction:
`CREATE TABLE `tbl_person` (
`ID` char(36) NOT NULL,
`TransactionID` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `TransactionID` (`TransactionID`),
CONSTRAINT `tbl_person_ibfk_1` FOREIGN KEY (`TransactionID`)
REFERENCES `tbl_Transaction` (`TransactionID`)
);
CREATE TABLE `tbl_transaction` (
`TransactionID` int(11) NOT NULL,
PRIMARY KEY (`TransactionID`)
);
#Table(name="tbl_person")
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#ToString
#Data
public class Person {
#Id
#GeneratedValue(generator = "hibernate-uuid")
#GenericGenerator(name = "hibernate-uuid", strategy = "uuid2")
#Column(name="ID", nullable = false)
private String ID;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "transactionId")
private Transaction transaction;
}
#Table(name="tbl_transaction")
#Entity
#Data
public class Transaction {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer transactionId;
}
public class Service() {
public void saveTransaction(Transaction transaction) {
Person person = new Person();
person.setTransaction(transaction);
getSessionCurrent().save(person);
}
}
`
service.saveTransaction(transaction);
The service.saveTransaction returns with no exception but it only inserts the transaction but not the person.
Can any one tell me what I am doing wrong ??
you need to define a #OneToOne field in Transaction class
like specified in this question:
#OneToOne bidirectional mapping with #JoinColumn
and then add this line:
transcation.setPerson(person);
I need to map a OneToMany relationship in hibernate, with the JPA annotations, in which is involved a weak entity.
For example
Table orders:
CREATE TABLE orders(
idorder serial NOT NULL,
note varchar(30),
CONSTRAINT orders_pkey PRIMARY KEY (idorder)
)
Table OrderItems:
CREATE TABLE orderitems(
idorder integer NOT NULL,
iditem serial NOT NULL,
qnt integer,
CONSTRAINT orderitems_pk PRIMARY KEY (idorder, iditem),
CONSTRAINT fk_orderitems FOREIGN KEY (idorder)
REFERENCES orders (idorder) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Within my class "Orders" I have realized the method getOrderItem() in this way:
// i need cascadeType.All here
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "order")
public Set<OrderItem> getOrderItems() {
return items;
}
now,
not being able to know the identifier that will be assigned to a new order
prior to insertion, such as Annotations can I use within the class OrderItem in order to submit automatically (in cascade mode) the correct idOrder?
my goal is to get a situation like this.
Order myOrder = new Order();
// myOrder.setId(1) not necessary
myOrder.setNote("orderNote");
OrderItem firstItem = new OrderItem();
// firstItem.setIdOrder() no need to specify idorder
// firstItem.setId(12);
firstItem.setName("firstItem");
firstItem.setQnt(2);
OrderItem secondItem = new OrderItem();
// secondItem.setId(13);
secondItem.setName("secondItem");
secondItem.setQnt(4);
Set<OrderItem> items = new HashSet<OrderItem>();
items.add(firstItem);
items.add(secondItem);
myOrder.setItems(items);
OrderDAO dao = new OrderDAO();
dao.save(myOrder); // i want inser all items in cascade with the idOder assigned to "myOrder"
Ok I will try to add Entity classes you need to have above given scenario.
#Entity
#Table(name="orders")
public class Order{
#Id
#GeneratedValue
#Column(name="ID")
private Long id;
#Column(name="note ")
private String note ;
#OneToMany(mappedBy="orders")
private Set<OrderItem> orderitems;
// Getter and Setter methods
}
And then the OrderItem class
#Entity
#Table(name="OrderItems")
public class OrderItem{
#Id
#Column(name="iditem")
private Long iditem;
#Column(name="qnt")
private long qnt ;
#ManyToOne
#JoinColumn(name="idorder")
private Order order;
public OrderItem() {
}
// Getter and Setter methods
}
Also i dint get which column your setname maps to.. and iditem isnt generated value its assigned id type
Ok then it also could be issue about inverse that who could be the relationship owener .. Inverse=true is same behaviour as mappedBy attribute usage so try changing it..
Let me know if this works or you get a issue while trying this out..
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.