【问题标题】:How to persist initial data with JpaRepository如何使用 JpaRepository 持久化初始数据
【发布时间】:2013-11-22 15:21:39
【问题描述】:

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

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

a1 -> c1
a2 -> c2
a3 -> c1

b1 -> c1
b2 -> c2

对于 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);
}

我在第三行得到一个异常(“分离的实体传递给持久化:C”),因为实例 c1 已经保存在第一行。

我怎样才能避免这种情况?我认为在我的服务中使用@Transactional 标记我的保存方法就足够了,但显然不是。

我当然可以写

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();
        }
    }
}

我在网上某处找到的测试的超类并对其进行了一些修改:

@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();
    }

}

如果您想要代码的运行版本 - 我在这里检查了它:

https://github.com/BernhardBln/stackexchange

这是一个 Maven 项目。

【问题讨论】:

  • 能否展示初始化 a1-3 和 c1、c2 实例的代码?
  • 我添加了它,还有一个指向 github 的链接,我在其中放置了一个运行示例

标签: java hibernate spring-data-jpa


【解决方案1】:

你不见了

@EnableTransactionManagement

关于你的 DBTest-Class

    @EnableTransactionManagement
    public class DBTest extends AbstractH2TestCase {

【讨论】:

    【解决方案2】:

    您的 @Transactional 注释没有用,因为您没有使用容器(如 J2EE 或 Spring)来实际确定该方法必须在一个事务中运行,因此每次调用 save() 都是一个事务,那就是为什么你得到一个独立的实体。

    尝试在this examplethe docs 中使用Spring。

    【讨论】:

    • 通过PersistService persistService = context.getBean(...)获取服务时,我以为我在使用容器?在我的测试用例的抽象超类中,通过注解进行的配置似乎与您的 xml 示例中的相同?
    • 当你创建 AnnotationConfigApplicationContext 时,你应该传递你想要扫描注解的 bean 的类。你只通过 DBTest.class。尝试传递定义 save() 方法的类。
    • 我用 @ComponentScan(basePackageClasses = A.class) 注释了 DBTest 的超类,这足以让所有类(在本例中)都存在于同一个包中。正如 PepperBob 在他的回答中指出的那样,实际的问题确实是事务管理没有被激活。在用 @EnableTransactionManagement 注释我的 DBTest 后,一切都按预期工作。我在github上更新了我的示例项目,喜欢的可以看看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 2021-10-29
    • 2016-03-09
    • 2021-11-13
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    相关资源
    最近更新 更多