6

如何使用 Hibernate 3.3.1ga 和 HSQLDB 在单元测试中完成这项工作:

@Entity
@Table(name="CATEGORY", schema="TEST")
public static class Category { ... }

问题是 Hibernate 期望模式存在。第二个问题是 HibernateCREATE TABLE TEST.CATEGORY在我的任何代码运行之前发出(这发生在 Spring 的测试设置深处),所以我无法在 Hibernate 之前获得与数据库的连接并手动创建模式。

但我需要模式,因为我必须在真实代码中访问不同的数据库。我应该怎么办?

休眠 3.3.1ga、HSQLDB、Spring 2.5

4

5 回答 5

5

您可以编写一个实现InitializingBean的类:

public class SchemaCreator implements InitializingBean {

    private String schema;
    private DataSource dataSource;

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.execute("CREATE SCHEMA " + schema + " AUTHORIZATION DBA");
    }

}

然后,您必须在此类的 bean 定义文件中定义一个 bean(我正在暗中拍摄您现有的 bean 定义的样子)。

<bean id="dataSource" class="...">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:mem:test"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
</bean>

<bean id="sessionFactory" depends-on="schemaCreator" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    ...
</bean>

<bean id="schemaCreator" class="SchemaCreator">
    <property name="dataSource" ref="dataSource"/>
    <property name="schema" value="TEST"/>
</bean>

通过使用depends-onHibernate 的 bean 的属性,Spring 将确保schemaCreatorbean 将首先被初始化,从而使 schema 及时存在。这也应该使您的意图更加清晰。

于 2009-06-05T18:34:50.707 回答
1

下面是一个示例,说明如何使用 test hslqdb 创建 spring 配置 它会自动从 @Table(schema =...) 检测您的所有模式并为您创建它们。

如果只是为了测试,这应该适合你:

import org.reflections.Reflections; //maven artifact: 'org.reflections:reflections:0.9.9-RC1'
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;

import javax.persistence.Table;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

@Configuration
@ComponentScan("com.test.collection")
public class CollectionConfig {

private static final String[] ENTITY_PACKAGES = { "com.test.collection.domain.dao" };
private static final String CONFIGURATION_LOCATION = "/movie-collection-hibernate.cfg.xml";

@Bean( name = "testSessionFactory" )
@Lazy
public LocalSessionFactoryBean getTestSessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setPackagesToScan( ENTITY_PACKAGES );

    Properties hibernateProperties = getHibernateHsqlTestDbProperties();
    sessionFactory.setHibernateProperties( hibernateProperties );

    createNonStandardSchemas( hibernateProperties );

    return sessionFactory;
}

private void createNonStandardSchemas( Properties properties ) {
    final String DEFAULT_SCHEMA = "";

    Set<String> schemas = new HashSet<>();
    Reflections reflections = new Reflections( ENTITY_PACKAGES );
    Set<Class<?>> annotatedClasses =
            reflections.getTypesAnnotatedWith( Table.class );

    for ( Class<?> clazz : annotatedClasses ) {
        Table table = clazz.getAnnotation( Table.class );
        if ( !DEFAULT_SCHEMA.equals( table.schema() ) ) {
            schemas.add( table.schema() );
        }
    }

    if ( !schemas.isEmpty() ) {
        DriverManagerDataSource driverManager = new DriverManagerDataSource();
        driverManager.setDriverClassName( properties.getProperty( "hibernate.connection.driver_class" ) );
        driverManager.setUrl( properties.getProperty( "hibernate.connection.url" ) );
        driverManager.setUsername( properties.getProperty( "hibernate.connection.username" ) );
        driverManager.setPassword( properties.getProperty( "hibernate.connection.password" ) );

        JdbcTemplate jdbcTemplate = new JdbcTemplate( driverManager );

        for ( String schemaName : schemas ) {
            jdbcTemplate.execute(
                    String.format( "DROP SCHEMA IF EXISTS %s", schemaName)
            );
            jdbcTemplate.execute(
                    String.format( "CREATE SCHEMA %s AUTHORIZATION DBA", schemaName)
            );
        }
    }
}


private Properties getHibernateHsqlTestDbProperties() {
    Properties prop = new Properties();
    prop.setProperty( "hibernate.connection.driver_class", "org.hsqldb.jdbcDriver" );
    prop.setProperty( "hibernate.connection.url", "jdbc:hsqldb:mem:test" );
    prop.setProperty( "hibernate.connection.username", "sa" );
    prop.setProperty( "hibernate.connection.password", "test" );
    prop.setProperty( "hibernate.connection.pool_size", "5" );
    prop.setProperty( "hibernate.dialect", "org.hibernate.dialect.HSQLDialect" );
    prop.setProperty( "hibernate.current_session_context_class", "thread" );
    prop.setProperty( "hibernate.cache.provider_class", "org.hibernate.cache.internal.NoCachingRegionFactory" );
    prop.setProperty( "hibernate.show_sql", "false" );
    prop.setProperty( "hibernate.format_sql", "false" );
    prop.setProperty( "hibernate.use_sql_comments", "false" );
    prop.setProperty( "hibernate.hbm2ddl.auto", "create-drop" );
    return prop;
}


}

这是一个测试样本:

@ContextConfiguration( classes = CollectionConfig.class )
@DirtiesContext( classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD )
public class DaoMappingTest extends AbstractTestNGSpringContextTests {

@Autowired
private SessionFactory testSessionFactory;

@Test
public void thatMovieIsSaved() {
    Movie killBill = getKillBillMovie0();

    saveToDb( Arrays.asList(killBill) );

    Session querySession = testSessionFactory.openSession();
    List<Movie> movies = querySession.createQuery( "from Movie" ).list();
    querySession.close();

    assertThat( movies ).containsExactly( killBill );
}

@Test
public void that2MoviesIsSaved() {
    Movie killBill = getKillBillMovie0();
    Movie terminator = getTerminatorMovie1();

    saveToDb( Arrays.asList( killBill, terminator ) );

    Session querySession = testSessionFactory.openSession();
    List<Movie> movies = querySession.createQuery( "from Movie" ).list();
    querySession.close();

    assertThat( movies ).containsOnly( killBill, terminator );
}

private void saveToDb( List<?> objects ) {
    Session session = testSessionFactory.openSession();
    session.beginTransaction();

    for( Object obj : objects) {
        session.save( obj );
    }

    session.getTransaction().commit();
    session.close();
}

@AfterSuite
public void tearDown() {
    testSessionFactory.close();
}
}
于 2014-01-17T08:03:35.657 回答
1

我目前的解决方案如下所示:

@Override
protected String[] getConfigLocations() {
    createHSQLDBSchemas ();

    return new String[]{
            "test-spring-config.xml"
    };
}

private static boolean hsqldbSchemasCreated = false;

public static void createHSQLDBSchemas ()
{
    if (hsqldbSchemasCreated)
        return;

    try
    {
        log.info ("createHSQLDBSchemas");

        Class.forName("org.hsqldb.jdbcDriver").newInstance();
        Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", "");
        Statement stmt = c.createStatement ();

        String sql;
        sql = "CREATE SCHEMA xxx AUTHORIZATION DBA";
        log.info (sql);
        stmt.execute (sql);

        stmt.close ();
        c.close ();
    }
    catch (Exception e)
    {
        throw new ShouldNotHappenException (e);
    }

    hsqldbSchemasCreated = true;
}

但这感觉就像一个非常丑陋的黑客。没有更好的解决方案吗?

于 2009-05-27T14:50:33.390 回答
0

我遇到了同样的问题,MS SQL Server 想要定义目录和模式,但 HSQLDB 没有。我的解决方案是为设置目录和架构的 MS SQL Server 加载自定义 orm.xml 文件(通过 persistence.xml)。

1.仅为您的实体指定@Table 名称(省略任何目录或架构信息):

@Entity
@Table(name="CATEGORY")
public static class Category { ... }

2.在您的 META-INF/persistence.xml 文件中指定两个持久性单元节点

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <!--
     | For production and integration testing we use MS SQL Server, which needs  
     | the catalog and schema set (see orm-mssql.xml).
     |-->
    <persistence-unit name="com.mycompany.prod">
        <mapping-file>META-INF/orm-mssql.xml</mapping-file>
    </persistence-unit>

    <!--
     | For unit testing we use HSQLDB, which does not need the catalog or schema.
     |-->
    <persistence-unit name="com.mycompany.test" />

</persistence>

3.在 orm-mssql.xml 文件中指定默认目录和架构:

<entity-mappings version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence/orm" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd">

    <persistence-unit-metadata>

        <!--
         | Set the catalog and schema for MS SQL Server
         |-->
        <persistence-unit-defaults>
            <schema>MYSCHEMA</schema>
            <catalog>MYCATALOG</catalog>
        </persistence-unit-defaults>

    </persistence-unit-metadata>

</entity-mappings>

4.我使用 Spring 来配置 JPA,所以我使用属性占位符作为 persistenceUnitName 的值:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
    <property name="persistenceUnitName" value="${entityManagerFactory.persistenceUnitName}" />
</bean>

对于单元测试,使用“com.mycompany.test”,对于集成测试/生产部署,使用“com.mycompany.prod”。

于 2012-12-28T19:21:17.317 回答
0

在我看来,您在 Hibernate DDL 创建代码中有一个可重现的错误。你应该报告一个错误——这是一个长期的解决方案,但它是在开源中完成事情的方式。当然,你可能想制作一个补丁,但我从来没有发现 Hibernate 代码库很容易破解。

于 2009-06-04T10:34:56.603 回答