- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
大家好,我已按照本指南使用 Spring-Boot 实现 Multi-Tenancy 应用程序:
https://www.baeldung.com/spring-abstract-routing-data-source
一切正常,我使用拦截器来拦截http请求并根据我的业务逻辑设置tanant_A或tenant_B。在我的用例中,我只有一种场景,我必须设置tenant_A,在事务中保留此数据源上的数据,然后我必须使用相同的实体和存储库在租户B上保留相同的数据(tenant_B是tenant_A的副本)。
我的 REST Controller 示例:
@RequestMapping(method = RequestMethod.POST,
path = "/save",
produces = MediaType.APPLICATION_JSON_VALUE)
Optional<StatusMessage> create(@RequestBody MyResource resource){
MyEntity a = mapper.resourceToEntity(resource);
service.saveToA(a); /*Trasactional @Service use default dataSource A */
TenantContext.clearTenantType();
TenantContext.setCurrentTenant(DatasourceType.TENANT_B);
service.saveToB(a); /*Trasactional @Service use dataSource B */
return Optional.of(new StatusMessage("200","saved"));
}
服务:
@Service
public class MyService implements IMyService {
@Autowired
private MyEntityRepository repository;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveToA(MyEntity a) {
repository.save(a);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveToB(MyEntity a) {
repository.save(e);
}
存储库:
@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity ,String> {}
第一个持久性事务始终有效。如果我更改持久化顺序并在tenant_A之后设置tenant_B,则数据将保留在我设置的第一个租户中,但不会保留在第二个租户中。
似乎第二个事务方法调用了我的 AbstractRoutingDataSource 实现的 certainCurrentLookupKey 方法,但服务内的存储库继续始终使用我设置的第一个租户。
奇怪的是,如果我不在 REST Controller 内重复此步骤,而是从一个简单的 main 方法重复此步骤,则在调用 @Transactional 方法时数据源会正确切换。
你有什么建议吗?
谢谢。
完成信息:
@Configuration
public class MultitenantConfiguration {
@Value("${A.JDBC.USERNAME}")
private String username;
@Value("${A.JDBC.PASSWORD}")
private String password;
@Value("${A.JDBC.CONNECTIONURL}")
private String url;
@Value("${B.JDBC.USERNAME}")
private String username_stg;
@Value("${B.JDBC.PASSWORD}")
private String password_stg;
@Value("${B.JDBC.CONNECTIONURL}")
private String url_stg;
@Primary
@Bean
public DataSource dataSource() {
MultitenantDataSourceRouter dataSource = new MultitenantDataSourceRouter();
Map<Object, Object> resolvedDataSources = new HashMap<>();
resolvedDataSources.put(DatasourceType.TENANT_A, dataSourceMaster());
resolvedDataSources.put(DatasourceType.TENANT_B, dataSourceSlave());
dataSource.setTargetDataSources(resolvedDataSources);
dataSource.setDefaultTargetDataSource(dataSourceMaster());
dataSource.afterPropertiesSet();
return dataSource;
}
public DataSource dataSourceMaster() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUrl(url);
return dataSource;
}
public DataSource dataSourceSlave() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUsername(username_stg);
dataSource.setPassword(password_stg);
dataSource.setUrl(url_stg);
return dataSource;
}
@Bean
DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager txm = new DataSourceTransactionManager(dataSource());
return txm;
}
这是我的 certainCurrentLookupKey 实现
public class MultitenantDataSourceRouter extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(MultitenantDataSourceRouter.class);
@Override
public Object determineCurrentLookupKey() {
log.info(">>> determineCurrentLookupKey thread: {},{}",Thread.currentThread().getId(), Thread.currentThread().getName() );
log.info(">>> RoutingDataSource: {}", TenantContext.getCurrentTenant());
return TenantContext.getCurrentTenant();
}
}
我的ThreadLocal
public class TenantContext {
private static ThreadLocal<DatasourceType> currentTenant = new ThreadLocal<>();
public static void setCurrentTenant(DatasourceType tenant) {
currentTenant.set(tenant);
}
public static DatasourceType getCurrentTenant() {
return currentTenant.get();
}
public static void clearTenantType() {
currentTenant.remove();
}
}
如果我运行这个简单的测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class FornitorePaylineInvokerIT{
@Autowired
private CreatorController creatorController;
@Test
public void execute() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
MyResource resource = objectMapper.readValue(request.toString(), MyResource.class);
Optional<StatusMessage> result = creatorController.create(resource);
System.out.println(result);
}
我在日志 4 中看到对 certainCurrentLookupKey 的调用:
MultitenantDataSourceRouter;28/11/2018 13:05:33,487;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:33,487;>>> RoutingDataSource: TENANT:A
MyService;28/11/2018 13:05:33,597;[BEFORE] com.services.MyService.save[MyEntity...]
MyService;28/11/2018 13:05:33,597;>>> MyService thread: 1,main
MyService;28/11/2018 13:05:33,597;MyService START on: TENANT_A
MultitenantDataSourceRouter;28/11/2018 13:05:33,644;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:33,644;>>> RoutingDataSource: PRODUZIONE
Service;28/11/2018 13:05:34,003;[AFTER] cMyService.save[MyEntity
MultitenantDataSourceRouter;28/11/2018 13:05:34,018;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:34,018;>>> RoutingDataSource: TENANT_B
MyService;28/11/2018 13:05:34,081;[BEFORE] MyService.save[MyEntity..]
MyService;28/11/2018 13:05:34,081;>>> MyService thread: 1,main
MyService;28/11/2018 13:05:34,081;MyService START on: TENANT_B
MultitenantDataSourceRouter;28/11/2018 13:05:34,081;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:34,081;>>> RoutingDataSource: TENANT_B
MyService;28/11/2018 13:05:34,288;[AFTER] com.cervedgroup.viscus.services.MyService.save[MyEntity..]
MyController;28/11/2018 13:05:34,297;[AFTER] MyController.create[MyResource...]
如果我通过 http 调用从 Controller 调用相同的服务
MultitenantDataSourceRouter;28/11/2018 13:05:33,487;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:33,487;>>> RoutingDataSource: TENANT:A
MyService;28/11/2018 13:05:33,597;[BEFORE] com.services.MyService.save[MyEntity...]
MyService;28/11/2018 13:05:33,597;>>> MyService thread: 1,main
MyService;28/11/2018 13:05:33,597;MyService START on: TENANT_A
MultitenantDataSourceRouter;28/11/2018 13:05:33,644;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:33,644;>>> RoutingDataSource: PRODUZIONE
Service;28/11/2018 13:05:34,003;[AFTER] cMyService.save[MyEntity
MultitenantDataSourceRouter;28/11/2018 13:05:34,018;>>> determineCurrentLookupKey thread: 1,main
MultitenantDataSourceRouter;28/11/2018 13:05:34,018;>>> RoutingDataSource: TENANT_B
MyService;28/11/2018 13:05:34,081;[BEFORE] MyService.save[MyEntity..]
MyService;28/11/2018 13:05:34,081;>>> MyService thread: 1,main
MyService;28/11/2018 13:05:34,081;MyService START on: TENANT_B
MyService;28/11/2018 13:05:34,288;[AFTER] com.cervedgroup.viscus.services.MyService.save[MyEntity..]
MyController;28/11/2018 13:05:34,297;[AFTER] MyController.create[MyResource...]
最佳答案
问题出在我的存储库类上:
@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity ,String> {}
如果我使用 JdbcTemplate 进行低级实现,效果很好:
@Repository
public class MyEntityRepository {
private final JdbcTemplate jdbcTemplate;
public MyEntityRepository(DataSource datasource) {
this.jdbcTemplate = new JdbcTemplate(datasource);
}
public void save(MyEntity e) {
jdbcTemplate.update("INSERT INTO TABLE (PARAM_A, PARAM_B) VALUES(?,?)",
new Object[]{e.getParamA(), e.getParamB()});
}
}
我在一次 REST 调用中将相同的信息保存到 2 个数据库 MASTER 和 REPLICA 中。
我不知道如何让 JpaRepository 在处理第二个调用时重新调用 getConnection。
关于java - REST POST 服务使用 AbstractRoutingDataSource 持久保存到两个不同的数据源中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53490700/
我试图通过预准备语句使用同一连接执行多个查询,但无法完全实现! 代码片段: public class PostPrReqDaoImpl implements PostPrReqDaoInterface
我目前有一个 2 列宽的 DataGridView,第一列是 DataGridViewTextBoxColumn,第二列是 DataGridViewComboBoxColumn。我还有一个预生成的通用
当我在一台机器上运行以下代码时,我得到了 org.apache.tomcat.dbcp.dbcp.BasicDataSource 的 tomcat 实现,当我在另一台机器上运行它时,我得到了 org.
不确定这是否可行,但这是我的设置。 我有一台带有双启动功能的笔记本电脑。 一个一个分区我有 WinXP 和 MSAccess 2000在另一个分区上,Ubuntu 10.04,带有 apache we
我试过: czmlDataSource.load(czmlurl).then(function(){ viewer.dataSource
我有一个 TableView 和一个数组源。当我在 viewDidLoad 方法中初始化数组时,tableview 显示数组中的数据。当我从 Internet 上的 XML 数据的 URL 填充数组时
我对 DataSource 和 SessionFactory 之间的区别感到困惑。 我认为SessionFactory是一个用于检索 session 的管理器(我猜这实际上是与数据库的连接)。 Dat
我想存储大量(~数千)个字符串并能够使用通配符执行匹配。 例如,这里是一个示例内容: Folder1 文件夹 1/Folder2 Folder1/* Folder1/Folder2/Folder3 文
我有一个 DataGridView 和一个从 SQL 表填充的一些对象的列表。我曾使用两种方法将列表绑定(bind)到网格。 1.直接使用列表到数据源 grdSomeList.DataSource =
我正在尝试在 DataGridView 中设置一些内容。看起来这应该很简单,但我遇到了麻烦。我想显示三列: 代码ID 代号 带有 TypeName 的 DisplayMember 和 TypeID 的
在我的 Config.groovy我把线: grails.config.locations = [ "classpath:app-config.properties"] 我在哪里设置数据源的定义。文件
为了这个问题,假设我有一个包含各种酒类的 Excel 数据源电子表格。 (Cell A) | (Cell B) Bacardi | Rum Smirnoff | Vodka Another Vodka
由于我经常使用第三方 API,我认为创建一些 Magento 模块以实现轻松连接和查询它们会很有帮助。理想情况下,您可以像这样查询 API... $data = Mage::getModel( 'to
将后台线程频繁更新的数据源与 GUI 主线程同步的最佳方法是什么? 我应该在每个方法调用周围放置一个 pthread 互斥体吗?这对我来说似乎也很重。 编辑:我正在寻找 10.5 解决方案 最佳答案
经过几个小时的点击和试用,在查看各种帖子寻求帮助后,这段代码终于起作用了。但我希望有人帮助我理解函数(i,dat),这意味着什么?下面是我的完整代码 - function get_assignedta
我使用的是 Wildfly 10.1 版本,有两个数据源,如下所示, jdbc:mysql://${dbhostn
我正在学习数据源,我想我开始理解它,但我不明白这一段。 据我所知,MySQL 和 PostgreSQL 等数据库供应商编写了自己的不同 DataSource 接口(interface)的实现。现在,这
我有一个关于 TomEE 和使用 tomee.xml 中指定的数据源的奇怪问题。值得注意的是,我使用的是 Netbeans、TomEE 和 MySQL。在 Ubuntu 13.04(Xubuntu 最
WWDC 2019 确实充满了 iOS 的新内容以及 TableViews 和 CollectionView 的新数据源,即 UITableViewDiffableDataSource . 我已成功将
我在独立模式下运行 jboss 并将 standalone.xml 中的数据源设置为以下内容: jdbc:sqlserver://myip:1433;databaseNam
我是一名优秀的程序员,十分优秀!