一个动态数据库路由实现

需求:

业务库按机构+应用进行了分库,业务系统要求能根据机构+应用切换数据库连接.

实现:

系统将所有业务库的数据连接信息保存到一个路由数据库中的路由表里.

程序在需要连接具体的业务库时,可以查询路由表得到连接信息,并建立连接.

知识点:

spring JdbcTemplate JavaConfig

首先定义一个助手接口JDBCRouteHelper,提供给业务类使用.

下面是一个业务类使用JDBCRouteHelper查询业务库的方式:

 1     @Autowired
 2     JDBCRouteHelper jdbcRouteHelper;
 3     
 4     @Override
 5     public Map<String, Object> getItem(Map<String, Object> inParam) {
 6         if (MapUtil.getValue("id", inParam) == "")
 7             throw new InParamCheckException("id不能为空:" + inParam.get("id"));
 8         String orgId = MapUtil.getValue("org_id", inParam);
 9         String appId = IDConstants.APP_ID_PIS;
10         NamedParameterJdbcTemplate jdbc = jdbcRouteHelper.getJDBCTemplate(orgId, appId);
11 
12         return new MyMap().put("simple_info", jdbc.query("select * from simple_info where id=:id", inParam, new HashMapRowMapper()))
13                 .put("simple_test",
14                         jdbc.query("select * from simple_test where simple_info_id=:id", inParam, new HashMapRowMapper()))
15                 .getMap();
16     }
 1 public interface JDBCRouteHelper {
 2 
 3     /**
 4      * @param jdbcRouteTemplate
 5      * 设置路由配置数据库的连接
 6      */
 7     void setRouteJdbc(NamedParameterJdbcTemplate jdbcRouteTemplate);
 8     
 9     /**
10      * 
11      * @param orgId
12      * @param appId
13      * @return
14      * 得到指定机构与指定应用对应的数据库操作模板类
15      */
16     NamedParameterJdbcTemplate getJDBCTemplate(String orgId, String appId);
17     
18     /**
19      * @param orgId
20      * @param appId
21      * @return
22      * 得到指定机构与指定应用对应的数据库事务模版类
23      */
24     public TransactionTemplate getTransactionTemplate(String orgId, String appId);
25 
26 }

接口实现类

public class JDBCRouteHelperImp implements JDBCRouteHelper {

    //缓存所有业务数据库的jdbc模版
    private final static Map<String, NamedParameterJdbcTemplate> mapJdbc = new ConcurrentHashMap<>();
    //缓存所有业务数据库的datasource
    private final static Map<String, DataSource> mapDataSource = new ConcurrentHashMap<>();
    //缓存所有业务数据库的事务模版
    private final static Map<String, TransactionTemplate> mapTx = new ConcurrentHashMap<>();
    private final static Object lockDS = new Object();
    private final static Object lockJDBC = new Object();
    private final static Object lockTX = new Object();
    //路由配置库的连接
    NamedParameterJdbcTemplate jdbcRouteTemplate;
    //查询路由配置库的连接属性
    static String sqlGetDS = "select * from bas_datasource where " + "org_id=:orgId and app_id=:appId and dbs_type=1";
    
    JDBCRouteHelperImp(NamedParameterJdbcTemplate jdbcRouteTemplate) {
        this.jdbcRouteTemplate = jdbcRouteTemplate;
    }

    @Override
    public void setRouteJdbc(NamedParameterJdbcTemplate jdbcRouteTemplate) {
        this.jdbcRouteTemplate = jdbcRouteTemplate;
    }

    @Override
    public NamedParameterJdbcTemplate getJDBCTemplate(String orgId, String appId) {
        String dbKey = orgId + "_" + appId;
        if (mapJdbc.get(dbKey) == null) {
            synchronized (lockJDBC) {
                if (mapJdbc.get(dbKey) == null) {
                    DataSource dataSource = getDataSource(orgId, appId);
                    NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
                    mapJdbc.put(dbKey, jdbcTemplate);
                }
            }
        }

        return mapJdbc.get(dbKey);
    }
    
    @Override
    public TransactionTemplate getTransactionTemplate(String orgId, String appId) {
        String dbKey = orgId + "_" + appId;
        if (mapTx.get(dbKey) == null) {
            synchronized (lockTX) {
                if (mapTx.get(dbKey) == null) {
                    DataSource dataSource = getDataSource(orgId, appId);
                    DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);
                    TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);
                    mapTx.put(dbKey, transactionTemplate);
                }
            }
        }
        return mapTx.get(dbKey);

    }
    
    /**
     * @param orgId
     * @param appId
     * @return
     * 得到指定机构与指定应用对应的DataSource
     */
    private DataSource getDataSource(String orgId, String appId) {
        String dbKey = orgId + "_" + appId;
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("orgId", orgId);
        paramMap.put("appId", appId);
        List<Map<String, Object>> lst = jdbcRouteTemplate.query(sqlGetDS, paramMap, new ColumnMapRowMapper());
        if (!lst.isEmpty()) {
            Map<String, Object> dsItem = lst.get(0);
            BasicDataSource dataSource = new BasicDataSource();
            dataSource.setDriverClassName((String) dsItem.get("DBS_DriverClassName"));
            dataSource.setUrl((String) dsItem.get("DBS_URL"));
            dataSource.setUsername((String) dsItem.get("DBS_UserName"));
            dataSource.setPassword((String) dsItem.get("DBS_Password"));
            mapDataSource.put(dbKey, dataSource);
        } else {
            throw new RuntimeException("在配置库中没有发现数据库连接配置:orgId:[" + orgId + "]  appId:" + appId);
        }
        return mapDataSource.get(dbKey);
    }

}

通过spring 的JavaConfig将实现类放入容器

@Configuration
@PropertySource("classpath:mysql_route.properties")
public class JDBCRouteConfig {
    @Autowired
    Environment env;

    @Bean("jdbcRouteHelper")
    JDBCRouteHelper routeHelper() {
        BasicDataSource dsRoute = new BasicDataSource();
        dsRoute.setDriverClassName(env.getProperty("mysql.driverClassName"));
        dsRoute.setUrl(env.getProperty("mysql.url"));
        dsRoute.setUsername(env.getProperty("mysql.username"));
        dsRoute.setPassword(env.getProperty("mysql.password"));
        NamedParameterJdbcTemplate jdbcRouteTemplate = new NamedParameterJdbcTemplate(dsRoute);
        return new JDBCRouteHelperImp(jdbcRouteTemplate);
    }

}

业务系统加载helper的bean

 1 public class App 
 2 {
 3     public static void main( String[] args ) throws IOException
 4     {
 5         @SuppressWarnings("resource")
 6         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 7         ctx.register(JDBCRouteConfig.class);
 8         ctx.refresh();
 9         System.out.println("任意键退出");
10         System.in.read();
11     }
12 }

如果是web容器,也可以在web.xml中配置加载:

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.linsen.jbdc.JdbcRouteConfig
</context-param>

<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>

这里只是展示实现机制,如果是用于开发框架,进一步封装可以不用业务方法直接调用获取jdbc模版类方法,而是有框架封装好,业务类直接注入框架组装好的jdbc模版类.实现思路是在每个请求的filter中获取具体业务库的jdbc模板类,并刷到spring容器中.

转载于:https://www.cnblogs.com/reachlins/p/6553554.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot 和 Vue 结合使用,可以通过 Element UI 来实现动态由。 具体步骤如下: 1. 在 Spring Boot 中,定义一个 API 接口,用于返回动态由的配置信息,例如菜单列表、权限信息等。 2. 在 Vue 中,使用 axios 等工具调用该 API 接口获取动态由的配置信息。 3. 在 Vue 中,使用 vue-router 来实现由功能。在由配置中,使用获取到的动态由信息来动态生成由。例如,使用菜单列表来生成菜单由,使用权限信息来控制由的访问权限等。 4. 在 Vue 中,结合 Element UI 的组件,可以实现一些常见的由功能,例如面包屑导航、菜单栏等。 综上所述,通过 Spring Boot、Vue 和 Element UI 的结合,可以实现动态由功能,从而实现更加灵活、可扩展的前端页面管理。 ### 回答2: Spring Boot是一个开发框架,Vue是一个前端框架,Element UI是一个UI组件库,如何实现动态由呢? 首先,在Spring Boot后端,需要定义一个接口来获取动态由的数据。可以使用一个数据库表来存储由信息,如径、组件名称、图标等。然后,通过编写一个控制器类,来处理获取动态由数据的请求。在该控制器类中,可以调用相应的服务类或数据访问层来获取由数据,并返回给前端。 接下来,在Vue前端项目中,可以使用Vue Router来实现动态由。可以在项目的入口文件(如main.js)中,通过发送请求获取动态由数据。可以使用axios等库来发送请求,获取后端返回的动态由数据。获取到数据后,可以通过遍历的方式,动态地把由配置项添加到Vue Router中。 同时,在项目中引入Element UI组件库,可以使用其中的菜单、导航等组件,来展示动态由。可以根据获取的动态由数据,来生成菜单和导航栏的数据,并将其展示在页面中。 为了实现动态由的跳转,可以使用Vue Router中的由守卫(如beforeEach),在由跳转之前判断是否有权限访问该由。可以根据当前用户的权限信息,来判断是否有权限访问该由。如果没有权限,则可以跳转到其他页面或者显示相应的提示信息。 总结来说,通过在Spring Boot后端定义接口获取动态由数据,并在Vue前端项目中将其配置到Vue Router中,配合使用Element UI的菜单、导航组件,就可以实现Spring Boot、Vue和Element UI的动态由。 ### 回答3: 要实现动态由,我们可以结合使用Spring Boot、Vue和Element UI。 首先,在Spring Boot后端,我们需要建立一个API接口,用于获取动态由的数据。这个接口可以返回一个JSON对象,包含了多个由对象的信息,如由名称、径、组件等。 接下来,在Vue前端,我们可以使用Element UI的导航菜单组件来实现动态由。首先,我们需要在Vue项目中安装Element UI,并引入导航菜单组件。然后,在主页面组件中,我们可以通过调用后端的API接口获取动态由数据,然后根据返回的数据动态生成导航菜单。可以使用Vue Router来管理由,并使用 `<router-view>` 标签来展示对应的页面组件。 在生成导航菜单时,我们可以使用递归组件来实现无限嵌套的导航菜单结构。每个导航菜单项可以绑定点击事件,当用户点击菜单项时,可以通过Vue Router进行由跳转,展示对应的页面组件。 为了保证由权限控制,我们可以在后端API接口中加入用户权限验证的逻辑。在前端,我们可以根据用户的角色或权限信息动态生成导航菜单,只展示用户有权限访问的由。 总结来说,使用Spring Boot提供API接口获取动态由数据,然后在Vue前端使用Element UI的导航菜单组件构建动态由。通过递归组件生成无限嵌套的导航菜单,并通过Vue Router实现由跳转。同时,可以结合用户权限信息进行由权限控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值