简体   繁体   中英

Hibernate/ DBunit weird behaviour

I'm currently writing a couple of unit tests for my application, using DBUnit (2.5.3) and hibernate (5.1.0). During the writing of these tests, I came across some odd behaviour.

@Test
public void test() {
    Channel channel = new Channel();
    channel.setName("My Channel");
    channel.setId(14L);

    channel = channelRepository.save(channel);

    Channel found = channelRepository.findOne(14L);
    Assert.assertNotNull(found);
    List<Channel> channels = channelRepository.findAll();
    Assert.assertEquals(1, channels.size());
}

Running the above test, you'd expect this test to succeed. However, this test fails at the last line of code, printing that my object "channels" is an empty list. The odd thing here is that the "findOne" dóes find my saved entity, whereas the findAll fails to find any entities at all. I'm suspecting some sort of caching issue, but I can't seem to find out how to fix it.

Here are some of the classes used during the setup

The test class itself:

public class ChannelRepositoryTest extends AbstractRepositoryTest {

    @Autowired
    private ChannelRepository channelRepository;

    @Override
    protected IDataSet getDataSet() throws Exception {
        return null;
    }

    @Test
    public void test() {
        Channel channel = new Channel();
        channel.setName("My Channel");
        channel.setId(14L);

        channel = channelRepository.save(channel);

        Channel found = channelRepository.findOne(14L);
        Assert.assertNotNull(found);
        List<Channel> channels = channelRepository.findAll();
        Assert.assertEquals(1, channels.size());
    }
}

The abstractRepositoryTest:

public abstract class AbstractRepositoryTest extends AbstractRTVTest {

    private static final ByteArrayResource QUERY = new ByteArrayResource("SET REFERENTIAL_INTEGRITY FALSE".toString().getBytes(Charset.forName("UTF-8")));


    @Autowired
    private DataSource dataSource;

    @Before
    public void beforeEveryTest() throws Exception {
        IDatabaseConnection dbConn = new     DatabaseDataSourceConnection(dataSource);
        ScriptUtils.executeSqlScript(dbConn.getConnection(), QUERY); // here to satisfy jenkins, otherwise we get constraint violation errors
        dbConn.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,new H2DataTypeFactory());
        if (getDataSet() != null) {
            DatabaseOperation.CLEAN_INSERT.execute(dbConn, getDataSet());
        }
    }

    protected abstract IDataSet getDataSet() throws Exception;

    protected FlatXmlDataSetBuilder getFlatXmlDataSetBuilder() {
        return new FlatXmlDataSetBuilder().setColumnSensing(true);
    }
}

The AbstractRTVTest:

@ContextConfiguration(classes = {HibernateTestConfiguration.class})
public abstract class AbstractRTVTest extends AbstractTransactionalJUnit4SpringContextTests {

}

The HibernateTestConfiguration

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application-test.properties")
@ComponentScan(basePackages = {"be.persgroep.rtv"})
public class HibernateTestConfiguration {

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(new String[] { "be.persgroep.rtv.domain.model" });
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
    }

    @Bean(name = "mySimpleDataSource")
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
            dataSource.setUrl("jdbc:h2:mem:something;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
        dataSource.setUsername("sa");
        dataSource.setPassword("");
        return dataSource;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        properties.put("hibernate.hbm2ddl.auto", "create-drop");
        properties.put("hibernate.show_sql", "true");
        properties.put("hibernate.cache.use_second_level_cache", "false");
        properties.put("hibernate.cache.use_query_cache", "false");
        return properties;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory s)     {
        HibernateTransactionManager txManager = new HibernateTransactionManager();
        txManager.setSessionFactory(s);
        return txManager;
    }
}

The ChannelRepository

public interface ChannelRepository extends JpaRepository<Channel, Long> {

}

The Channel is just a simple @Entity-annotated class

@Entity
@Table(name = "ZENDERS", catalog = "")
public class Channel {
    private long id;
    private String name;

    @Id
    @Column(name = "ID")
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "NAAM")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The application-test.properties

environment=local

server.port=8080

securityservice.baseUrl=
securityservice.apiKey=

spring.profiles.active=test

dpp.sec.springuser.application=RTV
# set to false to use profiles (default is actions) as granted authorities
dpp.sec.springuser.rolesAreActions=true
# set to true to allow access to authenticated users without granted authorities
dpp.sec.springuser.allowAuthenticationWithoutRoles=false
# comma seperated list of external providers: set to LDAP or LDS to only allow users from that provider
dpp.sec.springuser.externalProviderNames=LDAP,LDS
# prefix to prepend to all granted authorities (used by RoleVoter to distinguish from other security attributes)
dpp.sec.springuser.rolePrefix=AUTH_
#encrypt passwords with RSA for authentication
dpp.sec.springuser.useRsa=true

And finally, the build.gradle file:

buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
        maven { url "https://artifactory.persgroep.cloud/artifactory/libs-release" }
    }
    dependencies {
        classpath "net.persgroep.gradle:persgroep-gradle-plugins:1.3.3"
        classpath 'gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17'
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
        classpath "com.moowork.gradle:gradle-node-plugin:1.1.1"
        classpath 'org.ajoberstar:gradle-git:1.7.2'
//        classpath "com.github.iboyko.gradle.plugins:jpamodelgen-plugin:1.0.1" ?
    }

    ext["thymeleaf.version"] = "3.0.3.RELEASE"
    ext["thymeleaf-layout-dialect.version"] = "2.1.2"
    ext['hibernate.version'] = '5.1.0.Final'
}

allprojects {
    repositories {
        mavenCentral()
        maven { url "https://artifactory.persgroep.cloud/artifactory/libs-release" }
    }

    apply plugin: 'net.persgroep.java'
    apply plugin: 'net.persgroep.buildversion'
    apply plugin: 'com.gorylenko.gradle-git-properties'
    apply plugin: 'findbugs'
    apply plugin: 'pmd'
    apply plugin: 'jdepend'
    apply plugin: 'org.ajoberstar.grgit'

    findbugs {
        reportsDir = file("$project.buildDir/findbugsReports")
        effort = "max"
        reportLevel = "high"
    }

    tasks.withType(FindBugs) {
        reports {
            xml.enabled = true
            html.enabled = false
        }
    }

    pmd {
        ignoreFailures = true
    }

    tasks.withType(Pmd) {
        reports {
            xml.enabled = false
            html.enabled = true
        }
    }

    jdepend {
        toolVersion = '2.9.1'
        ignoreFailures = true
    }
}

subprojects {
    task listAllDependencies(type: DependencyReportTask) {}

    apply plugin: 'net.persgroep.java'
    apply plugin: 'net.persgroep.publish'
    apply plugin: 'io.spring.dependency-management'

    dependencies {
        compile('org.springframework.boot:spring-boot-starter')
        testCompile('org.springframework.boot:spring-boot-starter-test')
    }

    dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") } }
}

project('validation-tests'){
    dependencies {

        testCompile "info.cukes:cucumber-java:${cucumberVersion}"
        testCompile "info.cukes:cucumber-junit:${cucumberVersion}"
        testCompile "info.cukes:cucumber-picocontainer:${cucumberVersion}"

        testCompile "org.seleniumhq.selenium:selenium-java:3.4.0"
        testCompile "org.seleniumhq.selenium:selenium-support:3.4.0"
        testCompile "io.github.bonigarcia:webdrivermanager:1.7.1"
        testCompile group: 'org.seleniumhq.selenium', name: 'selenium-chrome-driver', version: '3.4.0'

    }

    // Don't run these tests by default
    test.enabled = project.hasProperty("browser") && project.hasProperty("environment")
    //test.enabled = false

    test {
        systemProperties = [
                browser: project.findProperty('browser')?:'chrome',
                baseTestUrl: "http://rtv." + (project.findProperty('environment') ?: 'test' )+ ".persgroep.net/"
        ]
    }

}

project('domain:model'){

    dependencies {
        compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
        compile ('be.persgroep.adv:commons-adv-model:1.1.3'){
            exclude module: 'orika-core'
        }
        compile group: 'ma.glasnost.orika', name: 'orika-core', version: '1.5.1'
        compile("org.hibernate:hibernate-java8:5.1.0.Final")
        compile group: 'com.fasterxml.jackson.core', name:'jackson-annotations'
        compile group: 'com.fasterxml.jackson.datatype', name:'jackson-datatype-jsr310'
    }
}

project('domain:services'){
    dependencies {
        compile project(':domain:model')

        compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
        compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
    }
}

project('application'){

    dependencies {
        compile project(':domain:model')
        compile project(':domain:services')

        compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
    }
}

project('infrastructure'){

    apply plugin: 'com.moowork.node'
    apply plugin: 'com.moowork.gulp'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'net.persgroep.deploy'
    apply plugin: 'net.persgroep.distzip'
    apply plugin: 'war'

    dependencies {
        compile project(':application')

        compile 'org.springframework.cloud:spring-cloud-starter-oauth2:1.2.0.RELEASE'

        compile("org.springframework.boot:spring-boot-starter-web")

        providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
        compile("org.springframework.boot:spring-boot-starter-batch")
        compile("org.springframework.boot:spring-boot-starter-data-jpa")
        compile("org.springframework.boot:spring-boot-starter-jdbc")
        compile("org.springframework.boot:spring-boot-starter-thymeleaf")
        compile("org.springframework.boot:spring-boot-starter-security")

        compile("org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.2.RELEASE")
        compile("org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.0.RELEASE")

        compile 'net.logstash.logback:logstash-logback-encoder:4.8'

        compile "net.persgroep.utils.spring.boot:dpp-spring-boot-starter-jwt:${dppSecurity}"
        compile group: 'be.persgroep.adv', name: 'commons-adv-io', version: '1.1.3'

        compile group: 'com.oracle', name: 'ojdbc6', version: '11.2.0.3'
        compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.6'

        compile 'org.webjars:bootstrap:3.3.7'
        compile 'org.samba.jcifs:jcifs:1.3.17'

        compile 'io.springfox:springfox-swagger2:2.7.0'
        compile 'io.springfox:springfox-swagger-ui:2.7.0'

        compile "net.persgroep.service.conversion:client:1.2"
        compile "net.persgroep.diocontent.images.webservice:diocontent-webservice-client:4.0.13"

        compile 'org.quartz-scheduler:quartz:2.2.1'

        compile group: 'c3p0', name: 'c3p0', version: '0.9.0.4' // default c3p0 does not cut it and causes pipeline to fail

        compile 'com.netflix.feign:feign-core:8.18.0'
        compile 'com.netflix.feign:feign-jaxb:8.18.0'
        compile 'com.netflix.feign:feign-gson:8.18.0'
        compile 'com.googlecode.json-simple:json-simple:1.1.1'
        compile group: 'org.json', name: 'json', version: '20170516'

        compile "net.persgroep.utils.monitor:status:1.4"
        compile "net.persgroep.utils.monitor:monitor:1.4"
        compile("com.h2database:h2:1.4.195")

        testCompile("org.dbunit:dbunit:2.5.3")
    }

    node {
        version = '6.10.3'
        npmVersion = '3.10.10'
        download = true
        nodeModulesDir = new File(projectDir, "src/main/client")
    }

    gulp {
        // Set the directory where gulpfile.js should be found
        workDir = new File(projectDir, "src/main/client")
        // Whether colors should output on the terminal
        colors = true
        // Whether output from Gulp should be buffered - useful when running tasks in parallel
        bufferOutput = false
    }

    springBoot {
        buildInfo()
    }

    processResources() {
        filesMatching('**/*.properties') {
            expand(project.properties)
        }
    }

    gulp_build.dependsOn 'installGulp'

    // processes your package.json before running gulp build
    gulp_build.dependsOn 'npmInstall'

    // runs "gulp build" as part of your gradle build
    war.dependsOn gulp_build

    apply from: "$rootDir/deploy.gradle"

    // needed to run the application on windows because of filename issues
    task pathingJar(type: Jar) {
        dependsOn configurations.runtime
        appendix = 'pathing'

        doFirst {
            manifest {
                // Build the Class-Path for absolute paths based on runtime dependencies.
                attributes "Class-Path": configurations.runtime.files.collect {
                    it.toURL().toString().replaceFirst(/file:\/+/, '/')
                }.join(' ')
            }
        }
    }

    bootRun {
        dependsOn pathingJar
        doFirst {
            // Add the compiled app classed to the classpath of the pathing jar. Probably there is a gradle variable for them?
            classpath = files("$buildDir/classes/main", "$buildDir/resources/main", "$projectDir/gsp-classes", pathingJar.archivePath)
        }
    }

}

task wrapper(type: Wrapper) {
    gradleVersion = "${gradleWrapperVersion}"
}

tasks.withType(Test) {
    reports.html.destination = file("${reporting.baseDir}/${name}")
}

The few properties used in the build.gradle are located in gradle.properties:

group=be.persgroep.rtv
cucumberVersion=1.2.5

gradleWrapperVersion=3.5
springBootVersion=1.5.3.RELEASE
dppSecurity=1.3.7

Any help is greatly appreciated!

Kind regards,

Elias

It seems there is a very subtle logical mistake.

It should be:

Assert.assertEquals(1, channels.size());

Also, please check the value of the channels variable using the debugger (for example, step-by-step debugging): it should not be empty after performing the assignment: channels = channelRepository.findAll(); .

Update

The wrong transaction manager is used: the JPA repositories are used, therefore, the JpaTransactionManager should be used instead of the HibernateTransactionManager . Because the correct one is already configured by default, it is sufficient to remove the HibernateTransactionManager bean definition (the transactionManager() method).

Additional related references

  1. Spring JPA Repository findAll returns no data in JUnit test .
  2. Transactional annotation not working in Spring Boot .

Hope this helps.

I think your problem should be solved by adding flush after save . Now when you getting element by id there is no any request to database because your object is present in first level cache and hibernate just returning it from Context, but when you calling findAll hibernate is sending request for fetching data but your data base is empty because by default hibernate will send insert before transaction commit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM