2016-04-20 4 views
0

Ich versuche Springboot, HSQL und Hibernate zusammen zu verwenden, um einige ziemlich langweilige Daten zu erhalten. Das Problem, das ich in laufen lasse, ist, dass Hibernate meine Tabellen nicht in der Lage scheint korrekt zu verweisen, die folgende Ausnahme werfen:Springboot, Hibernate4 und HSQL Zugreifen auf Schema falsch?

ERROR [main] (SpringApplication.java:826) - Application startup failed 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'strangerEntityManagerFactory' defined in class path resource [com/healz/stranger/config/profiles/GenericSqlConfig.class]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Missing column: user_USER_ID in PUBLIC.STRANGER.PROTECTED_PROPERTIES 
at 
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578) 
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) 
... 

Anfang war ich HSQL-Standardschemanamen, PUBLIC und bemerkte mit, dass die Ausnahme ausgelöst bekommen war, dass Die Anwendung konnte PUBLIC.PUBLIC.PROTECTED_PROPERTIES nicht finden. Das sieht sehr verdächtig aus - warum gibt es hier eine "extra Schicht" von PUBLIC? Es sieht definitiv nicht richtig aus. Der Code, der die EntityManagerFactory Setup funktioniert wie folgt aussieht:

@Log4j 
@Configuration 
@EnableAspectJAutoProxy 
@ComponentScan (basePackages = {"com.healz.stranger.data"}) 
@EnableJpaRepositories (
    entityManagerFactoryRef="strangerEntityManagerFactory", 
    transactionManagerRef="txManager", 
    basePackages={"com.healz.stranger.data.model"} 
) 
@EntityScan (basePackages={ 
    "com.healz.stranger.data.model" 
    }) 
@Import ({HsqlConfig.class, DevMySqlConfig.class, ProdMySqlConfig.class}) 
public class GenericSqlConfig { 

    @Configuration 
    @EnableTransactionManagement(order = Ordered.HIGHEST_PRECEDENCE) 
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 
    protected static class TransactionManagementConfigurer { 
    // ignore annoying bean auto-proxy failure messages 
    } 

    @Bean 
    public static PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor() throws Exception { 
    return new PersistenceAnnotationBeanPostProcessor(); 
    } 

    @Bean 
    public JpaDialect jpaDialect() { 
    return new HibernateJpaDialect(); 
    } 

    @Autowired 
    @Qualifier("hibernateProperties") 
    private Properties hibernateProperties; 

    @Autowired 
    @Qualifier("dataSource") 
    private DataSource dataSource; 


    @Bean (name="strangerEntityManagerFactory") 
    public LocalContainerEntityManagerFactoryBean strangerEntityManagerFactory(
       final @Qualifier("hibernateProperties") Properties props, 
       final JpaDialect jpaDialect) { 
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); 
    emf.setDataSource(dataSource); 
    emf.setPackagesToScan("com.healz.stranger.data"); 

    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
    emf.setJpaVendorAdapter(vendorAdapter); 
    emf.setJpaProperties(hibernateProperties); 
    emf.setJpaDialect(jpaDialect); 

    emf.setPersistenceUnitName("strangerEntityManagerFactory"); 

    return emf; 
    } 

    @Bean (name="sessionFactory") 
    public SessionFactory configureSessionFactory(LocalContainerEntityManagerFactoryBean emf) { 
    SessionFactory sessionFactory = emf.getObject().unwrap(SessionFactory.class); 
    return sessionFactory; 
    } 

    /** 
    * Helper method to get properties from a path. 
    * @param path 
    * @return 
    */ 
    @SneakyThrows (IOException.class) 
    public static Properties getHibernatePropertiesList(final String path) { 
    Properties props = new Properties(); 
    Resource resource = new ClassPathResource(path); 
    InputStream is = resource.getInputStream(); 
    props.load(is); 
    return props; 
    } 

    @Bean (name="txManager") 
    @Autowired 
    public PlatformTransactionManager getTransactionManager(LocalContainerEntityManagerFactoryBean lcemfb, JpaDialect jpaDialect) { 
    EntityManagerFactory emf = null; 
    emf = lcemfb.getObject(); 

    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(); 
    jpaTransactionManager.setEntityManagerFactory(emf); 
    jpaTransactionManager.setJpaDialect(jpaDialect); 
    return jpaTransactionManager; 
    } 
} 

Die HSQL Config wie folgt aussieht:

@Configuration 
@Profile ("hsql") 
public class HsqlConfig { 
    @Bean(name = "dataSource") 
    public DataSource initDataSource() { 
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() 
             .setType(EmbeddedDatabaseType.HSQL) 
             .addScript("classpath:env/dbcache/hsql-schema.sql") 
             .addScript("classpath:env/dbcache/hsql-data.sql"); 
    builder.setName("stranger"); 
    builder.setScriptEncoding("UTF-8"); 
    return builder.build(); 
    } 

    @Bean(name = "hibernateProperties") 
    public Properties getHibernateProperties() { 
    Properties props = new Properties(); 
    props.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); 
    props.put("hibernate.hbm2ddl.auto", "validate"); // using auto and ignoring the hsql scripts "works", but isn't correct 
    props.put("hibernate.default_schema", "stranger"); 
    props.put("hibernate.current_session_context_class", "org.hibernate.context.internal.ThreadLocalSessionContext"); 
    return props; 
    } 

} 

Die andere noticably Seltsame daran ist, dass Hibernate Suche eine Spalte zu sein scheint mit der Name user_USER_ID anstelle von USER_ID, und ich bin mir nicht sicher, warum das auch passiert. Ich bezweifle, dass dies alles durch einen Mapping-Fehler verursacht wurde, da ähnlicher Code mit einer anders konfigurierten EntityMappingFactory zu funktionieren scheint, aber ich möchte diese Möglichkeit nicht ausschließen. Der Code für diese sieht wie folgt aus:

@Entity (name="properties") 
@Table (name="PROTECTED_PROPERTIES") 
public class DbProtectedProperties extends AbstractModel<DbProtectedPropertiesId> implements Serializable { 

    private static final long serialVersionUID = 1L; 

    public void setId(DbProtectedPropertiesId id) { 
    super.id = id; 
    } 

    @EmbeddedId 
    public DbProtectedPropertiesId getId() { 
    if (super.id == null) { 
     super.id = new DbProtectedPropertiesId(); 
    } 
    return super.id; 
    } 

    @Column (name="PROPERTY_VALUE", length=4096, nullable=false) 
    public String getPropertyValue() { 
    return propertyValue; 
    } 


    @Setter 
    private String propertyValue; 

} 

und die ID-Klasse:

@EqualsAndHashCode (of={ "user", "propertyName" }) 
@ToString 
public class DbProtectedPropertiesId implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Setter 
    private DbUsers user; 

    @Setter 
    private String propertyName; 

    @ManyToOne (optional=false, fetch=FetchType.EAGER) 
    @PrimaryKeyJoinColumn (name="USER_ID") 
    public DbUsers getUser() { 
    return user; 
    } 

    @Column (name="PROPERTY_NAME", length=2048, nullable=false, insertable=false, updatable=false) 
    public String getPropertyName() { 
    return propertyName; 
    } 
} 
+0

Gibt es einen Grund, warum Sie versuchen, den Spring-Boot nicht zu verwenden? Sie versuchen sehr hart, um es zu umgehen ... Was noch schlimmer ist, ist, dass Ihre Konfiguration Dinge wie Transaktionsmanagement bricht. –

+0

Können Sie diesen Kommentar erweitern? Es ist mir nicht klar, was du damit meinst. Spring Boot macht eine Menge automatische Konfiguration und es ist mir nicht ganz klar, was automatisch ausgeführt wird. Ich habe bereits festgestellt, dass einige dieser Objekte automatisch erstellt und konfiguriert wurden. Ist ein TransactionManager auch eingerichtet und konfiguriert? – xtro

+0

Grundsätzlich ist alles, was Sie haben, automatisch konfiguriert. Das einzige, was nicht ist, ist die 'SessionFactory', aber warum brauchen Sie das, wenn Sie den' EntityManager' verwenden können? Ich würde alles entfernen, erstellen Sie eine globale 'application.properties' und spezifische' application- .properties' und beginnen Sie damit. –

Antwort

0

Ich gehe hier davon aus, dass Sie eine StrangerApplication im Paket com.healz.stranger haben, wenn Sie nicht wirklich sollten oder verschieben Sie es dort, da es Ihnen eine Menge Konfiguration sparen wird.

Sie verwenden Spring Boot, aber Ihre Konfiguration versucht sehr schwer, nicht.

Zunächst wird die Anwendung

@SpringBootApplication 
public class StrangerApplication { 

    public static void main(String... args) throws Exception { 
     SpringApplication.run(StrangerApplication.class, args); 
    } 

    @Bean (name="sessionFactory") 
    public SessionFactory configureSessionFactory(EntityManagerFactoryBean emf) { 
     SessionFactory sessionFactory = emf.unwrap(SessionFactory.class); 
     return sessionFactory; 
    } 

} 

Jetzt eine application.properties schaffen, die die Standardeigenschaften und allgemeine Eigenschaften enthält. Fügen Sie für das Profil hsql eine application-hsql.properties hinzu, die mindestens Folgendes enthält (von Ihren Konfigurationsklassen abgezogen).

spring.jpa.properties.hibernate.default_schema=stranger 
spring.jpa.hibernate.ddl-auto=validate # maybe this needs to be in application.properties (?) 

Dann wird Ihre hsql-data.sql und hsql-schema.sql-data-gsql.sql und schema-hsql.sql umbenennen und in src/main/resources Frühjahr Boot platzieren wird die für das spezifische Profil erkennen (erklärt here im Referenzhandbuch). Stellen Sie sicher, dass Sie das Schema und die Tabellen in diesem neuen Schema in Ihrer schema.sql erstellen.

Alles andere wird automatisch konfiguriert (Spring Data JPA, AspectJ Proxy, Erkennung von Entitäten). Sie können grundsätzlich alle Konfigurationsklassen entfernen und den Zusatz application-{profile}.properties für die 2 verbleibenden MySQL-Konfigurationsoptionen erstellen.

Der allgemeine Ratschlag wäre, mit dem Framework zu arbeiten, anstatt zu versuchen, es zu umgehen.

+0

Danke! Das war eine großartige Erklärung. Die meisten Extra-Beans wurden doppelt definiert, und das verursachte seltsames Verhalten. Nichtsdestoweniger erforderte die Datenbankkonfiguration, nach der ich gesucht hatte, nur so viel benutzerdefiniertes Verhalten, dass es nicht wirklich möglich war, das asymmetrische Verhalten für ein "normales" RDBMS gegenüber einem eingebetteten wie H2 oder HSQL zu erhalten. Ich habe das größtenteils so gelöst, wie Sie es empfehlen: Es gibt 2 aktive Profile (eines für die Umgebung und eines für die db-Seite). Die DB-Seite wird meistens über die Properties-Datei konfiguriert, aber es gibt auch einen Gerüstcode. – xtro

+0

Welche Art von Gerüst müssen Sie tun? im Allgemeinen kann das ziemlich leicht gelöst werden, während noch die automatische Konfiguration verwendet wird. –

0

Das Problem hier scheint zu sein, dass Frühling Stiefel eine eigene Instanz von LocalContainerEntityManagerFactoryBean definiert und die zweite Definition verursacht wurde seltsame Konflikte . Außerdem gibt es keinen Grund, den JPA-Dialekt auf TransactionManager anzuwenden, da die TransactionManager die Einstellungen von EntityManagerFactory übernimmt, die Spring Boot sowieso konfigurieren wird.