Java 如何使用JpaRepository保存初始数据

Java 如何使用JpaRepository保存初始数据,java,hibernate,spring-data-jpa,Java,Hibernate,Spring Data Jpa,为了最初用数据填充数据库,我创建了实体实例,并编写了一个服务类,将它们保存在数据库中 以一种非常简单的方式,我的模型由三个类a、B和C组成。类a和B的实例都引用(相同)C的实例(用@ManyToOne注释): 对于A和B,我使用JpaRepositories进行持久化 然后,我尝试保存以编程方式创建的实例: @Transactional public void save(A a1, A a2, A a3, B b1, B b2) { aRepo.save(a1); aRepo.save(

为了最初用数据填充数据库,我创建了实体实例,并编写了一个服务类,将它们保存在数据库中

以一种非常简单的方式,我的模型由三个类a、B和C组成。类a和B的实例都引用(相同)C的实例(用
@ManyToOne
注释):

对于A和B,我使用
JpaRepositories
进行持久化

然后,我尝试保存以编程方式创建的实例:

@Transactional
public void save(A a1, A a2, A a3, B b1, B b2) {
  aRepo.save(a1);
  aRepo.save(a2);
  aRepo.save(a3);

  bRepo.save(b1);
  bRepo.save(b2);
}
我在第三行得到一个异常(“传递给persist:C的分离实体”),因为实例c1已经保存在第一行中

我怎样才能避免呢?我认为在我的服务中用
@Transactional
标记我的save方法已经足够了,但显然不行

我当然可以写

aRepo.save(Arrays.asList(a1, a2, a3));
bRepo.save(Arrays.asList(b1, b2));
但这只会转移我的问题,因为当引用已经保存的c1的b1应该被保存时,第二行会出现异常

如何保存在一个方法中创建的数据结构而不出现异常

下面是代码的其余部分:

@Entity
public class A {

    @Id
    @GeneratedValue
    @Access(AccessType.FIELD)
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @Access(AccessType.FIELD)
    private C c;

    public A(C c) {
        this.c = c;
    }

    public C getC() {
        return c;
    }
}
类B看起来完全相同,类C只包含ID参数

实例是在测试用例中创建的:

public class DBTest extends AbstractH2TestCase {

    @Test
    public void testDataStorage() {

        final AbstractApplicationContext context = new AnnotationConfigApplicationContext(DBTest.class);

        try {
            final PersistService persistService = context.getBean(PersistService.class);

            C c1 = new C();
            C c2 = new C();

            A a1 = new A(c1);
            A a2 = new A(c2);
            A a3 = new A(c1);

            B b1 = new B(c1);
            B b2 = new B(c2);

            persistService.save(b1, b2, a1, a2, a3);

        } finally {
            context.close();
        }
    }
}
我在web上找到了测试的超类,并对其进行了一些修改:

@Configuration
@ComponentScan(basePackageClasses = A.class)
@EnableJpaRepositories
public class AbstractH2TestCase {

    public AbstractH2TestCase() {
        super();
    }

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder().setType(H2).build();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
            JpaVendorAdapter jpaVendorAdapter) {

        final LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();

        lef.setDataSource(dataSource);
        lef.setJpaVendorAdapter(jpaVendorAdapter);

        final String thisPackageAndSubpackages = this.getClass().getPackage().getName();
        lef.setPackagesToScan(thisPackageAndSubpackages);

        return lef;
    }

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {

        final HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();

        hibernateJpaVendorAdapter.setShowSql(false);
        hibernateJpaVendorAdapter.setGenerateDdl(true);
        hibernateJpaVendorAdapter.setDatabase(Database.H2);

        return hibernateJpaVendorAdapter;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager();
    }

}
如果您想要代码的运行版本-我在此处签入:


这是一个maven项目。

您的
@Transactional
注释是无用的,因为您没有使用容器(如J2EE或Spring)来实际计算该方法必须在一个事务中运行,因此对
save()
的每次调用都是一个事务,这就是为什么您得到一个分离的实体

尝试在或中使用Spring。

您缺少了

@EnableTransactionManagement
关于你的DBTest类

    @EnableTransactionManagement
    public class DBTest extends AbstractH2TestCase {

你能展示一下初始化a1-3和c1,c2实例的代码吗?我添加了它,还有一个指向github的链接,在那里我放了一个正在运行的示例。当通过
PersistService PersistService=context.getBean(…)
获取服务时,我以为我在使用一个容器?在我的测试用例的抽象超类中,通过注释进行的配置似乎与xml示例中的配置相同?创建AnnotationConfigApplicationContext时,应该传递要扫描注释的bean类。您只通过了DBTest.class。尝试传递定义save()方法的类。我用
@ComponentScan(basePackageClasses=A.class)
注释了DBTest的超类,这就足够了,因为所有类(在本例中)都位于同一个包中。正如PepperBob在回答中指出的那样,实际问题确实是事务管理没有被激活。在用
@EnableTransactionManagement
注释了我的DBTest之后,一切都正常工作。我在github上更新了我的示例项目,如果您愿意,可以看一看。
    @EnableTransactionManagement
    public class DBTest extends AbstractH2TestCase {