NutzCN Logo
精华 根据用户多数据源切换问题
发布于 128天前 作者 naxxm 412 次浏览 复制 上一个帖子 下一个帖子
标签:

通过A数据源判断用户的数据源信息,然后SimpleDataSource链接到新B数据源,那么此时的dao是原来的A数据源还是新的B数据源呢?另外有什么好的解决思路没有,这样不通过连接池是不是对性能有影响呢?

public SimpleDataSource dataSourceChange(String company) throws ClassNotFoundException {		
		SysCompany companyDB  =  dao.fetch(SysCompany.class,Cnd.where("bm", "=", company));		
			
		if(companyDB != null){
			String[] DBInfo = companyDB.getDBInfo().split(",");
			String driverClassName = "net.sourceforge.jtds.jdbc.Driver";
			String jdbcUrl = DBInfo[0];
			String username = DBInfo[1];
			String password = DBInfo[2];
			
			SimpleDataSource ds = new SimpleDataSource();
			ds.setDriverClassName(driverClassName);
			ds.setJdbcUrl(jdbcUrl);
			ds.setUsername(username);
			ds.setPassword(password);
			
			System.out.println("------------------------------------------------------------------------------------------------");
			System.out.println(companyDB.getMc() + "---" + jdbcUrl + "---" + username + "---" + password + "---" + ds);
			System.out.println("------------------------------------------------------------------------------------------------");
			
			return ds;
		}
36 回复

实现DataSource, 在里面getConnection方法内, 按Mvcs.getReq()获取当前连接,然后决定返回何种连接

这样就可以完全自定义了, 我说明白了吗?

@wendal 没大明白,意思是我现在的就是两个链接,用dao操作的还是A数据源对吧,能有代码辅助一下吧,编程全自学,不懂得太多,万分感谢
好像手机版的坏了,没法回复帖子

那你声明2个数据源,2个dao好了

手机版一直都回复,我早上还发过

看下了这个好像不太符合需求,每个用户购买产品后都会新建一个新的数据库,这样操作的话,需要每次都要去配置dao吧?我在想能不能入口数据源A负责鉴别用户的所属数据源(一个用户对应一个数据库,就会有很多个数据库),用户自己去创建自己的数据源链接,就想SimpleDataSource 一样,建立连接后的操作就不再需要数据源A,而是所有的dao都指向自己的数据源N

@wendal 不知道我的描述是否清晰~

@wendal 这个URL能不能代码操作动态改变呢,这样是不是符合我的需求,针对用户来说用到的其实只有两个数据库,这个配置文件的url,username,password能否动态修改来实现我的需求呢?默认登陆的时候链接数据源A,验证通过后,直接修改url相关信息去链接自己的数据库B?这个是tomcat启动时就连接了吗?不知道能不能实现,求大神提供思路~

var ioc = {
	dataSource : {
		type : "com.alibaba.druid.pool.DruidDataSource",
		events : {
			create : "init",
			depose : 'close'
		},
		fields : {
			url : "jdbc:jtds:sqlserver://127.0.0.1:1433/testdb_sys",//jdbc:mysql://127.0.0.1:3306/testdb_sys
			username : "sa",
			password : "****",
			testWhileIdle : true,
			validationQuery : "select 1",
			maxActive : 100
		}
	},
	dao : {
		type : "org.nutz.dao.impl.NutDao",
		args : [ {
			refer : "dataSource"
		} ]
	}
};

你需要实现DataSource接口了,在返回连接之前判断用户信息

嗯, 明白思路很重要

@wendal 我重新写DataSource接口不是和SimpleDataSource 接口类似或者一样了吗?这样我的dao是判断用的数据源A还是用户数据源B呢? 在配置信息中dao绑定的是数据源A,我想判断用户后,让dao切换到数据源B(B有很多),有点晕了。。。

切换数据源,跟在数据源里面切换链接是一个道理

前者是 dao --1--N--> DataSource -- 1 -- 1 -- DB
后者是 dao -- 1-- 1 --> MyDataSource --1 -- N --> DataSource --1--1--- DB, 其中的DataSource可省略

给dao动态设置dataSource并非一个好主意, 所以建议后者

另外一种思路是 每个数据源一个 datatSource, 每个dataSource对应一个dao实例, 按用户取出对应的dao进行操作

@wendal

另外一种思路是 每个数据源一个 datatSource, 每个dataSource对应一个dao实例, 按用户取出对应的dao进行操作

这样操作后期用户量大了会很麻烦,每个用户对应一个数据源,后期用户多了,全要在配置文件维护了。。

不需要数据库维护哦

说错, 不需要配置文件哦

SimpleDataSource和NutDao都可以动态new嘛, 而数据库信息, 可以从主数据库取嘛

我现在想做的就是,比如一个公司买了产品后,只需要给他新建立一个数据库,然后在总数据库A中存入相应的数据库链接信息,用户登陆时根据填写的公司切换到自己数据库就好,切换数据库容易实现,问题是怎么操作切换后的数据库,还是原来的dao吗?

嗯, 通过Session里面放用户标识, 然后自定义DataSource,在getConnection里面判断用户标识, 读取对应的用户数据库连接信息, 建立连接,返回之

我一楼的代码用SimpleDataSource不是已经实现了切换数据源吗?不懂和自己再写个DataSource有什么不同之处呢?
返回ds如何用dao去操作呢?还是返回ds后直接已经替换了原来的数据源A吗?

前者是 dao --1--N--> DataSource -- 1 -- 1 -- DB
后者是 dao -- 1-- 1 --> MyDataSource --1 -- N --> DataSource --1--1--- DB, 其中的DataSource可省略

另外,这个前者后者是指的这个问题吗?先谢谢您的耐心回答~哈哈

你那做法, 中途有其他请求进来的时候, 算谁的连接信息

哦, 话说, 我是不是没完全理解你现在写的方法, 调用dataSourceChange的代码贴来看看?

用的shiro框架,判断数据源信息,在用户认证之前,即输入公司编码、用户信息后,点击登陆,先进行公司数据源信息匹配,然后再走shiro的认证授权,这样每个用户只有一个对应的返回的ds吧?没有中途请求进来吧。。。

package com.naxxm.shiro.handlers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.adaptor.JsonAdaptor;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;

import com.alibaba.fastjson.JSONObject;
import com.naxxm.service.DataService;
import com.naxxm.utils.JSONResult;
import com.naxxm.utils.LeeJSONResult;

@IocBean
@At("/naxxm")
/**
 * @author liuyc 
 * */
public class TestCustomRealmMd5 {
	public static final Log Log = Logs.get();

	@Inject
	private DataService dataService;

	@At("/login")
	@Ok("json")
	@AdaptBy(type = JsonAdaptor.class)
	public JSONResult login(@Param("..") JSONObject params, HttpServletRequest request) throws Exception {
		System.out.println(LeeJSONResult.ok());
		String company = params.getString("company");
		String username = params.getString("username");
		String password = params.getString("password");
		Boolean rememberMe = params.getBooleanValue("rememberMe");

		System.out.println("--->步骤一:公司编码:" + company + "用户输入用户名:" + username + ",密码:" + password);
		if (StringUtils.isBlank(company)) {
			return JSONResult.errorMsg("公司编码不能为空!");
		}
		if (StringUtils.isBlank(username)) {
			return JSONResult.errorMsg("用户名不能为空!");
		}
		if (StringUtils.isBlank(password)) {
			return JSONResult.errorMsg("密码不能为空!");
		}

		// 验证公司数据源信息,并切换到对应数据源
		if (dataService.dataSourceChange(company) == null) {
			// 还没有写,不知如何dao与返回数据源关联
			return null;
		}
		;

		Subject currentUser = SecurityUtils.getSubject();
		if (!currentUser.isAuthenticated()) {
			// 把用户名和密码封装为 UsernamePasswordToken 对象
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			// rememberme
			token.setRememberMe(rememberMe);
			try {

				currentUser.login(token);

			} catch (UnknownAccountException e) {

				return JSONResult.errorMsg("账号不存在");
			} catch (DisabledAccountException e) {
				return JSONResult.errorMsg("账号未启用");
			} catch (IncorrectCredentialsException e) {
				return JSONResult.errorMsg("密码错误");
			} catch (RuntimeException e) {
				return JSONResult.errorMsg("未知错误,请联系管理员");
			}
			return JSONResult.ok();
		}

		return JSONResult.ok();
	}

	// 退出方法

	@At("/logout")
	@Ok("json")
	@AdaptBy(type = JsonAdaptor.class)
	// @RequiresPermissions("manager:query")
	public JSONResult logout(HttpServletRequest request, HttpServletResponse response) {
		Subject subject = SecurityUtils.getSubject();
		// 主体用户退出
		subject.logout();

		return JSONResult.ok();
	}

}

那我觉得你还是先考虑22楼的方式吧

@wendal

public void dataSourceChange(String company) throws ClassNotFoundException {

		SysCompany companyDB = dao.fetch(SysCompany.class, Cnd.where("bm", "=", company));

		if (companyDB != null && companyDB.getDBInfo() != null) {

			String[] DBInfo = companyDB.getDBInfo().split("/");

			System.out.println("------------------------------------------------------------------------------------------------");
			System.out.println(companyDB.getMc() + "---" + DBInfo[0] + "---" + DBInfo[1] + "---" + DBInfo[2]+ "---" + DBInfo[3].split(",")[0]);
			System.out.println("------------------------------------------------------------------------------------------------");

			
			// 判断用户数据库是否初始化对象获取
			SysDB sysDB = dao.fetch(SysDB.class, Cnd.where("db", "=", DBInfo[3].split(",")[0]));

			Mvcs.setSessionAttrSafe("companyDB", companyDB, true);
			MyDataSource myDataSource = new MyDataSource();
			NutDao mydao = new NutDao(myDataSource);

问题一:这样设置后应该dao对应的时数据源A,mydao对应的就是用户数据源了吗?
问题二:那么如何把mydao设置成像dao一样能够注入的呢?

贴个全的代码

package com.naxxm.service.impl;

import java.text.ParseException;
import java.util.List;

import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
import org.nutz.dao.FieldMatcher;
import org.nutz.dao.impl.NutDao;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.annotation.Param;

import com.alibaba.fastjson.JSONObject;
import com.justep.baas.data.RowState;
import com.justep.baas.data.Table;
import com.justep.baas.data.Transform;
import com.naxxm.MyDataSource;
import com.naxxm.bean.company.SysEmployee;
import com.naxxm.bean.system.SysCompany;
import com.naxxm.bean.system.SysDB;
import com.naxxm.service.DataService;
import com.naxxm.utils.DataUtils2;
import com.naxxm.utils.Transform2;

/**
 * @ClassName: DataServiceImpl
 * @Description: TODO
 * @author: liuyc
 * @date: 2018-1-18 下午7:38:28
 */
@IocBean
public class DataServiceImpl implements DataService {
	@Inject
	private Dao dao;
	@Inject
	private Dao mydao;
	public static final Log Log = Logs.get();

	public JSONObject query(Integer limit, Integer offset, String searchRange, String searchVal, Class<?> type, String actived, String locked, Boolean ignoreNull) {

		Cnd cnd = Cnd.NEW();
		if (Strings.isNotBlank(searchVal) && Strings.isNotBlank(searchRange)) {
			searchVal = "%" + searchVal + "%";
			cnd.or(searchRange, "like", searchVal);
		}

		int pageNumber = (int) Math.ceil(offset / limit) + 1;
		Pager pager = new Pager();
		pager.setPageNumber(pageNumber);
		pager.setPageSize(limit);

		FieldMatcher rule = FieldMatcher.make(actived, locked, true);
		List<?> rows = Daos.ext(dao, FieldFilter.create(type, rule)).query(type, cnd, pager);
		Log.debug(Json.toJson(rows));

		// 设置总个数
		pager.setRecordCount(dao.count(type, cnd));
		JSONObject table = new JSONObject();
		table.put("@type", "table");
		table.put("rows", rows);
		JSONObject data = new JSONObject();
		data.put("sys.count", pager.getRecordCount());
		table.put("data", data);
		return table;
	}

	public void save(@Param("..") JSONObject params) throws ParseException {
		Table table = Transform.jsonToTable(params.getJSONObject("table"));
		// 更新
		List<?> list = Transform2.tableToPojo(table, RowState.EDIT, params.getClass());
		dao.update(list, DataUtils2.arrayJoin4Regex(table.getColumnNames().toArray(), "%s", "|"));
		// 添加
		list = Transform2.tableToPojo(table, RowState.NEW, params.getClass());
		dao.insert(list);
		// 删除
		list = Transform2.tableToPojo(table, RowState.DELETE, params.getClass());
		dao.delete(list);

	}

	public void dataSourceChange(String company) throws ClassNotFoundException {

		SysCompany companyDB = dao.fetch(SysCompany.class, Cnd.where("bm", "=", company));

		if (companyDB != null && companyDB.getDBInfo() != null) {

			String[] DBInfo = companyDB.getDBInfo().split("/");

			System.out.println("------------------------------------------------------------------------------------------------");
			System.out.println(companyDB.getMc() + "---" + DBInfo[0] + "---" + DBInfo[1] + "---" + DBInfo[2] + "---" + DBInfo[3].split(",")[0]);
			System.out.println("------------------------------------------------------------------------------------------------");

			// 判断用户数据库是否初始化对象获取
			SysDB sysDB = dao.fetch(SysDB.class, Cnd.where("db", "=", DBInfo[3].split(",")[0]));

			Mvcs.setSessionAttrSafe("companyDB", companyDB, true);
			MyDataSource myDataSource = new MyDataSource();
			NutDao mydao = new NutDao(myDataSource);

			// 判断用户数据库是否初始化
			if (sysDB != null && sysDB.getState().equals("0")) {
				System.out.println("--->数据库未初始化,初始化数据库");
				Daos.createTablesInPackage(mydao, "com.naxxm.bean.company", false);
				sysDB.setState("1");
				dao.update(sysDB);

			} else {
				System.out.println("--->数据库已经初始化");
			}
		}

	}

}

在MyDataSource内部入手

@Inject只会执行一次的, 再把新对象放进ioc容器也没用的

@IocBean
public class MyDataSource {
     @Inject Dao dao; // 注入主库
     @Inject DataSource dataSource; // 注入主库的数据源
     @IocBean(name="myDao") // 通过@IocBean工厂方法,声明一个用于操作子库的Dao实例
      public Dao  myDao() {
             return new NutDao(this);
      }
      
     public Connection getConnection() {
             HttpServletRequest req = Mvcs.getReq();
             if (req == null) { // 非request上下文? 那就用主库的连接
                       return dataSource.getConnection();
             }
             // 通过自定义逻辑拿到数据库信息(maybe需要做个map缓存一下)
             String username = xxx;
             ...
             return DriverManager.getConnection(url, username, password);
      }
      // 其他无关紧要的方法,补齐即可.
}

操作主库的时候用@Inject Dao dao, 操作字库的时候用@Inject Dao myDao, 名字自己选, 意会

也许dao跟myDao反过来更方便

@wendal 不容易啊,搞了一天终于成功了,谢谢~

写个blog记录一下呗

好,有空写一下~刚看见

添加回复
请先登陆
回到顶部