从 Java 使用 @Transactional 方法手动创建 Spring @Service 实例

Create Spring @Service instance with @Transactional methods manually from Java

假设有 @Service@Repository 接口,如下所示:

@Repository
public interface OrderDao extends JpaRepository<Order, Integer> {

}

public interface OrderService {

    void saveOrder(Order order);

}

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

    @Override
    @Transactional
    public void saveOrder(Order order) {
        orderDao.save(order);
    }

}

这是工作应用程序的一部分,一切都配置为访问单个数据库,一切正常。

现在,我希望有可能 使用自动连接的 OrderDao 使用 pure Java[创建 OrderService 的独立工作实例=51=] 在 Java 代码 中指定了 jdbcUrl,像这样:

final int tenantId = 3578;
final String jdbcUrl = "jdbc:mysql://localhost:3306/database_" + tenantId;
OrderService orderService = someMethodWithSpringMagic(appContext, jdbcUrl);

如您所见,我想介绍 多租户架构 以及 每个数据库一个租户 策略到现有 Spring基于应用程序。

请注意,在使用自我实现的类似 jdbcTemplate 的逻辑以及 JDBC 事务正常工作之前,我可以很容易地 实现 ,所以这是 非常有效的任务

另请注意,我需要非常简单的事务逻辑来启动事务,在该事务范围内的服务方法中执行多个请求,然后在出现异常时提交it/rollback .

Web 上关于 Spring 多租户的大多数解决方案建议在 xml 配置 AND/OR 中使用基于注解的配置指定具体的持久性单元 不灵活 因为为了添加新数据库 url 整个应用程序应该停止,xml config/annotation 代码应该更改并启动应用程序。

所以,基本上我正在寻找一段能够创建 @Service 的代码,就像 Spring 在从 XML 配置/注释中读取属性后在内部创建它一样.我也在考虑为此使用 ProxyBeanFactory,因为 Spring 使用 AOP 来创建服务实例(所以我想简单的好旧的可重用 OOP 不是去这里的方法).

Spring是否足够灵活以允许这种相对简单的代码重用案例

任何提示将不胜感激,如果我找到了这个问题的完整答案,我会post在这里为后代提供:)

为带注释的服务创建事务代理并不是一项艰巨的任务,但我不确定您是否真的需要它。要为 tenantId 选择数据库,我猜你只需要专注于 DataSource 界面。

例如,使用简单的驱动程序管理的数据源:

public class MultitenancyDriverManagerDataSource extends DriverManagerDataSource {

    @Override
    protected Connection getConnectionFromDriverManager(String url,
            Properties props) throws SQLException {

        Integer tenant = MultitenancyContext.getTenantId();

        if (tenant != null)
            url += "_" + tenant;

        return super.getConnectionFromDriverManager(url, props);
    }

}

public class MultitenancyContext {

    private static ThreadLocal<Integer> tenant = new ThreadLocal<Integer>();

    public static Integer getTenantId() {
        return tenant.get();
    }

    public static void setTenatId(Integer value) {
        tenant.set(value);
    }
}

当然,如果要使用连接池,则需要详细说明一下,例如每个租户使用一个连接池。

HIbernate 有 out of the box support for multi tenancy, check that out before trying your own. Hibernate requires a MultiTenantConnectionProvider and CurrentTenantIdentifierResolver,其中有开箱即用的默认实现,但您始终可以编写自己的实现。如果它只是模式更改,那么实现起来实际上非常简单(在返回连接之前执行查询)。否则持有一个数据源映射并从中获取一个实例,或者创建一个新实例。

大约 8 年前,我们已经编写了一个通用解决方案,并记录在案 here and the code is here。它不是专门针对休眠的,基本上可以与您需要切换的任何东西一起使用。我们将它用于 DataSources 以及一些网络相关的东西(主题等等)。