2009-05-27 3 views
6

Как я могу сделать эту работу в модульных тестах с использованием Hibernate 3.3.1ga и HSQLDB:Использование @Table с именем схемы в спящем режиме 3.3.1ga и HSQLDB

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

Проблема заключается в том, что Hibernate ожидает, что схема существует , Вторая проблема заключается в том, что Hibernate выдает CREATE TABLE TEST.CATEGORY до того, как какой-либо из моих кодов запускается (это происходит глубоко внутри тестовой установки Spring), поэтому я не могу получить соединение с БД до Hibernate и создать схему вручную.

Но мне нужна схема, потому что мне нужно получить доступ к различным базам данных в реальном коде. Что мне делать?

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 Но, по-видимому, разработчикам не нравится патч (он открыт для * трех лет *, сейчас). Это говорит мне: никогда не будет исправления. Им просто все равно. Так что мне нужно обходное решение. –

+0

Это позор, но это происходит: | , Какое решение вы ищете? Что является субоптимальным в вашем - тем фактом, что вам нужно создать схему или тот факт, что она помещена (я полагаю, из кода) в вашем тестовом коде? –

+0

Я недоволен тем, что мне нужно сделать это в getConfigLocations() - этот метод делает что-то совсем другое, и если кто-то ищет этот код, это будет последнее место для поиска. –

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 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 depends-on, весна будет гарантировать, что schemaCreator боб будет инициализирован первым, в результате чего схема существует только во времени. Это также должно сделать ваши намерения более ясными.

0

Я столкнулся с той же проблемой, когда MS SQL Server хочет, чтобы каталог и схема были определены, но HSQLDB этого не делает. Моим решением было загрузить собственный файл orm.xml (через persistence.xml) специально для MS SQL Server, который устанавливает каталог и схему.

1.Only указать @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.В каталог по умолчанию и схемы в файле ОРМ-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.I'm с использованием SPRI нг настроить 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» и для интеграции-тесты/развертывание производства, использование «ком .mycompany.prod.

1

Ниже приведен пример того, как вы можете создать конфигурацию пружины с помощью теста hslqdb Он автоматически обнаруживает все ваши схемы из @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(); 
} 
} 
Смежные вопросы