在Hibernate 3.3.1ga和HSQLDB中使用带有模式名称的@Table

时间:2009-05-27 14:19:44

标签: spring hibernate schema hsqldb

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

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

问题是Hibernate期望架构存在。第二个问题是Hibernate在我的任何代码运行之前发出CREATE TABLE TEST.CATEGORY(这发生在Spring的测试设置中),因此我无法在Hibernate之前获得与DB的连接并手动创建模式。

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

Hibernate 3.3.1ga,HSQLDB,Spring 2.5

5 个答案:

答案 0 :(得分: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>

通过使用Hibernate bean的depends-on属性,Spring将确保首先初始化schemaCreator bean,导致架构及时存在。这也应该让你的意图更清晰。

答案 1 :(得分: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;
}

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

答案 2 :(得分: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();
}
}

答案 3 :(得分:0)

在我看来,你在Hibernate DDL创建代码中有一个可重现的错误。你应该report a bug - 这是一个长期的解决方案,但它是在开源中完成的。当然你可能想要生成一个补丁,但我从来没有发现Hibernate代码库很容易破解。

答案 4 :(得分: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,所以我使用property-placeholder作为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”。