ORA-00942: table 或 Spring JDBC 模板在 Spring 引导中不存在视图

ORA-00942: table or view does not exist with Spring JDBC Template in Spring Boot

我在尝试通过 Spring JDBC 模板执行数据库代码时遇到奇怪的 ORA-00942: table or view does not exist 异常:

2019-12-26 22:01:36.863[0;39m [31mERROR[0;39m [35m12232[0;39m [2m---[0;39m [2m[ctor-http-nio-3][0;39m [36ma.w.r.e.AbstractErrorWebExceptionHandler[0;39m [2m:[0;39m [ca8305eb] 500 Server Error for HTTP GET "/exs/acs/accounts-links?limit=20&q=632626&showActive=false&systemName=IMMS"

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select a.FACCIN, a.FACCKEY, a.FACCSNAME, b.LNKSYSTEM, b.LNKLOANKEY, a.FACCSTATUS, a.FACCCOND from BNYMACS.ACCOUNT a left outer join BNYMACS.LINKS b on a.FACCIN = b.lnkacc where (upper(a.FACCKEY) like ? or upper(FACCSNAME) like ? or (b.LNKLOANKEY like ? )) and b.LNKSYSTEM =?  and rownum<=? order by 3 ]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist

    at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:235) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ? org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
    |_ checkpoint ? HTTP GET "/exs/acs/accounts-links?limit=20&q=632626&showActive=false&systemName=IMMS" [ExceptionHandlingWebHandler]
Stack trace:
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:235) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]

。 . . . . . .

Caused by: java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist

    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:509) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:461) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1104) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:550) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:268) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:655) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:270) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:91) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:807) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:983) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1168) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3666) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.T4CPreparedStatement.executeInternal(T4CPreparedStatement.java:1426) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3713) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1167) ~[ojdbc8-19.3.0.0.jar:19.3.0.0.0]
    at com.p6spy.engine.wrapper.PreparedStatementWrapper.executeQuery(PreparedStatementWrapper.java:78) ~[p6spy-3.8.2.jar:na]
    at org.springframework.jdbc.core.JdbcTemplate.doInPreparedStatement(JdbcTemplate.java:678) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:669) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:700) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:712) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:763) ~[spring-jdbc-5.2.0.RELEASE.jar:5.2.0.RELEASE]

由于使用了 Oracle 驱动程序,我无法真正调试到 oracle.jdbc.driver.OraclePreparedStatementWrapper 中发生的事情,因为 ojdbc8 源代码已对开发人员关闭。

奇怪的是,来自 Spring 的 JDBC 模板的查询进入 Oracle 驱动程序,当在调试会话期间使用其参数提取并复制到外部 SQL 客户端在那里工作得很好:

select a.FACCIN, a.FACCKEY, a.FACCSNAME, b.LNKSYSTEM, b.LNKLOANKEY, a.FACCSTATUS, a.FACCCOND from BNYMACS.ACCOUNT a left outer join BNYMACS.LINKS b on a.FACCIN = b.lnkacc where (upper(a.FACCKEY) like '632626%' or upper(FACCSNAME) like '632626%' or (b.LNKLOANKEY like '632626%' )) and b.LNKSYSTEM ='IMMS'  and rownum<=20 order by 3 ;

并生成多个结果记录。

这是有问题的 Spring 存储库:

import static cwp.services.adhoc_processor.domain.acs.Account.*;
import static cwp.services.adhoc_processor.domain.acs.Links.*;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;


import cwp.services.adhoc_processor.domain.acs.AccountLinkDetail;
import cwp.services.adhoc_processor.domain.acs.AccountLinkDetail.AccountLinkDetailBuilder;

import cwp.services.adhoc_processor.domain.acs.AccountLinksList;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AccountLinksRepositoryCustomImpl implements AccountLinksRepositoryCustom {

    private static final String TERMINATED = "TERMINATED";
    private static final String STOPPED = "STOPPED";

    private final static String STATIC_QUERY_PART = "select a.FACCIN, a.FACCKEY, a.FACCSNAME, b.LNKSYSTEM, b.LNKLOANKEY, a.FACCSTATUS, a.FACCCOND "
            + "from BNYMACS.ACCOUNT a left outer join BNYMACS.LINKS b "
            + "on a.FACCIN = b.lnkacc where (upper(a.FACCKEY) like ? or upper(FACCSNAME) like ? "
            + "or (b.LNKLOANKEY like ? )) and b.LNKSYSTEM =? ";

    private final static String activeSuffix = "and upper(a.FACCSTATUS) <> 'TERMINATED' and upper(a.FACCCOND) <> 'STOPPED' ";

    private final static String orderbyClause = " and rownum<=? order by 3 ";

    @Autowired
    @Qualifier("acsJdbcTemplate")
    private JdbcTemplate template;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.template = new JdbcTemplate(dataSource);
    }

    @Override
    public AccountLinksList searchByAccountOrLinks(String searchTerm, int limit, boolean isActive, String systemName) {
        AccountLinksList accountLinksList = new AccountLinksList();

        try {
            String dynQuery = STATIC_QUERY_PART;

            if (isActive)
                dynQuery += activeSuffix;

            dynQuery += orderbyClause;

            List<AccountLinkDetail>  accountDetailsList = template.<AccountLinkDetail>query(
                    dynQuery,
                    new Object[] { searchTerm, searchTerm, searchTerm, systemName, limit }, 
                    new RowMapper<AccountLinkDetail>() {
                        @Override
                        public AccountLinkDetail mapRow(ResultSet rs, int currentRow) throws SQLException {
                            AccountLinkDetailBuilder accountDetails = AccountLinkDetail.builder();
                            accountDetails
                                    .accountId(rs.getLong(ID_COLUMN_NAME))
                                    .accountKey(rs.getString(ACCOUNT_KEY_COLUMN_NAME))
                                    .accountName(rs.getString(ACCOUNT_SHORT_NAME_COLUMN_NAME))
                                    .loanKey(rs.getString(LOAN_KEY_COLUMN_NAME))
                                    .systemName(rs.getString(LINK_SYSTEM_COLUMN_NAME));
                            if (rs.getString(CONDITION_COLUMN_NAME) != null
                                    && !rs.getString(CONDITION_COLUMN_NAME).equalsIgnoreCase(STOPPED)
                                    && rs.getString(STATUS_COLUMN_NAME) != null
                                    && !rs.getString(STATUS_COLUMN_NAME).equalsIgnoreCase(TERMINATED)) {
                                accountDetails.isActive(true);
                            }
                            return accountDetails.build();
                        }



                    });
            accountLinksList.setAccountLinkDetail(accountDetailsList);
        } catch (Exception e) {
            log.error("Technical Exception", e);
            throw e;
        }
        return accountLinksList;
    }

}

数据库是这样配置的:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = {"cwp.services.adhoc_processor.repository.acs" }, 
        entityManagerFactoryRef = "acsEntityManagerFactory", 
        transactionManagerRef = "acsTransactionManager")

public class AcsDatasourceConfiguration {

    @Autowired
    private AcsDbProperties properties;

    @Validated
    @Component
    @ConfigurationProperties(prefix = "acs.read.datasource")
    public class AcsDbProperties extends HikariDBProperties {
    }

    @ConditionalOnBean(value = AcsDbProperties.class)
    @Bean(name = "acsDataSource", destroyMethod = "")
    public DataSource acsDataSource() {
        return new HikariDataSource(new HikariConfig(properties.getProperties()));
    }

    @Bean("acsJdbcTemplate")
    public JdbcTemplate pmtTemplate () throws Exception {
        return new JdbcTemplate(acsDataSource());
    }

    @Bean(name = "acsEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean acsEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(acsDataSource())
                .packages("cwp.services.adhoc_processor.domain.acs" )
                .persistenceUnit("acs")
                .build();
    }

    /*
     * Leaky requirement for the unsatisfied transaction manager bean stemming from nxn-workflow-services#NWFProcessEngineConfiguration.java dependency.
     * 
     * N.B. MUST BE CALLED THIS EXACT NAME, else will get 
     * org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.PlatformTransactionManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=transactionManager)}
     * 
     */
    @Bean("transactionManager") // <-- must be called `transactionManager`.
    public PlatformTransactionManager acsTransactionManager
            (
            @Qualifier("acsEntityManagerFactory")
            final  LocalContainerEntityManagerFactoryBean acsEntityManagerFactory
            ) {

                return new JpaTransactionManager(acsEntityManagerFactory.getObject());
    }


}

这是用例的调用:

curl --location --request GET 'http://localhost:7010/exs/acs/accounts-links?limit=20&q=632626&showActive=false&systemName=IMMS' \
--header 'Content-Type: application/json'

table好像是同义词:

并且运行时用户是 WEBSVC_READ:

当直接查询数据库时,我使用相同的 WEBSVC_READ 模式并且工作正常:

N.B.:该行为似乎只与 JDBC 模板有关。以下 JPA 查询运行良好:

"select  a from Account a, Links l where l.accountId = a.id and l.loanKey = :loanKey and l.linkSystem = :systemName and(upper(a.status) <> 'TERMINATED' or upper(a.condition) <>'STOPPED' )";

application.properties 中存在以下内容:

spring.jpa.properties.hibernate.default_schema=BNYMACS

以及像这样配置的实体(没有明确的架构):

@Entity(name="Account")
@Table(name="ACCOUNT")
public class Account implements Serializable{...}


@Entity
@Table(name="LINKS")
@Data
public class Links implements Serializable{...}

这里可能有什么问题?这是 ORA-00942 有意义的错误还是只是其他内容的占位符?也许有人可以给我有用的指示,让我了解如何深入了解它。

提前致谢。

罪魁祸首是我的存储库中的以下错误和不必要的定义:

@Autowired
public void setDataSource(DataSource dataSource) {
        this.template = new JdbcTemplate(dataSource);
    }

它已经在配置中定义 class 并且重新定义它造成了所有的破坏。