I'm wondering is it possible with JPA (Hibernate) to persist and update entities indirectly through the owner of association.
There are two datasources in my project. I'm trying to find a way to share some entities between databases. For that I just have to scan it twice with each of my Entity Manager Factories. According to my idea, an Employee
entity could be used in both databases. For that, I just need to create a Phone
entity in the second datasource and all it fields will be migrated via Hibernate to my second database.
Here is a sample of code (I've used lombok and removed imports to simplify it)
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"name"})})
@lombok.NoArgsConstructor(access = PROTECTED)
@lombok.AllArgsConstructor
@lombok.Data
public class Employee {
@Id
private Long id;
private String name;
}
@Entity
@lombok.Data
@lombok.NoArgsConstructor(access = PROTECTED)
public class Phone {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(cascade = {PERSIST, MERGE, REFRESH})
@JoinColumn(name = "em_id")
private Employee employee;
private String number;
public Phone(Employee employee, String number) {
this.employee = employee;
this.number = number;
}
}
I would like to use a Spring Data Jpa PhoneRepository configured to work with my second datasource
public interface PhoneRepository extends JpaRepository<Phone, Long> {}
And an EmployeeRepository, as I think, could be used only once to be configured with first datasource. All relations in the second database could be created automatically by Spring/Hibernate. At least, I would like this. In my tests below it configured with my second datasource for illustrative purposes only.
public interface EmployeeRepository extends JpaRepository<Employee, Long> {}
Here are some tests
@Autowired
EmployeeRepository employeeRepository;
@Autowired
PhoneRepository phoneRepository;
/**
* Passes successfully.
*/
@Test
public void shouldPersitPhonesCascaded() {
phoneRepository.save(new Phone(new Employee(1L, "John Snow"), "1101"));
phoneRepository.save(new Phone(new Employee(2L, "Hans Schnee"), "1103"));
}
/**
* Causes <blockquote><pre>
* org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PRIMARY KEY ON PUBLIC.EMPLOYEE(ID)"; SQL statement:
* insert into employee (name, id) values (?, ?) [23505-190]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
* at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
* ...
* </pre></blockquote>
*/
@Test
public void shouldMergePhonesCascaded() {
Employee employee = new Employee(1L, "John Snow");
phoneRepository.save(new Phone(employee, "1101"));
phoneRepository.save(new Phone(employee, "1102"));
}
/**
* Works with changed Phone entity's field.
* <blockquote><pre>
* {@literal @}ManyToOne
* {@literal @}JoinColumn(name = "em_id")
* private Employee employee;
* </pre></blockquote>
*/
@Test
public void shouldAllowManualMerging() {
Employee employee = new Employee(1L, "John Snow");
employeeRepository.save(employee);
phoneRepository.save(new Phone(employee, "1101"));
phoneRepository.save(new Phone(employee, "1102"));
}
Ideally I would like to take an object (Employee
) from my first datasource, put it into a wrapping entity (Phone
) from my second datasource and update the second database without violations.