如何使用Hibernate 3.3.1ga和HSQLDB在单元测试中完成这项工作:
@Entity
@Table(name="CATEGORY", schema="TEST")
public static class Category { ... }
问题是Hibernate期望架构存在。第二个问题是Hibernate在我的任何代码运行之前发出CREATE TABLE TEST.CATEGORY
(这发生在Spring的测试设置中),因此我无法在Hibernate之前获得与DB的连接并手动创建模式。
但我需要架构,因为我必须访问真实代码中的不同数据库。我该怎么办?
Hibernate 3.3.1ga,HSQLDB,Spring 2.5
答案 0 :(得分: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定义文件中定义一个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>
通过使用Hibernate bean的depends-on
属性,Spring将确保首先初始化schemaCreator
bean,导致架构及时存在。这也应该让你的意图更清晰。
答案 1 :(得分: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;
}
但这感觉就像一个非常丑陋的黑客。是不是有更好的解决方案?
答案 2 :(得分:1)
下面是如何使用test hslqdb创建spring配置的示例 它会自动检测来自@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();
}
}
答案 3 :(得分:0)
在我看来,你在Hibernate DDL创建代码中有一个可重现的错误。你应该report a bug - 这是一个长期的解决方案,但它是在开源中完成的。当然你可能想要生成一个补丁,但我从来没有发现Hibernate代码库很容易破解。
答案 4 :(得分:0)
我遇到了同样的问题,MS SQL Server希望定义目录和架构,但HSQLDB却没有。我的解决方案是专门为设置目录和架构的MS SQL Server加载自定义orm.xml文件(通过persistence.xml)。
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,所以我使用property-placeholder作为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”。