spring 在 Hibernate 3.3.1ga 和 HSQLDB 中使用带有模式名称的 @Table
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/915974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using @Table with schema name in Hibernate 3.3.1ga and HSQLDB
提问by Aaron Digulla
How can I make this work in unit tests using Hibernate 3.3.1ga and HSQLDB:
如何使用 Hibernate 3.3.1ga 和 HSQLDB 在单元测试中完成这项工作:
@Entity
@Table(name="CATEGORY", schema="TEST")
public static class Category { ... }
The problem is that Hibernate expects the schema to exist. The second problem is that Hibernate issues the CREATE TABLE TEST.CATEGORYbefore any of my code runs (this happens deep inside Spring's test setup), so I can't get a connection to the DB before Hibernate and create the schema manually.
问题是 Hibernate 期望模式存在。第二个问题是 HibernateCREATE TABLE TEST.CATEGORY在我的任何代码运行之前发出问题(这发生在 Spring 的测试设置深处),所以我无法在 Hibernate 之前连接到数据库并手动创建模式。
But I need the schema because I have to access different databases in the real code. What should I do?
但是我需要模式,因为我必须在实际代码中访问不同的数据库。我该怎么办?
Hibernate 3.3.1ga, HSQLDB, Spring 2.5
Hibernate 3.3.1ga、HSQLDB、Spring 2.5
回答by Adam Paynter
You could write a class that implements InitializingBean:
您可以编写一个实现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");
}
}
You then have to define a bean in your bean definition file of this class (I'm taking a shot in the dark as to what your existing bean definitions look like).
然后,您必须在此类的 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>
By using the depends-onattribute of Hibernate's bean, Spring will ensure that the schemaCreatorbean will be initialized first, causing the schema to exist just in time. This should also make your intentions clearer.
通过使用depends-onHibernate 的 bean的属性,Spring 将确保schemaCreator首先初始化 bean,从而使 schema 及时存在。这也应该使您的意图更加清晰。
回答by Aaron Digulla
My current solution looks like this:
我目前的解决方案是这样的:
@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;
}
but that feels like a really ugly hack. Isn't there a better solution?
但这感觉就像一个非常丑陋的黑客。没有更好的解决办法吗?
回答by Yurii Bondarenko
Below is an example of how you can create spring config with test hslqdb It automaticly detects all your schemas from @Table(schema =...) and creates them for you.
下面是一个示例,说明如何使用 test hslqdb 创建 spring 配置它会自动检测来自 @Table(schema =...) 的所有模式并为您创建它们。
If it is just for testing this should work for you:
如果只是为了测试,这应该适合你:
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;
}
}
And here is a test sample:
这是一个测试样本:
@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();
}
}
回答by Robert Munteanu
It looks to me like you have a reproducible bug in the Hibernate DDL creation code. You should report a bug- it's a long term solution but it's the way things are done in open source. Of course you might want to produce a patch, but I never found the Hibernate code base easy to hack.
在我看来,您在 Hibernate DDL 创建代码中有一个可重现的错误。你应该报告一个错误——这是一个长期的解决方案,但它是在开源中完成的方式。当然,您可能想要制作补丁,但我从未发现 Hibernate 代码库易于破解。
回答by Patrick Crocker
I ran into the same problem where MS SQL Server wants the catalog and schema defined, but HSQLDB does not. My solution was to load a custom orm.xml file (via persistence.xml) specifically for MS SQL Server that sets the catalog and schema.
我遇到了同样的问题,MS SQL Server 想要定义目录和模式,但 HSQLDB 没有。我的解决方案是专门为设置目录和架构的 MS SQL Server 加载自定义 orm.xml 文件(通过 persistence.xml)。
1.Only specify the @Table name (omit any catalog or schema info) for your entity:
1.只为您的实体指定@Table 名称(省略任何目录或架构信息):
@Entity
@Table(name="CATEGORY")
public static class Category { ... }
2.Specify two persistence-unit nodes in your META-INF/persistence.xml file
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.Specify the default catalog and schema in the orm-mssql.xml file:
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.I'm using Spring to configure JPA, so I use a property-placeholder for the value of the persistenceUnitName:
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>
For unit tests, use 'com.mycompany.test' and for integration-tests/production deployments, use 'com.mycompany.prod'.
对于单元测试,请使用“com.mycompany.test”,对于集成测试/生产部署,请使用“com.mycompany.prod”。

