问题
I'm getting a weird ORA-00942: table or view does not exist
exception while trying to execute database code via Spring JDBC template:
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$1.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]
Since an Oracle driver is used, I cannot really debug into what's happening in oracle.jdbc.driver.OraclePreparedStatementWrapper
as ojdbc8
source code is closed for developers.
The strange thing is that the query from Spring's JDBC template that goes into the Oracle driver, when extracted with its parameters during a debugging session and copied into an external SQL client as is works just fine there:
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 ;
and produces several records of a result.
This is the Spring repository in question:
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;
}
}
This is how DB is configured:
@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());
}
}
and this is the invocation of the use case:
curl --location --request GET 'http://localhost:7010/exs/acs/accounts-links?limit=20&q=632626&showActive=false&systemName=IMMS' \
--header 'Content-Type: application/json'
The table seems to be a synonym:
and the runtime user is WEBSVC_READ:
When querying DB directly, I'm using the same WEBSVC_READ schema and that works fine:
N.B.: The behavior seems to be related to the JDBC template only. The following JPA query runs fine:
"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' )";
with the following present in the application.properties:
spring.jpa.properties.hibernate.default_schema=BNYMACS
and the entities configured like so (no explicit schema):
@Entity(name="Account")
@Table(name="ACCOUNT")
public class Account implements Serializable{...}
@Entity
@Table(name="LINKS")
@Data
public class Links implements Serializable{...}
What might be wrong here? Is this ORA-00942
a meaningful error or just a placeholder for something else? Perhaps someone can give me useful pointers on approaches of getting to the bottom of it.
Thanks in advance.
回答1:
The culprit was the following incorrect and unnecessary definition in my Repository:
@Autowired
public void setDataSource(DataSource dataSource) {
this.template = new JdbcTemplate(dataSource);
}
It is already defined in the Configuration class and its redefining was causing all the havoc.
来源:https://stackoverflow.com/questions/59495461/ora-00942-table-or-view-does-not-exist-with-spring-jdbc-template-in-spring-boot