В моем реальном проекте есть сущность с ~ 15 свойствами (столбцами). Мне нужно хранить еще около 10 свойств, связанных с сущностью. Чтобы избежать создания новых столбцов в той же таблице, один из подходов заключается в том, чтобы сохранить новые свойства в другом объекте и связать их с отношением «один к одному». Здесь Я нашел другой подход, который подразумевает сохранение объекта emdebbable во вторичной таблице. Проблема в том, что встраиваемый объект с неинициализированными свойствами не сохраняется в базе данных. Я воспроизвел проблему в демонстрационном проекте :
@Entity
@Table(name = "STUDENTS")
@SecondaryTable(
name = ADDRESSES,
pkJoinColumns = @PrimaryKeyJoinColumn(name = "STUDENT_ID")
)
public class Student {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
@Embedded // not needed
private Address address;
//constructors, getters and setters
@Embeddable
public class Address {
public static final String ADDRESSES = "ADDRESSES";
@Column(table = ADDRESSES)
private String country;
@Column(table = ADDRESSES)
private String city;
@Column(table = ADDRESSES)
private String street;
@Column(table = ADDRESSES)
private Integer houseNumber;
//constructors, getters and setters
@Service
@Transactional
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@Transactional(readOnly = true)
public List<Student> findAll() {
return studentRepository.findAll();
}
public Student createStudentWithEmptyAddress() {
Student student = new Student("Caleb", "Baker", new Address());
return studentRepository.save(student);
}
public Student createStudentWithNonEmptyAddress() {
Address address = new Address("France", "Paris", "Rue Vieille Du Temple", 88);
Student student = new Student("Cayden", "Hoover", address);
return studentRepository.save(student);
}
public Student findById(Long id) {
return studentRepository
.findById(id)
.orElseThrow(() -> new RuntimeException("Student with id: " + id + " was not found"));
}
public Student updateStudentWithEmptyAddress(Long id) {
Student student = findById(id);
student.setAddress(new Address());
return student;
}
public Student updateStudentWithNonEmptyAddress(Long id) {
Student student = findById(id);
Address address = new Address("USA", "New York", "Stanton Street", 17);
student.setAddress(address);
return student;
}
}
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/all")
@ResponseStatus(OK)
public List<Student> findAll() {
return studentService.findAll();
}
@GetMapping("/{id}")
@ResponseStatus(OK)
public Student findOne(@PathVariable Long id) {
return studentService.findById(id);
}
@PostMapping("/emptyAddress")
@ResponseStatus(CREATED)
public Student createStudentWithEmptyAddress() {
return studentService.createStudentWithEmptyAddress();
}
@PostMapping("/nonEmptyAddress")
@ResponseStatus(CREATED)
public Student createStudentWithNonEmptyAddress() {
return studentService.createStudentWithNonEmptyAddress();
}
@PutMapping("/{id}/emptyAddress")
@ResponseStatus(OK)
public Student updateStudentWithEmptyAddress(@PathVariable Long id) {
return studentService.updateStudentWithEmptyAddress(id);
}
@PutMapping("/{id}/nonEmptyAddress")
@ResponseStatus(OK)
public Student updateStudentWithNonEmptyAddress(@PathVariable Long id) {
return studentService.updateStudentWithNonEmptyAddress(id);
}
}
POST localhost: 8080 / student / nonEmptyAddress возвращает ожидаемый результат:
jdbc.sqlonly: insert into students (first_name, last_name, id) values ('Cayden', 'Hoover', 4)
jdbc.sqlonly: insert into addresses (city, country, house_number, street, student_id) values ('Paris', 'France', 88, 'Rue Vieille Du Temple', 4)
{
"id": 4,
"firstName": "Cayden",
"lastName": "Hoover",
"address": {
"country": "France",
"city": "Paris",
"street": "Rue Vieille Du Temple",
"houseNumber": 88
}
}
GET localhost: 8080 / student / 4 возвращает приведенный выше результат. Теперь давайте создадим ученика с объектом Address, где все свойства не инициализированы:
POST localhost: 8080 / student / nonEmptyAddress возвращает ожидаемый результат:
jdbc.sqlonly: insert into students (first_name, last_name, id) values ('Caleb', 'Baker', 5)
jdbc.sqlonly: insert into addresses (city, country, house_number, street, student_id) values (NULL, NULL, NULL, NULL, 5)
{
"id": 5,
"firstName": "Caleb",
"lastName": "Baker",
"address": {
"country": null,
"city": null,
"street": null,
"houseNumber": null
}
}
Однако когда я вызываю конечную точку GET, я получаю другой результат. GET localhost: 8080 / student / 5:
jdbc.sqlonly: select student0_.id as id1_1_0_, student0_.first_name as first_na2_1_0_, student0_.last_name as last_nam3_1_0_, student0_1_.city as city1_0_0_, student0_1_.country as country2_0_0_, student0_1_.house_number as house_nu3_0_0_, student0_1_.street as street4_0_0_ from students student0_ left outer join addresses student0_1_ on student0_.id=student0_1_.student_id where student0_.id=5
{
"id": 5,
"firstName": "Caleb",
"lastName": "Baker",
"address": null
}
Content of ADRESSES table:
CITY COUNTRY HOUSE_NUMBER STREET STUDENT_ID
Paris France 88 Rue Vieille Du Temple 4
null null null null 5
Адрес нулевой, несмотря на наличие второй записи в таблице ADRESSES. Обновление учащегося с помощью адреса, когда в объекте адреса есть хотя бы одно инициализированное свойство, работает должным образом. Однако если вы обновите учащегося по адресу, в котором все свойства будут нулевыми (PUT localhost: 8080 / student / 3 / emptyAddress), то таблица ADDRESSES даже не обновляется.
Что не так в примере над? Я предполагаю, что неправильно использовать @Embeddable и @Secondary table для достижения желаемого результата.