2009-05-27 7 views
6

¿Cómo puedo hacer este trabajo en las pruebas unitarias usando Hibernate 3.3.1ga y HSQLDB:@Table con nombre de esquema en Hibernate 3.3.1ga y HSQLDB

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

El problema es que Hibernate espera que exista el esquema . El segundo problema es que Hibernate emite el CREATE TABLE TEST.CATEGORY antes de que se ejecute cualquiera de mis códigos (esto ocurre en el interior de la configuración de prueba de Spring), por lo que no puedo conectarme con el DB antes de Hibernate y crear el esquema manualmente.

Pero necesito el esquema porque tengo que acceder a diferentes bases de datos en el código real. ¿Que debería hacer?

Hibernate 3.3.1ga, HSQLDB, Primavera 2,5

Respuesta

1

Mi solución actual es el siguiente:

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

pero que se siente como un corte muy feo. ¿No hay una mejor solución?

0

Me parece que tiene un error reproducible en el código de creación Hibernate DDL. Deberías report a bug: es una solución a largo plazo, pero es la forma en que se hacen las cosas en código abierto. Por supuesto, es posible que desee producir un parche, pero nunca encontré la base de código de Hibernate fácil de hackear.

+0

Ya hay errores abiertos para esto: http: // opensource. atlassian.com/projects/hibernate/browse/HHH-1853 Pero aparentemente, a los desarrolladores no les gusta el parche (está abierto desde hace * tres años *, ahora). Esto me dice: nunca habrá una solución. Simplemente no les importa. Entonces necesito una solución. –

+0

Es una pena, pero sucede: | . ¿Qué tipo de solución estás buscando? ¿Qué es subóptimo en el tuyo, el hecho de que tienes que crear el esquema o el hecho de que está colocado (supongo que por el código) en tu código de prueba? –

+0

No estoy contento de tener que hacerlo en getConfigLocations(): este método hace algo completamente diferente y si alguien estaba buscando este código, este sería el último lugar para buscar. –

5

Se podría escribir una clase que implementa 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"); 
    } 

} 

A continuación, tiene que definir un grano en el archivo de definición de frijol de esta clase (que estoy tomando un tiro en la oscuridad en cuanto a lo que su existente las definiciones de bean parecen).

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

Al utilizar el atributo depends-on de frijol de Hibernate, Spring asegurará que el frijol schemaCreator será inicializado primero, haciendo que el esquema de existir justo a tiempo. Esto también debería aclarar tus intenciones.

0

Me encontré con el mismo problema donde MS SQL Server quiere que el catálogo y el esquema estén definidos, pero HSQLDB no. Mi solución fue cargar un archivo personalizado de orm.xml (a través de persistence.xml) específicamente para MS SQL Server que establece el catálogo y el esquema.

1.Sólo especificar el nombre @Table (omitir cualquier información catálogo o esquema) para su entidad:

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

2.Especifique dos nodos persistencia de unidades en el archivo 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.Specify el catálogo predeterminado y el esquema en el archivo 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 usando Spri ng para configurar la APP, así que utilice una propiedad de marcador de posición para el valor de la 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> 

Para las pruebas de unidad, utilice 'com.mycompany.test' y para la integración-tests/despliegues de producción, uso 'com .mycompany.prod '.

1

A continuación se muestra un ejemplo de cómo puede crear configuraciones de resortes con la prueba hslqdb Detecta automáticamente todos sus esquemas de @Table (schema = ...) y los crea para usted.

Si es sólo para probar esto debería funcionar para usted:

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


} 

Y aquí es una muestra de prueba:

@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(); 
} 
} 
Cuestiones relacionadas