Java 在JPA中级联。通过关联的所有者持久化和合并实体
我想知道JPA(Hibernate)是否可以通过关联所有者间接地持久化和更新实体 我的项目中有两个数据源。我试图找到一种在数据库之间共享一些实体的方法。为此,我只需要用我的每个实体管理器工厂扫描两次。根据我的想法,两个数据库中都可以使用Java 在JPA中级联。通过关联的所有者持久化和合并实体,java,hibernate,jpa,spring-data,spring-data-jpa,Java,Hibernate,Jpa,Spring Data,Spring Data Jpa,我想知道JPA(Hibernate)是否可以通过关联所有者间接地持久化和更新实体 我的项目中有两个数据源。我试图找到一种在数据库之间共享一些实体的方法。为此,我只需要用我的每个实体管理器工厂扫描两次。根据我的想法,两个数据库中都可以使用员工实体。为此,我只需要在第二个数据源中创建一个Phone实体,所有it字段都将通过Hibernate迁移到第二个数据库中 下面是一个代码示例(我使用并删除了导入来简化它) 我想使用一个配置为与我的第二个数据源一起工作的SpringDataJPaPhoneRepo
员工实体。为此,我只需要在第二个数据源中创建一个Phone
实体,所有it字段都将通过Hibernate迁移到第二个数据库中
下面是一个代码示例(我使用并删除了导入来简化它)
我想使用一个配置为与我的第二个数据源一起工作的SpringDataJPaPhoneRepository
public interface PhoneRepository extends JpaRepository<Phone, Long> {}
Hibernate: select department0_.id as id1_3_0_, department0_.name as name2_3_0_ from department department0_ where department0_.id=?
Hibernate: select employee0_.id as id1_4_0_, employee0_.dept_id as dept_id3_4_0_, employee0_.name as name2_4_0_ from employee employee0_ where employee0_.id=?
Hibernate: insert into department (name, id) values (?, ?)
Hibernate: insert into employee (dept_id, name, id) values (?, ?, ?)
Hibernate: select employee_.id, employee_.dept_id as dept_id3_4_, employee_.name as name2_4_ from employee employee_ where employee_.id=?
Hibernate: insert into phone (id, em_id, number) values (null, ?, ?)
Hibernate: select department0_.id as id1_3_0_, department0_.name as name2_3_0_ from department department0_ where department0_.id=?
Hibernate: select employee0_.id as id1_4_0_, employee0_.dept_id as dept_id3_4_0_, employee0_.name as name2_4_0_ from employee employee0_ where employee0_.id=?
Hibernate: insert into department (name, id) values (?, ?)
Hibernate: insert into employee (dept_id, name, id) values (?, ?, ?)
Hibernate: select employee_.id, employee_.dept_id as dept_id3_4_, employee_.name as name2_4_ from employee employee_ where employee_.id=?
Hibernate: insert into phone (id, em_id, number) values (null, ?, ?)
public interface PhoneRepository扩展了JpaRepository{}
我认为,EmployeeRepository只能使用一次来配置第一个数据源。第二个数据库中的所有关系都可以通过Spring/Hibernate自动创建。至少,我想要这个。在我下面的测试中,它配置了第二个数据源,仅用于说明目的
public interface EmployeeRepository extends JpaRepository<Employee, Long> {}
public接口EmployeeRepository扩展了JpaRepository{}
这里有一些测试
@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"));
}
@Autowired
雇员职位雇员职位;
@自动连线
电话储存库;
/**
*成功通过。
*/
@试验
公共无效应被分类为{
phoneRepository.save(新电话(新员工(1L,“John Snow”),“1101”);
phoneRepository.save(新电话(新员工(2L,“Hans Schnee”),“1103”);
}
/**
*原因
*org.springframework.dao.DataIntegrityViolationException:无法执行语句;SQL[n/a];约束[“PUBLIC.EMPLOYEE(ID)上的主键];SQL语句:
*在员工(姓名、id)值(?,)[23505-190]]中插入;嵌套异常为org.hibernate.exception.ConstraintViolationException:无法执行语句
*位于org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
* ...
*
*/
@试验
公共空间应合并电话卡(){
员工=新员工(1L,“约翰·斯诺”);
保存(新电话(员工,“1101”);
保存(新电话(员工,“1102”);
}
/**
*使用已更改电话实体的字段。
*
*{@literal@}ManyToOne
*{@literal@}JoinColumn(name=“em_id”)
*私人雇员;
*
*/
@试验
public void应允许ManualMerging(){
员工=新员工(1L,“约翰·斯诺”);
employeeRepository.save(雇员);
保存(新电话(员工,“1101”);
保存(新电话(员工,“1102”);
}
理想情况下,我希望从第一个数据源中获取一个对象(Employee
),从第二个数据源将其放入包装实体(Phone
),并在不违反规则的情况下更新第二个数据库。首先,我需要创建一个自定义存储库接口和实现类,该类扩展了SimpleJpaRepository
并完全复制了SimpleJpaRepository
的功能,除了Save
方法之外
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
@Override
@SuppressWarnings("unchecked")
protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
return new BaseRepositoryFactory<>(em);
}
private static class BaseRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private final EntityManager em;
public BaseRepositoryFactory(EntityManager em) {
super(em);
this.em = em;
}
@Override
@SuppressWarnings("unchecked")
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new BaseRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), em);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}
RepositoryFactoryBean
将SimpleParepository
替换为自定义实现-BaseRepositoryImpl
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"name"})})
@lombok.NoArgsConstructor(access = PROTECTED)
@lombok.AllArgsConstructor
@lombok.Data
public class Department {
@Id
private Long id;
private String name;
}
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"name"})})
@MergeColumns({"department"})
@lombok.NoArgsConstructor(access = PROTECTED)
@lombok.AllArgsConstructor
@lombok.Data
public class Employee {
@Id
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "dept_id")
private Department department;
}
一些实体将取自我的第一个数据源。也可以为id
字段(如中)使用一些自定义生成器
还有一个Phone
实体“包装”它们以用于我的第二个数据源
public interface PhoneRepository extends JpaRepository<Phone, Long> {}
Hibernate: select department0_.id as id1_3_0_, department0_.name as name2_3_0_ from department department0_ where department0_.id=?
Hibernate: select employee0_.id as id1_4_0_, employee0_.dept_id as dept_id3_4_0_, employee0_.name as name2_4_0_ from employee employee0_ where employee0_.id=?
Hibernate: insert into department (name, id) values (?, ?)
Hibernate: insert into employee (dept_id, name, id) values (?, ?, ?)
Hibernate: select employee_.id, employee_.dept_id as dept_id3_4_, employee_.name as name2_4_ from employee employee_ where employee_.id=?
Hibernate: insert into phone (id, em_id, number) values (null, ?, ?)
Hibernate: select department0_.id as id1_3_0_, department0_.name as name2_3_0_ from department department0_ where department0_.id=?
Hibernate: select employee0_.id as id1_4_0_, employee0_.dept_id as dept_id3_4_0_, employee0_.name as name2_4_0_ from employee employee0_ where employee0_.id=?
Hibernate: insert into department (name, id) values (?, ?)
Hibernate: insert into employee (dept_id, name, id) values (?, ?, ?)
Hibernate: select employee_.id, employee_.dept_id as dept_id3_4_, employee_.name as name2_4_ from employee employee_ where employee_.id=?
Hibernate: insert into phone (id, em_id, number) values (null, ?, ?)
下面是Hibernate在我的第一次测试中的工作原理
我不确定这是否是最简单的解决方案,但至少它确实满足了我的需要。我用的是弹簧靴1.2.8。较新版本的实现可能有所不同。经过一些研究,我得到了以下代码。首先,我需要创建一个自定义存储库接口和实现类,该类扩展了SimpleJpaRepository
并完全复制了SimpleJpaRepository
的功能,除了Save
方法之外
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
@Override
@SuppressWarnings("unchecked")
protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
return new BaseRepositoryFactory<>(em);
}
private static class BaseRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private final EntityManager em;
public BaseRepositoryFactory(EntityManager em) {
super(em);
this.em = em;
}
@Override
@SuppressWarnings("unchecked")
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new BaseRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), em);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}
RepositoryFactoryBean
将SimpleParepository
替换为自定义实现-BaseRepositoryImpl
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"name"})})
@lombok.NoArgsConstructor(access = PROTECTED)
@lombok.AllArgsConstructor
@lombok.Data
public class Department {
@Id
private Long id;
private String name;
}
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"name"})})
@MergeColumns({"department"})
@lombok.NoArgsConstructor(access = PROTECTED)
@lombok.AllArgsConstructor
@lombok.Data
public class Employee {
@Id
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "dept_id")
private Department department;
}
一些实体将取自我的第一个数据源。也可以为id
字段(如中)使用一些自定义生成器
还有一个Phone
实体“包装”它们以用于我的第二个数据源
public interface PhoneRepository extends JpaRepository<Phone, Long> {}
Hibernate: select department0_.id as id1_3_0_, department0_.name as name2_3_0_ from department department0_ where department0_.id=?
Hibernate: select employee0_.id as id1_4_0_, employee0_.dept_id as dept_id3_4_0_, employee0_.name as name2_4_0_ from employee employee0_ where employee0_.id=?
Hibernate: insert into department (name, id) values (?, ?)
Hibernate: insert into employee (dept_id, name, id) values (?, ?, ?)
Hibernate: select employee_.id, employee_.dept_id as dept_id3_4_, employee_.name as name2_4_ from employee employee_ where employee_.id=?
Hibernate: insert into phone (id, em_id, number) values (null, ?, ?)
Hibernate: select department0_.id as id1_3_0_, department0_.name as name2_3_0_ from department department0_ where department0_.id=?
Hibernate: select employee0_.id as id1_4_0_, employee0_.dept_id as dept_id3_4_0_, employee0_.name as name2_4_0_ from employee employee0_ where employee0_.id=?
Hibernate: insert into department (name, id) values (?, ?)
Hibernate: insert into employee (dept_id, name, id) values (?, ?, ?)
Hibernate: select employee_.id, employee_.dept_id as dept_id3_4_, employee_.name as name2_4_ from employee employee_ where employee_.id=?
Hibernate: insert into phone (id, em_id, number) values (null, ?, ?)
下面是Hibernate在我的第一次测试中的工作原理
我不确定这是否是最简单的解决方案,但至少它确实满足了我的需要。我用的是弹簧靴1.2.8。对于较新版本,实现可能有所不同