项目中有两个数据源时原生查询的问题

问题描述 投票:0回答:1

当项目只有一个数据源时,原生查询运行良好,现在当有两个数据源时,hibernate 无法确定接收原生查询的模式,非原生查询运行良好。

application.yaml

spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  flyway:
    test:
      testLocations: classpath:/db_test/migration,classpath:/db_test/migration_test
      testSchemas: my_schema
    locations: classpath:/db_test/migration
    baselineOnMigrate: true
    schemas: my_schema
  jpa:
    packages-to-scan: example.example1.example2.example3.example4
    show-sql: false
    properties:
      hibernate.dialect: org.hibernate.dialect.PostgreSQL10Dialect
      hibernate.format_sql: false
      hibernate.jdbc.batch_size: 50
      hibernate.order_inserts: true
      hibernate.order_updates: true
      hibernate.generate_statistics: false
      hibernate.prepare_connection: false
      hibernate.default_schema: my_schema
      org.hibernate.envers:
        audit_table_prefix: log_
        audit_table_suffix:
      hibernate.javax.cache.uri: classpath:/ehcache.xml
      hibernate.cache:
        use_second_level_cache: true
        region.factory_class: org.hibernate.cache.ehcache.internal.SingletonEhcacheRegionFactory
      hibernate:
        connection:
          provider_disables_autocommit: true
          handling_mode: DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION
    hibernate.ddl-auto: validate
#    todo:
    open-in-view: false
    database-platform: org.hibernate.dialect.PostgreSQL10Dialect

  #database connections
  read-only:
    datasource:
      url: jdbc:postgresql://localhost:6432/db
      username: postgres
      password: postgres
      configuration:
        pool-name: read-only-pool
        read-only: true
        auto-commit: false
        schema: my_schema
  read-write:
    datasource:
      url: jdbc:postgresql://localhost:6433/db
      username: postgres
      password: postgres
      configuration:
        pool-name: read-write-pool
        auto-commit: false
        schema: my_schema

数据源配置:

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.read-write.datasource")
    public DataSourceProperties readWriteDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("spring.read-only.datasource")
    public DataSourceProperties readOnlyDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("spring.read-only.datasource.configuration")
    public DataSource readOnlyDataSource(DataSourceProperties readOnlyDataSourceProperties) {
        return readOnlyDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }


    @Bean
    @ConfigurationProperties("spring.read-write.datasource.configuration")
    public DataSource readWriteDataSource(DataSourceProperties readWriteDataSourceProperties) {
        return  readWriteDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @Primary
    public RoutingDataSource routingDataSource(DataSource readWriteDataSource, DataSource readOnlyDataSource) {
        RoutingDataSource routingDataSource = new RoutingDataSource();

        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put(DataSourceType.READ_WRITE, readWriteDataSource);
        dataSourceMap.put(DataSourceType.READ_ONLY, readOnlyDataSource);
        routingDataSource.setTargetDataSources(dataSourceMap);
        routingDataSource.setDefaultTargetDataSource(readWriteDataSource);

        return routingDataSource;
    }

    @Bean
    public BeanPostProcessor dialectProcessor() {

        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof HibernateJpaVendorAdapter) {
                    ((HibernateJpaVendorAdapter) bean).getJpaDialect().setPrepareConnection(false);
                }
                return bean;
            }
        };
    }
}

路由数据源

public class RoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceTypeContextHolder.getTransactionType();
    }

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        afterPropertiesSet();
    }
}

根据事务类型只读或非,选择数据源

public class DataSourceTypeContextHolder {
    private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();

    public static void setTransactionType(DataSourceType dataSource) {
        contextHolder.set(dataSource);
    }

    public static DataSourceType getTransactionType() {
        return contextHolder.get();
    }

    public static void clearTransactionType() {
        contextHolder.remove();
    }
}
@Aspect
@Component
@Slf4j
public class TransactionAspect {

    @Before("@annotation(transactional) && execution(* *(..))")
    public void setTransactionType(Transactional transactional) {
        if (transactional.readOnly()) {
            DataSourceTypeContextHolder.setTransactionType(DataSourceType.READ_ONLY);
        } else {
            DataSourceTypeContextHolder.setTransactionType(DataSourceType.READ_WRITE);
        }
    }

    @AfterReturning("@annotation(transactional) && execution(* *(..))")
    public void clearTransactionType(Transactional transactional) {
        DataSourceTypeContextHolder.clearTransactionType();
    }
}

错误

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [UPDATE my_table SET lock_until = timezone('utc', CURRENT_TIMESTAMP) + cast(? as interval), locked_at = timezone('utc', CURRENT_TIMESTAMP), locked_by = ? WHERE my_table.name = ? AND my_table.lock_until <= timezone('utc', CURRENT_TIMESTAMP)]; nested exception is org.postgresql.util.PSQLException: ERROR: relation "my_table" does not exist
  Позиция: 8
    at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:235)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:862)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:883)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:321)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:326)
    at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.lambda$execute$0(JdbcTemplateStorageAccessor.java:115)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
    at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.execute(JdbcTemplateStorageAccessor.java:115)
    at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.updateRecord(JdbcTemplateStorageAccessor.java:81)
    at net.javacrumbs.shedlock.support.StorageBasedLockProvider.doLock(StorageBasedLockProvider.java:91)
    at net.javacrumbs.shedlock.support.StorageBasedLockProvider.lock(StorageBasedLockProvider.java:65)
    at jdk.internal.reflect.GeneratedMethodAccessor328.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205)
    at com.sun.proxy.$Proxy139.lock(Unknown Source)
    at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:63)
    at net.javacrumbs.shedlock.spring.aop.MethodProxyScheduledLockAdvisor$LockingInterceptor.invoke(MethodProxyScheduledLockAdvisor.java:86)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at example1.example2.example3.example3.example3.example3.example3.scheduler.ExampleContentSheduler$$EnhancerBySpringCGLIB$$631d68e1.loadData(<generated>)
    at jdk.internal.reflect.GeneratedMethodAccessor320.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.postgresql.util.PSQLException: ERROR: relation "my_table" does not exist

当我更改本机查询并在表名称之前指定架构时,查询会正常处理: UPDATE my_schema.my_table SET lock_until = timezone('utc', CURRENT_TIMESTAMP) + cast(? 作为间隔),locked_at = timezone('utc', CURRENT_TIMESTAMP),locked_by = ?哪里 my_schema.my_table.name = ?和 my_schema.my_table.lock_until <= timezone('utc', CURRENT_TIMESTAMP);

谢谢!

java spring postgresql spring-boot hibernate
1个回答
0
投票

您得到的 BadSqlGrammarException 是因为在预定义架构中找不到表“my_table”。正如您所说,使用架构修改本机查询,显式指定

my_schema.my_table
,它之所以有效,是因为您准确地告诉了在哪里可以找到该表。

当您有多个数据源时,每个数据源可以有不同的模式,如果您没有明确指定,Hibernate 将不知道要搜索哪个模式。

my_table

在 application.yaml 中,您已使用属性

my_schema

:
将默认架构定义为
hibernate.default_schema

spring: jpa: properties: hibernate.default_schema: my_schema
但是此选项仅适用于 Hibernate 特定代码,并且在运行本机 SQL 查询时不适用,因为 Hibernate 无法应用其功能,因为查询特定于数据库并且必须按原样执行。

对于本机查询,您应该始终在查询本身中指定架构。这就是 Hibernate 如何知道要使用哪个模式中的哪个表。

© www.soinside.com 2019 - 2024. All rights reserved.