1
我想創建一個使用Spring-data-jpa,hibernate,spring-data,H2(用於tesitng)和最終Postgress(生產)的Parent-Child關係。spring-data-jpa @OneToMany失敗,延遲初始化
以下是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.grad文件來使用不同的斯波克
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
}
}
您的映射不正確:雙向關聯必須有一個所有者端和一個由mappedBy屬性標記的反面。在OneToMany中,一方必須是反面。當然,如果你在Menu和MenuItem之間有一個OneToMany,你必須在MenuItem和Menu之間有一個ManyToOne,而不是OneToOne。還要注意targetEntity是無用的:列表是一個'List
我改變了我的映射,但仍然收到相同的錯誤消息。根據我見過的其他例子,這看起來是正確的。這讓我想知道它的交易方式是否在春季啓動時使用,但這是一個瘋狂的猜測。 – sonoerin
Spock可能不知道有關Spring Boot bootstrap功能的任何信息。它是否知道'@ Transactional'?就你的引導使用而言,你所有的'MyConfiguration'都可能被刪除(除了'@ EntitiesS'所在的pacakges可能需要'@ EntityScan')。 Spring Boot已啓用'@ Transactional'併爲您定義事務管理器。 –