使用Java手动创建带有@Transactional方法的Spring @Service实例

时间:2015-05-08 12:48:30

标签: java spring transactions aop spring-aop

我们说有@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);
    }

}

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

现在,我希望能够使用纯Java 创建具有自动连线OrderDao 的独立工作实例,并使用Java中指定的 jdbcUrl代码,类似这样:

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

正如您所看到的,我想向现有的基于Spring的应用程序引入多租户架构每个数据库的租户策略。

请注意,在使用自我实现的jdbcTemplate类逻辑之前,我能够非常轻松地实现 ,并且JDBC事务正常工作,因此这是非常有效的任务

请注意,我需要非常简单的事务逻辑来启动事务,在该事务范围内的service方法中执行多个请求,然后在异常时提交/回滚。

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

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

Spring足够灵活允许这种相对简单的代码重用吗?

任何提示都将受到高度赞赏如果我找到这个问题的完整答案,我会在这里发布以供后代使用:)

2 个答案:

答案 0 :(得分:2)

为带注释的服务创建事务代理并不是一项艰巨的任务,但我不确定您是否真的需要它。要为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);
    }
}

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

答案 1 :(得分:2)

HIbernate有out of the box support for multi tenancy,在尝试自己之前检查一下。 Hibernate需要一个MultiTenantConnectionProviderCurrentTenantIdentifierResolver,其默认实现是开箱即用的,但您始终可以编写自己的实现。如果它只是一个模式更改,实际上很容易实现(在返回连接之前执行查询)。否则,保存数据源地图并从中获取实例,或创建新实例。

大约8年前,我们已经编写了一个通用解决方案,其中记录了here,代码为here。它不是特定于休眠的,并且可以基本上用于切换所需的任何内容。我们将它用于DataSource s以及一些与网络相关的事物(其中包括其他内容)。