前言
在日常的项目开发中,项目可能需要适配不同的数据库类型。而你需要做的仅是修改对应datasource配置即可。不知你是否曾想过,在不同数据库间,更换完对应的数据库配置后,框架究竟为我们都做了哪些内容,其实现机制是什么。
正文
为了使理解更清楚,这里有一个小的功能需求,如下图:
根据上面图中输入的对应数据库用户名、密码、驱动、连接查询出对应SQL的内容。并返回List<Map<String,Object>>的结果集,或者List<PO>的结果集。
注意,这里对应驱动和连接内容没有限制,也就是可以任意类型数据库都可以。
这个该如何实现?
上面这个需求即是动态连接不同数据库的方法内容。
说到这里,就必须谈到对应jdbc了。可能会有人认为,这里跟JDBC有什么关系,我们是不同数据库的连接呀。
其实,jdk的JDBC是所有数据库驱动连接数据库的最底层实现。
一般使用JDBC连接数据库会是如下写法:
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
其中各属性内容为:
driver:数据库驱动。
url:数据库连接
user:用户名
password:密码
根据数据的driver内容,JDBC会选择对应的驱动内容。这里默认所有的第三方数据库Jar都存在。
如果驱动是mysql,则会对应去找mysql的jar。
然后根据用户名、密码以及连接地址去连接数据库了。
成功连接后就会拿到连接内容,
然后就可以根据sql内容查询结果了:
首先对sql进行与加载:
如:String sql = seltct * from t_1 where id = ?
Preparedstatement statement = connection.prepareStatement(sql)。
然后就是根据占位符而给对应内容赋值。
如:
statement.setString(1,"1");
这就表示第一个占位符 ? 的值为 1。
当然,对应站位符可能还是int类型,这个时候就需要匹配对应int类型。
生成为完成的sql,即可根据sql查询了:
ResultSet result = statement.executeQuery();
result 即为这条sql查询出来的结果级。
如上面提到如果要将结果返回List<Map<String,Object>>的形式,
接下来就是自己手动结果进行改造处理封装到对应的容器载体中。
如需要List<PO> 类型结果集,同样进行对应处理。
这里常用的就是反射、泛型以及ResultSet相关API进行操作。
上面的JDBC相关内容比较基础,如果你了解了。
那么对于SpringJDBC常见的API内容,你一定会感觉豁然开朗。
相关的持久层API都是根据上述内容实现出来的。
例如:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/blogsrc?useUnicode=true&characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("root");
jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select * from user where id =?";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql, 1L);
List<Map<String, Object>> users1 = jdbcTemplate.queryForList(sql, new Object[] {1L});
对应的API都是在基础的JDBC上封装了一层自己的实现,而最底层都是连接JDBC。
下面内容就是我自己写的实现逻辑,根据输入的驱动,连接,用户名,密码登录对应的数据库,然后查询SQL返回List<Map<String,Object>>的结果集。
public static List<Map<String, Object>> queryForListExample(String driver,String url,String user,String user){
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
DBParam dbParam2 = new DBParam();
dbParam2.addParam("1");
List<Map<String, Object>> listMap = queryLisMapBySQL(sqlContent, dbParam2,conn);
}
private List<Map<String, Object>> queryLisMapBySQL(String sql,DBParam param, Connection conn) throws Exception {
try{
return queryDataSuperDao(sql,param,conn,new AbsTscpResultHandler<List<Map<String,Object>>>() {
public List<Map<String,Object>> resultHandler(ResultSet result)throws TscpBaseException {
try {
List<Map<String,Object>> resultmap = new ArrayList<Map<String,Object>>();
while(result.next()){
Map<String,Object> map = new HashMap<String,Object>();
for(int i=0;i<result.getMetaData().getColumnCount();i ){
map.put(this.columnConvert(result.getMetaData().getColumnLabel(i 1)),dbDataTypeConvert(result.getObject(i 1)));
}
resultmap.add(map);
}
return resultmap;
} catch (SQLException e) {
throw new TscpBaseException("TSCP-9086:Structure handler failure !",e);
}
}
});
}catch(Throwable e){
throw new Exception("TSCP-9086:Structure handler failure !",e);
}
}
private <T> T queryDataSuperDao(String sql,DBParam param, Connection conn,ITscpResultHandler<T> handler) throws Throwable {
PreparedStatement statement = this.getPreparedStatement(sql,conn);
this.paramConvertStatement(statement, param);
ResultSet result = null;
try {
result = statement.executeQuery();
T map = handler.resultHandler(result);
return map;
} catch (Throwable ex) {
throw ex;
}finally{
conn.close();
conn = null;
}
}
private PreparedStatement getPreparedStatement(String sql, Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
private void paramConvertStatement(PreparedStatement statement,DBParam params) throws SQLException {
if (statement == null || params == null|| params.isEmpty())
return;
for (int i = 0; i < params.getParams().size(); i ) {
Object param = params.getParams().get(i);
if (param == null)
statement.setObject(i 1, null);
if (param instanceof Character)
statement.setString(i 1, param.toString());
else if (param instanceof String)
statement.setString(i 1, (String) param);
else if (param instanceof Integer)
statement.setInt(i 1, (Integer) param);
else if (param instanceof Double)
statement.setDouble(i 1, (Double) param);
else if (param instanceof Long)
statement.setLong(i 1, (Long) param);
else if (param instanceof Timestamp)
statement.setTimestamp(i 1, (Timestamp) param);
else if (param instanceof java.sql.Date)
statement.setDate(i 1, (java.sql.Date) param);
else if (param instanceof java.util.Date)
statement.setTimestamp(i 1, new Timestamp(((java.util.Date) param).getTime()));
else if (param instanceof BigDecimal)
statement.setBigDecimal(i 1, (BigDecimal) param);
else if (param instanceof Byte)
statement.setByte(i 1, (Byte) param);
else if (param instanceof Short)
statement.setShort(i 1, (Short) param);
else if (param instanceof Float)
statement.setFloat(i 1, (Float) param);
else if (param instanceof Boolean)
statement.setString(i 1, (Boolean) param ? "Y" : "N");
else
statement.setObject(i 1, param);
}
}
public Object dbDataTypeConvert(Object inType) throws TscpBaseException{
if (inType == null)
return null;
if (inType instanceof Timestamp)
return (Date)inType;
else if (inType instanceof java.sql.Date)
return (Date)inType;
else if (inType instanceof BigDecimal)
return ((BigDecimal)inType).intValue();
return inType;
}
对应DBParam则是存放查询属性内容的载体:
public class DBParam {
private List<Object> params = new ArrayList<Object>();
public DBParam addParam(Object param){
if(params == null)
params = new ArrayList<Object>();
params.add(param);
return this;
}
}
至此,完成逻辑内容。感兴趣的可能将上述代码本地进行运行。