2009-05-27 13 views
6

Wie kann ich Hibernate machen Mit @Table 3.3.1ga und HSQLDB diese Arbeit in der Unit-Tests unter Verwendung von:mit Schemanamen in Hibernate 3.3.1ga und HSQLDB

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

Das Problem ist, dass Hibernate das Schema zu existieren erwartet . Das zweite Problem ist, dass Hibernate den Code CREATE TABLE TEST.CATEGORY ausgibt, bevor einer meiner Code-Läufe ausgeführt wird (dies geschieht tief in der Testkonfiguration von Spring). Daher kann ich vor dem Hibernate keine Verbindung zur Datenbank herstellen und das Schema manuell erstellen.

Aber ich brauche das Schema, weil ich im realen Code auf verschiedene Datenbanken zugreifen muss. Was soll ich machen?

Hibernate 3.3.1ga, HSQLDB, Spring 2.5

Antwort

1

Meine aktuelle Lösung sieht wie folgt aus:

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

aber das fühlt sich an wie eine wirklich hässliche Hack. Gibt es keine bessere Lösung?

0

Es sieht für mich so aus, als ob Sie einen reproduzierbaren Fehler im Hibernate-DDL-Erstellungscode haben. Sie sollten report a bug - es ist eine langfristige Lösung, aber es ist die Art, wie Dinge in Open Source getan werden. Natürlich möchten Sie vielleicht einen Patch erstellen, aber ich fand die Hibernate-Codebasis nie einfach zu hacken.

+0

Es gibt bereits Bugs offen für diese: http: // Open Source. atlassian.com/projects/hibernate/browse/HHH-1853 Aber anscheinend mögen die Entwickler den Patch nicht (er ist jetzt für * drei Jahre * offen). Das sagt mir: Es wird nie eine Lösung geben. Es interessiert sie einfach nicht. Also brauche ich einen Workaround. –

+0

Das ist eine Schande, aber es passiert: | . Nach welcher Art von Lösung suchst du sie? Was ist in Ihrem sub-optimal - die Tatsache, dass Sie das Schema oder die Tatsache, dass es platziert ist (ich vermute von dem Code) in Ihrem Testcode erstellen? –

+0

Ich bin unglücklich, dass ich es in getConfigLocations() tun muss - diese Methode macht etwas ganz anderes und wenn jemand nach diesem Code suchen würde, wäre dies der letzte Ort, an dem man suchen müsste. –

5

Sie könnten eine Klasse schreiben, die InitializingBean implementiert:

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

} 

Sie haben dann eine Bohne in Ihrem Bean Definitionsdatei dieser Klasse zu definieren (Ich bin ein Schuss im Dunkeln zu nehmen, was Ihre vorhandenen Bean Definitionen sehen aus wie).

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

das depends-on Attribut von Hibernate Bean Durch die Verwendung wird Frühling sicherzustellen, dass die schemaCreator Bohne zunächst initialisiert werden, so dass das Schema nur in der Zeit existieren. Dies sollte auch Ihre Absichten klarer machen.

0

Ich stieß auf das gleiche Problem, wo MS SQL Server möchte, dass der Katalog und das Schema definiert, aber HSQLDB nicht. Meine Lösung bestand darin, eine benutzerdefinierte Datei orm.xml (via persistence.xml) speziell für MS SQL Server zu laden, die den Katalog und das Schema festlegt.

1.Only geben Sie den Namen @Table (weglassen jeden Katalog oder Schema info) für Ihr Unternehmen:

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

2.Geben zwei Persistenz-Einheit Knoten in Ihrem META-INF/persistence.xml Datei

<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 der Standardkatalog und das Schema in der ORM-mssql.xml Datei:

<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 mit Spri ng JPA zu konfigurieren, so dass ich eine Eigenschaft-Platzhalter für den Wert des 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> 

Für Unit-Tests verwenden 'com.mycompany.test' und für die Integration-Tests/Produktionsimplementierungen, Einsatz ‚com .meinefirma.prod '.

1

Unten ist ein Beispiel, wie Sie Spring-Konfiguration mit Test hslqdb erstellen können Es erkennt automatisch alle Ihre Schemas von @Table (Schema = ...) und erstellt sie für Sie.

Wenn es nur diese für die Prüfung sollte für Sie arbeiten:

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


} 

Und hier ist eine Testprobe:

@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(); 
} 
} 
Verwandte Themen