【问题标题】:How does hibernate resolve schema for initially validating to a Teradata database?hibernate 如何解析架构以对 Teradata 数据库进行初始验证?
【发布时间】:2013-08-18 03:48:06
【问题描述】:

环境:

Java/Spring 应用程序使用 JPA/Hibernate 进行持久化,并连接到通过 JNDI 访问的应用程序容器 (Tomcat) 中配置的 Teradata 数据源。

我正在使用的版本:

java: 6
spring: 3.2.4.RELEASE
hibernate.core: 4.2.4.Final
hibernate.entitymanager: 4.2.4.Final
hibernate.validator: 5.0.1.Final
springdata: 1.3.4.RELEASE
javax.validation: 1.1.0.Final

问题:

同一服务器中有两个 Teradata 数据库,它们具有相同的命名表但具有不同的列:

DatDe001.SFITEM
Columns: [iipcst, iidesc, iivend, updated_at, iisku#, created_at, item_expdt, item_effdt]

DEV_DIG_UMT.SFITEM
Columns: [iipcst, iidesc, iivend, row_updt_tms, iisku#, row_insrt_tms, item_expdt, item_effdt]

如您所见,不同的列是 updated_at -> row_updt_tms 和 created_at -> row_insrt_tms

我正在使用使用此 jdbc url 配置的 JNDI 数据源:

jdbc:teradata://<server_ip>/DATABASE=DEV_DIG_UMT,DBS_PORT=1025,COP=OFF,CHARSET=UTF8,TMODE=ANSI  

假设 jdbc 连接将使用该 jdbc url 中的 DATABASE 值解析表的位置。然而,Hibernate 似乎采取了错误的做法:DatDe001.SFITEM 在执行初始模式验证时,即在 Spring 尝试创建 EntityManagerFactory bean 的上下文初始化时刻:

2013-08-15 13:32:03,635 INFO localhost-startStop-1 org.hibernate.tool.hbm2ddl.TableMetadata - HHH000261: Table found: DatDe001.SFITEM
2013-08-15 13:32:03,635 INFO localhost-startStop-1 org.hibernate.tool.hbm2ddl.TableMetadata - HHH000037: Columns: [iipcst, iidesc, iivend, updated_at, iisku#, created_at, item_expdt, item_effdt]

因此,由于我的 JPA 实体(请参阅帖子下面的实体)没有这些列,因此休眠验证会引发异常(请参阅汇总的堆栈跟踪):

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in file [C:\APP\springsource\vfabric-tc-server-developer-2.9.2.RELEASE\base-instance\wtpwebapps\profile-items\WEB-INF\classes\META-INF\spring\applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:529)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in file [C:\APP\springsource\vfabric-tc-server-developer-2.9.2.RELEASE\base-instance\wtpwebapps\profile-items\WEB-INF\classes\META-INF\spring\applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1482)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
...
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
            at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:924)
            at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:899)
...
Caused by: org.hibernate.HibernateException: Missing column: row_updt_tms in DatDe001.SFITEM
at org.hibernate.mapping.Table.validateColumns(Table.java:366)
at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1305)
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:155)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:508)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1790)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:96)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:914)

看到之后,我想知道当通过 JPA/hibernate 对数据库执行查询语句时,这种行为是否会持续存在,或者在这种情况下它是否会指向正确的表。

然后出于调查目的,我将 JPA 实体更改为与 DatDe001.SFITEM 表具有相同的列:

@Entity
public class Sfitem implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private SfitemPK id;

    @Column(name="\"iidesc\"")
    private String iidesc;

    @Column(name="\"iipcst\"")
    private BigDecimal iipcst;

    @Column(name="\"iivend\"")
    private BigDecimal iivend;

    @Temporal
    @Column(name="\"item_expdt\"")
    private Date itemExpdt;

    @Temporal
    @Column(name="\"created_at\"")
    private Date createdAt;

    @Temporal
    @Column(name="\"updated_at\"")
    private Date updatedAt;

    ...
}

我启动了应用程序并成功加载。现在日志看起来不错,而不是显示异常:

...
2013-08-15 14:42:52,056 INFO localhost-startStop-1 org.hibernate.tool.hbm2ddl.TableMetadata - HHH000261: Table found: DatDe001.SFITEM
2013-08-15 14:42:52,056 INFO localhost-startStop-1 org.hibernate.tool.hbm2ddl.TableMetadata - HHH000037: Columns: [iipcst, iidesc, iivend, updated_at, iisku#, created_at, item_expdt, item_effdt]
2013-08-15 14:42:52,061 DEBUG localhost-startStop-1 org.hibernate.internal.SessionFactoryImpl - Checking 0 named HQL queries
2013-08-15 14:42:52,061 DEBUG localhost-startStop-1 org.hibernate.internal.SessionFactoryImpl - Checking 0 named SQL queries
2013-08-15 14:42:52,063 TRACE localhost-startStop-1 org.hibernate.service.internal.AbstractServiceRegistryImpl - Initializing service [role=org.hibernate.service.config.spi.ConfigurationService]
2013-08-15 14:42:52,113 TRACE localhost-startStop-1 org.hibernate.service.internal.AbstractServiceRegistryImpl - Initializing service [role=org.hibernate.stat.spi.StatisticsImplementor]
...

我尝试对表执行查询,但意外地发现这次 Hibernate 指向了正确的数据库/模式:DEV_DIG_UMT,查询失败,因为现在实体有其他列数据库:DatDe001,查看日志:

2013-08-15 14:50:05,731 TRACE tomcat-http--4 org.hibernate.engine.query.spi.QueryPlanCache - Located HQL query plan in cache (SELECT o FROM Sfitem o WHERE o.id.iisku = :iisku AND o.id.itemEffdt <= :date AND coalesce(o.itemExpdt, cast('9999-12-31' as date)) >= :date)
2013-08-15 14:50:05,766 TRACE tomcat-http--4 org.hibernate.engine.query.spi.QueryPlanCache - Located HQL query plan in cache (SELECT o FROM Sfitem o WHERE o.id.iisku = :iisku AND o.id.itemEffdt <= :date AND coalesce(o.itemExpdt, cast('9999-12-31' as date)) >= :date)
2013-08-15 14:50:05,768 TRACE tomcat-http--4 org.hibernate.engine.query.spi.HQLQueryPlan - Find: SELECT o FROM Sfitem o WHERE o.id.iisku = :iisku AND o.id.itemEffdt <= :date AND coalesce(o.itemExpdt, cast('9999-12-31' as date)) >= :date
2013-08-15 14:50:05,772 TRACE tomcat-http--4 org.hibernate.engine.spi.QueryParameters - Named parameters: {iisku=387671, date=2013-08-08}
2013-08-15 14:50:05,810 DEBUG tomcat-http--4 org.hibernate.SQL - select sfitem0_."iisku#" as iisku1_0_, sfitem0_."item_effdt" as item_eff2_0_, sfitem0_."created_at" as created_3_0_, sfitem0_."iidesc" as iidesc4_0_, sfitem0_."iipcst" as iipcst5_0_, sfitem0_."iivend" as iivend6_0_, sfitem0_."item_expdt" as item_exp7_0_ from sfitem sfitem0_ where sfitem0_."iisku#"=? and sfitem0_."item_effdt"<=? and coalesce(sfitem0_."item_expdt", cast('9999-12-31' as DATE))>=?
2013-08-15 14:50:05,832 DEBUG tomcat-http--4 org.hibernate.engine.jdbc.spi.SqlExceptionHelper - could not prepare statement [select sfitem0_."iisku#" as iisku1_0_, sfitem0_."item_effdt" as item_eff2_0_, sfitem0_."created_at" as created_3_0_, sfitem0_."iidesc" as iidesc4_0_, sfitem0_."iipcst" as iipcst5_0_, sfitem0_."iivend" as iivend6_0_, sfitem0_."item_expdt" as item_exp7_0_ from sfitem sfitem0_ where sfitem0_."iisku#"=? and sfitem0_."item_effdt"<=? and coalesce(sfitem0_."item_expdt", cast('9999-12-31' as DATE))>=?]
com.teradata.jdbc.jdbc_4.util.JDBCException: [Teradata Database] [TeraJDBC 14.00.00.21] [Error 3810] [SQLState 42S22] Column/Parameter 'DEV_DIG_UMT.sfitem0_.created_at' does not exist.
    at com.teradata.jdbc.jdbc_4.util.ErrorFactory.makeDatabaseSQLException(ErrorFactory.java:307)
    at com.teradata.jdbc.jdbc_4.statemachine.ReceiveInitSubState.action(ReceiveInitSubState.java:102)
    at com.teradata.jdbc.jdbc_4.statemachine.StatementReceiveState.subStateMachine(StatementReceiveState.java:320)
    at com.teradata.jdbc.jdbc_4.statemachine.StatementReceiveState.action(StatementReceiveState.java:201)
    at com.teradata.jdbc.jdbc_4.statemachine.StatementController.runBody(StatementController.java:121)
    at com.teradata.jdbc.jdbc_4.statemachine.StatementController.run(StatementController.java:112)
...
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:161)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:182)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:159)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1859)
        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1836)
        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1816)
        at org.hibernate.loader.Loader.doQuery(Loader.java:900)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)
        at org.hibernate.loader.Loader.doList(Loader.java:2526)
        at org.hibernate.loader.Loader.doList(Loader.java:2512)
        at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2342)
        at org.hibernate.loader.Loader.list(Loader.java:2337)
        at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:495)

这意味着休眠验证和查询执行程序的行为不同

具有正确字段的实体:

@Entity
public class Sfitem implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private SfitemPK id;

    @Column(name="\"iidesc\"")
    private String iidesc;

    @Column(name="\"iipcst\"")
    private BigDecimal iipcst;

    @Column(name="\"iivend\"")
    private BigDecimal iivend;

    @Column(name="\"item_expdt\"")
    private Date itemExpdt;

    @Column(name="\"row_insrt_tms\"")
    private Timestamp rowInsrtTms;

    @Column(name="\"row_updt_tms\"")
    private Timestamp rowUpdtTms;

    ...
}

Persistence.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.TeradataDialect"/>
            <!-- value="create" to build a new database on each run; value="update" to modify an existing database; value="create-drop" means the same as "create" but also drops tables when Hibernate closes; value="validate" makes no changes to the database -->
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
            <property name="hibernate.connection.charSet" value="UTF-8"/>
            <!-- Uncomment the following two properties for JBoss only -->
            <!-- property name="hibernate.validator.apply_to_ddl" value="false" /-->
            <!-- property name="hibernate.validator.autoregister_listeners" value="false" /-->
        </properties>
    </persistence-unit>
</persistence>

数据源和实体管理器 bean:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="${datasource.jndiName}"/>
  <property name="lookupOnStartup" value="true"/>
  <property name="resourceRef" value="true" />
</bean>

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

这是错误还是配置问题?有没有人遇到过同样的问题?

我不想在持久性单元或实体中配置默认​​模式,因为我们遵循的方法是通过使用在容器上下文。这样我们在部署到不同环境(Dev、QA、Prod 等)时无需担心

【问题讨论】:

    标签: java spring hibernate jpa teradata


    【解决方案1】:

    您可能需要在提交给 Teradata 的 SELECT 查询中完全限定您的表名。

    select sfitem0_."iisku#" as iisku1_0_, sfitem0_."item_effdt" as item_eff2_0_,
           sfitem0_."created_at" as created_3_0_, sfitem0_."iidesc" as iidesc4_0_,
           sfitem0_."iipcst" as iipcst5_0_, sfitem0_."iivend" as iivend6_0_,
           sfitem0_."item_expdt" as item_exp7_0_ 
     from DatDe001.SFITEM sfitem0_ /* Notice database name is included here */
    where sfitem0_."iisku#"=? 
      and sfitem0_."item_effdt"<=? 
      and coalesce(sfitem0_."item_expdt", cast('9999-12-31' as DATE))>=?
    

    编辑

    您还可以构造一个字符串,在每个 SELECT 语句之前执行,该语句指定您希望用作默认数据库的架构/数据库,用于查找 SQL 中不完全限定的对象:

    DATABASE=?
    

    然后可能使用参数来提供该值,就像您是 WHERE 子句的值一样

    编辑 2

    您只能为给定的连接字符串指定单个 DATABASE 参数。如果您的要求是允许支持应用程序前端的数据库使用不同的名称,您需要参数化应用程序需要在后端进行通信的 each 数据库的连接字符串。

    【讨论】:

    • 是的,这是使其指向正确数据库的方法之一,但正如我在帖子中提到的,我想将数据源配置保留在应用程序之外,因为这是一个企业应用程序部署在多个环境中的数据库名称在每个环境中都不同,并且可以在开发阶段进行更改。这个想法是 Hibernate 按预期从 URL 中提取数据库名称,但在运行模式验证器时不知何故它没有这样做。
    • 感谢 Rob,是的,可以以某种方式参数化模式名称,然后直接将其提供给持久性单元、实体或查询。但这会将数据源的配置再次附加到应用程序端。我正在寻找的是一种告诉 Hibernate 验证器从 jdbc url 中提取模式/数据库名称的方法: jdbc:teradata:///DATABASE=DEV_DIG_UMT... 就像在运行查询时完成的那样
    • 查看编辑 2。我想不出另一种方法来完成你想做的事情。也许其他人有更好的主意。
    • 嗨 Rob,关于您的第二次编辑。实际上应用程序只连接到一个数据库,我在连接 jdbc url 字符串中指定它,如问题帖子中所述。问题是休眠模式验证没有使用该数据库,而是服务器中包含具有该名称的表的第一个数据库,因为该数据库包含不同的版本,因此无法验证该表。
    猜你喜欢
    • 1970-01-01
    • 2014-02-12
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 2012-10-09
    相关资源
    最近更新 更多