2009-05-27 97 views
6

模式名稱我怎樣才能使單元測試這項工作中使用Hibernate 3.3.1ga和HSQLDB:使用@Table,在Hibernate 3.3.1ga和HSQLDB

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

的問題是,Hibernate所期待的模式存在。第二個問題是,在我的任何代碼運行之前(這發生在Spring的測試設置內部),Hibernate發出CREATE TABLE TEST.CATEGORY,所以我無法在Hibernate之前獲得與數據庫的連接並手動創建模式。

但我需要架構,因爲我必須訪問真實代碼中的不同數據庫。我該怎麼辦?

休眠3.3.1ga,HSQLDB,Spring 2.5的

回答

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; 
} 

但感覺像一個真正的醜陋的黑客。沒有更好的解決方案嗎?

0

它看起來像我在Hibernate DDL創建代碼中有一個可重現的錯誤。你應該是report a bug - 這是一個長期的解決方案,但它是以開源的方式完成的。當然你可能想要生成一個補丁,但是我從來沒有發現Hibernate代碼庫容易被破解。

+0

已經有蟲子開放內容:HTTP://開源。 atlassian.com/projects/hibernate/browse/HHH-1853但顯然,開發人員不喜歡這個補丁(它現在已經開放*三年了)。這告訴我:永遠不會有修復。他們根本不在乎。所以我需要一個解決方法。 – 2009-06-04 10:49:35

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 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豆將首先初始化,使架構只是在時間存在。這也應該讓你的意圖更清晰。

0

我遇到了同樣的問題,MS SQL Server希望定義目錄和模式,但HSQLDB沒有。我的解決方案是加載一個自定義的orm.xml文件(通過persistence.xml),專門用於設置目錄和模式的MS SQL Server。在META-INF/persistence.xml文件

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

2.Specify兩種持久性單元節點:

1.Only對你的實體指定@Table名(忽略任何目錄或模式信息)

<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.Specify默認的目錄和架構在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> 

和你爸用SPRI NG配置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」。

1

下面是一個如何使用test hslqdb創建spring config的示例。它自動檢測@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(); 
} 
}