Использование @Table с именем схемы в Hibernate 3.3.1ga и HSQLDB - PullRequest
6 голосов
/ 27 мая 2009

Как я могу заставить это работать в модульных тестах с использованием Hibernate 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

Ответы [ 5 ]

5 голосов
/ 05 июня 2009

Вы можете написать класс, который реализует 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-компонентов).

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

Используя атрибут depends-on bean-компонента Hibernate, Spring гарантирует, что сначала будет инициализирован bean-компонент schemaCreator, в результате чего схема будет существовать как раз вовремя. Это также должно прояснить ваши намерения.

1 голос
/ 17 января 2014

Ниже приведен пример того, как вы можете создать весенний конфиг с помощью теста 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();
}
}
1 голос
/ 27 мая 2009

Мое текущее решение выглядит так:

@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 голосов
/ 28 декабря 2012

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

1. Только укажите имя @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. Укажите каталог и схему по умолчанию в файле 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. Я использую Spring для настройки 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.

0 голосов
/ 04 июня 2009

Мне кажется, у вас есть воспроизводимая ошибка в коде создания Hibernate DDL. Вы должны сообщить об ошибке - это долгосрочное решение, но это то, как все делается в открытом коде Конечно, вы можете захотеть создать патч, но я никогда не находил базу кода Hibernate легкой для взлома.

...