当项目只有一个数据源时,原生查询运行良好,现在当有两个数据源时,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);
谢谢!
您得到的 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 如何知道要使用哪个模式中的哪个表。