2009-05-27 9 views
6

Jak mogę dokonać tej pracy w testach jednostkowych za pomocą hibernacji 3.3.1ga i hsqldb:Korzystanie @Table z nazwą schematu w hibernacji 3.3.1ga i HSQLDB

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

Problem polega na tym, że spodziewa się, Hibernate schemat istnieć . Drugi problem polega na tym, że Hibernacja wydaje CREATE TABLE TEST.CATEGORY zanim którykolwiek z moich kodów zostanie uruchomiony (dzieje się to głęboko w konfiguracji testowej Springa), więc nie mogę uzyskać połączenia z DB przed hibernacją i ręcznie utworzyć schemat.

Ale potrzebuję schematu, ponieważ muszę uzyskać dostęp do różnych baz danych w prawdziwym kodzie. Co powinienem zrobić?

Hibernate 3.3.1ga, HSQLDB, Wiosna 2,5

Odpowiedz

1

Moje obecne rozwiązanie wygląda tak:

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

jednak, że czuje się jak naprawdę brzydki hack. Czy nie ma lepszego rozwiązania?

0

Wygląda na to, że masz powtarzalny błąd w kodzie tworzenia Hibernate DDL. Powinieneś: report a bug - to rozwiązanie długoterminowe, ale tak właśnie działa się w open source. Oczywiście możesz chcieć stworzyć łatkę, ale nigdy nie znalazłem łatwej do hackowania bazy kodu Hibernate.

+0

Są już otwarte dla robaków to: http: // opensource. atlassian.com/projects/hibernate/browse/HHH-1853 Wygląda jednak na to, że deweloperzy nie lubią łatki (jest ona dostępna * przez trzy lata *). To mi mówi: nigdy nie będzie poprawki. Po prostu ich to nie obchodzi. Potrzebuję obejścia. –

+0

To wstyd, ale się zdarza: | . Jakiego rodzaju rozwiązanie ich szukasz? Co jest podoptymalne w twoim - fakt, że musisz stworzyć schemat lub fakt, że jest on umieszczony (zakładam z kodu) w twoim kodzie testowym? –

+0

Jestem niezadowolony, że muszę to zrobić w getConfigLocations() - ta metoda robi coś zupełnie innego i jeśli ktoś szukał tego kodu, byłoby to ostatnie miejsce do patrzenia. –

5

Można napisać klasy, która implementuje 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"); 
    } 

} 

Następnie trzeba zdefiniować fasoli w Twojej fasoli plik definicji tej klasy (Biorę strzał w ciemności, co istniejącego definicje fasoli wyglądają jak).

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

Za pomocą atrybutu fasoli Hibernate depends-on, Wiosna zapewni, że schemaCreator fasola będzie pierwszy zainicjowany, powodując schemat istnieć tylko w czasie. Powinno to również sprawić, że twoje intencje będą bardziej wyraźne.

0

Wpadłem na ten sam problem, w którym MS SQL Server chce zdefiniować katalog i schemat, ale HSQLDB tego nie robi. Moim rozwiązaniem było załadowanie niestandardowego pliku orm.xml (przez persistence.xml) specjalnie dla MS SQL Server, który ustawia katalog i schemat.

1.Only określić @Table nazwę (pomijają żadnych katalogowy lub schematu info) dla jednostki:

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

2.Specify dwa węzły wytrwałość-jednostkowych w META-INF pliku/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.Specify katalog domyślny i schemat w pliku 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.I'm wykorzystaniem Wiosna ng do konfiguracji JPA, więc używam właściciela zastępczy dla wartości 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> 

Dla testów jednostkowych, użyj „com.mycompany.test” i integracyjnych testów/wdrożenia produkcji, stosowania „com .mycompany.prod ".

1

Poniżej znajduje się przykład tworzenia konfiguracji wiosna za pomocą testu hslqdb Automatycznie wykrywa wszystkie schematy z @ Tab (schemat = ...) i tworzy je dla Ciebie.

Jeżeli jest to tylko do testowania to powinno działać dla Ciebie:

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


} 

A oto próbka testowa:

@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(); 
} 
} 
Powiązane problemy