spring-data-jpa @OneToMany使用延迟初始化失败

时间:2014-04-14 14:20:22

标签: hibernate spring-data-jpa spring-boot

我正在尝试使用Spring-data-jpa,hibernate,spring-data,H2(用于tesitng)以及最终Postgress(生产)创建父子关系。

以下是h2.sql中定义的表:

CREATE TABLE IF NOT EXISTS Menu (
  menuId bigint(11) NOT NULL AUTO_INCREMENT,
  displayText varchar (100)  DEFAULT NOT NULL,
  displayOrder int default NULL
  );


CREATE TABLE IF NOT EXISTS MenuItem (
  menuItemId bigint(11) NOT NULL AUTO_INCREMENT,
  displayText varchar (100)  DEFAULT NOT NULL,
  path varchar (50)  NULL,
  toolTip varchar (500)  DEFAULT NOT NULL,
  displayOrder int default NULL,
  callType varchar (50)  DEFAULT NOT NULL
  );

我有两个简单的实体:

@Entity
public class Menu {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long menuId;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "menu")
    private List<MenuItem> menuItems = new ArrayList<MenuItem>();

    private String displayText;
    private int displayOrder;

@Entity
public class MenuItem {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long menuItemId;

    private String displayText;
    private String path;
    private String toolTip;
    private int displayOrder;

   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "menuId", nullable = false)
    private Menu menu;

    @Enumerated(EnumType.STRING)
    @Column(name = "callType", nullable = false)
    private HttpType callType;

我有一个申请表:

@Configuration
@ComponentScan
@EnableJpaRepositories
@EnableTransactionManagement
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        ApplicationContext MyApplication = SpringApplication.run( Application.class, args );
    }
}

配置类:

@Configuration
public class MyConfiguration {
@Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
        LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
        lef.setDataSource( dataSource );
        lef.setJpaVendorAdapter( jpaVendorAdapter );
        lef.setPackagesToScan( "com.xxx.yyy" );
        return lef;
    }

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql( true );
        hibernateJpaVendorAdapter.setGenerateDdl( true );
        hibernateJpaVendorAdapter.setDatabase( Database.H2 );
        return hibernateJpaVendorAdapter;
    }

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

    @Bean
    public DataSource dataSource() {

        return new EmbeddedDatabaseBuilder().setType( EmbeddedDatabaseType.H2 ).setName( "product" )
                .addScript( "classpath:h2.sql" ).build();
    }
}

我有一个测试:

@SpringApplicationConfiguration
@Transactional
class MenuRepositoryTest extends Specification {

    @Shared
    ConfigurableApplicationContext context

    @Shared
    private MenuRepository menuRepository

    void setupSpec() {
        Future future = Executors.newSingleThreadExecutor().submit(
                new Callable() {
                    @Override
                    public ConfigurableApplicationContext call() throws Exception {
                        return (ConfigurableApplicationContext) SpringApplication.run(Application.class)
                    }
                })
        context = future.get(60, TimeUnit.SECONDS)
        menuRepository = context.getBean(MenuRepository.class)
    }

    void cleanupSpec() {
        if (context != null) {
            context.close()
        }
    }
 @Transactional
    def "test creating a single menu with a single menuItem"() {

        def menu = new Menu()
        menu.setDisplayOrder(0)
        menu.setDisplayText("test")
        menuRepository.save(menu)

        def menuItem = new MenuItem()
        menuItem.setToolTip("tooltip 1")
        menuItem.setPath("/1")
        menuItem.setCallType(HttpType.GET)
        menuItem.setDisplayText("tooltip")
        menu.addMenuItem(menuItem)

        when:
        def menus = menuRepository.findAll()
        menus[0].getMenuItems()

        then:
        menus[0].getMenuItems().size() == 1

    }
}

这是我的gradle显示依赖项:

apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'jacoco'
apply plugin: 'war'
apply plugin: 'maven'


buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-snapshot" }
        mavenLocal()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.0.RC4")
    }
}
repositories {
    mavenCentral()
    maven { url "http://repo.spring.io/libs-snapshot" }
    maven { url 'http://repo.spring.io/milestone' }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:1.0.0.RELEASE")
    compile("org.springframework.boot:spring-boot-starter-data-jpa:1.0.1.RELEASE")
    compile("org.springframework.boot:spring-boot:1.0.1.RELEASE")
    compile("org.springframework:spring-orm:4.0.0.RC1")
    compile("org.hibernate:hibernate-entitymanager:4.2.1.Final")
    compile("org.springframework:spring-tx")
    compile("com.h2database:h2:1.3.172")
    compile("joda-time:joda-time:2.3")
    compile("org.thymeleaf:thymeleaf-spring4")
    compile("org.codehaus.groovy.modules.http-builder:http-builder:0.7.1")
    compile('org.codehaus.groovy:groovy-all:2.2.1')
    compile('org.jadira.usertype:usertype.jodatime:2.0.1')

    testCompile('org.spockframework:spock-core:0.7-groovy-2.0') {
        exclude group: 'org.codehaus.groovy', module: 'groovy-all'
    }
    testCompile('org.codehaus.groovy.modules.http-builder:http-builder:0.7+')
    testCompile("junit:junit")
}

jacocoTestReport {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
}

sourceSets {

    main {

        java {
            srcDirs = []
        }
        groovy {
            srcDirs = ['src/main/groovy', 'src/main/java']
        }
        resources {
            srcDirs = ['src/main/resources']
        }

        output.resourcesDir = "build/classes/main"
    }

    test {
        java {
            srcDirs = []
        }
        groovy {
            srcDirs = ['src/test/groovy', 'src/test/java']
        }
        resources {
            srcDirs = ['src/test/resources']
        }

        output.resourcesDir = "build/classes/test"
    }
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

回答

将build.gradle更改为使用不同的spock

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-milestone" }
        mavenLocal()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.1.RELEASE")
    }
}
repositories {
    mavenCentral()
    maven { url "http://repo.spring.io/libs-milestone" }
    maven { url "https://repository.jboss.org/nexus/content/repositories/releases" }
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
    maven { url "http://repo.spring.io/snapshot" }
    maven { url 'http://repo.spring.io/milestone' }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.0..RELEASE")
    compile("org.springframework.boot:spring-boot:1.0.1.RELEASE")
    compile("org.springframework.boot:spring-boot-starter-data-jpa:1.0.1.RELEASE")
    testCompile('org.spockframework:spock-core:1.0-groovy-2.0-SNAPSHOT') {
        exclude group: 'org.codehaus.groovy', module: 'groovy-all'
    }

    testCompile('org.spockframework:spock-spring:1.0-groovy-2.0-SNAPSHOT') {
        exclude group: 'org.spockframework', module: 'spock-core'
        exclude group: 'org.spockframework', module: 'spring-beans'
        exclude group: 'org.spockframework', module: 'spring-test'
        exclude group: 'org.codehaus.groovy', module: 'groovy-all'
    }
    testCompile('org.springframework:spring-test:4.0.3.RELEASE')
...}

将测试更改为不使用SetupSpec或@Shared:

@ContextConfiguration(classes = MyApplication, loader = SpringApplicationContextLoader)
@Transactional
class MenuRepositoryTest extends Specification {

    @Autowired
    private MenuRepository menuRepository

    def "test creating a single menu with a single menuItem"() {

        def menu = new Menu()
        menu.setDisplayOrder(0)
        menu.setDisplayText("test")
        menuRepository.save(menu)

        def menuItem = new MenuItem()
        menuItem.setToolTip("tooltip 1")
        menuItem.setPath("/1")
        menuItem.setCallType(HttpType.GET)
        menuItem.setDisplayText("tooltip")
        menu.addMenuItem(menuItem)

        when:
        def menus = menuRepository.findAll()
        menus[0].getMenuItems()

        then:
        menus[0].getMenuItems().size() == 1

    }
}

1 个答案:

答案 0 :(得分:3)

这个问题的答案在于Spock和Spring集成。映射都是正确的,但Spock和Spring并没有很好地融合在一起。我更新了问题以显示运行集成测试的正确方法。