I try to use the @PrimaryKeyJoinColumn
annotation. When doing this, I get an error - attempted to assign id from null one-to-one property.
The user is saved, but the address is not saved. I want to create a common primary key for the User
and Address
table. I found an example here.
Please, tell me what I'm doing wrong, why doesn't this example work for me?
https://github.com/mytestPercon/TestHiber
User.java
@Entity
@Table(name = "user", schema = "TestKeyJoin")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Basic
@Column(name = "name")
private String name;
@OneToOne(mappedBy = "user")
@PrimaryKeyJoinColumn
private Address activated;
// Getter and Setter ...
}
Address.java
@Entity
@Table(name = "Address", schema = "TestKeyJoin")
public class Address implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Basic
@Column(name = "city")
private String city;
@OneToOne
@MapsId
@JoinColumn(name = "id")
private User user;
// Getter and Setter ...
}
SaveController.java
@Controller
public class SaveController {
@Autowired
ServiceJpa serviceJpa;
@GetMapping(value = "/saveUser")
public String getJpa () {
User user = new User();
user.setId(1L);
user.setName("Michael Joseph Jackson");
serviceJpa.saveUser(user);
Address address = new Address();
address.setId(1L);
address.setCity("Los Angeles");
serviceJpa.saveActivated(address);
return "/saveUser";
}
}
Try to correct your mapping in the following way:
@Entity
@Table(name = "user", schema = "TestKeyJoin")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private Address address;
// ...
}
@Entity
@Table(name = "Address", schema = "TestKeyJoin")
public class Address implements Serializable {
@Id
private Long id;
@Column(name = "city")
private String city;
/* You can use @PrimaryKeyJoinColumn instead of the @MapsId here.
See the Example 153. Derived identifier @PrimaryKeyJoinColumn
from the hibernate documentation.
*/
@OneToOne
@MapsId
@JoinColumn(name = "id")
private User user;
// ...
}
and then save user in the following way:
@GetMapping(value = "/saveUser")
public String getJpa () {
User user = new User();
user.setName("Michael Joseph Jackson");
Address address = new Address();
address.setCity("Los Angeles");
// make both sides of the bidirectional @OneToOne in-sync
user.setAddress(address);
address.setUser(user);
serviceJpa.saveUser(user);
return "/saveUser";
}
And several notes:
Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times.
You should not save your entities separately, you just should use a proper cascading on your association.
You should not set value for the generated identifier.
When you use @MapsId
on the Address.user
, it actually means that the Address
entity will borrow the identifier from the one-to-one association. So, you should not use @GeneratedValue
annotation for the Address.id
. (See this)