package com.walker.connector.support;
|
|
import com.walker.connector.AbstractConnector;
|
import com.walker.db.DatabaseException;
|
import com.walker.db.DatabaseExistException;
|
import com.walker.db.DatabaseType;
|
import com.walker.db.page.GenericPager;
|
import com.walker.db.page.ListPageContext;
|
import com.walker.infrastructure.utils.StringUtils;
|
import com.walker.jdbc.ds.MyDruidDataSource;
|
import org.springframework.dao.DataAccessException;
|
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
|
import javax.sql.DataSource;
|
import java.sql.PreparedStatement;
|
import java.sql.SQLException;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 基于数据库实现的连接器对象,目前仅能查询数据集合
|
* @author shikeying
|
* @date 2015年12月15日
|
*
|
*/
|
public abstract class DatabaseConnector extends AbstractConnector {
|
|
public static final String OPTION_USER = "user";
|
public static final String OPTION_PASSWORD = "password";
|
public static final String OPTION_MAX_ACTIVE = "maxActive";
|
public static final String OPTION_MAX_IDLE = "maxIdle";
|
public static final String OPTION_INIT_SIZE = "initialSize";
|
|
public static final String SQL_CREATE_DB_PREF = "CREATE DATABASE ";
|
public static final String SQL_CREATE_DB_SUF = " DEFAULT CHARSET utf8 COLLATE utf8_general_ci;";
|
|
// private SupportDBTypeDataSource dataSource = null;
|
// 2023-05-08 统一使用平台配置数据源,不再创建独立数据源。
|
// private DataSource dataSource = null;
|
|
protected JdbcTemplate jdbcTemplate = null;
|
protected NamedParameterJdbcTemplate namedJdbcTemplate;
|
|
/* 管理模式:不设置数据库名字,可以创建数据库,应该也可以管理 */
|
private boolean manageMode = false;
|
|
private DataSource dataSource = null;
|
|
/**
|
* 没有数据源,只能通过设置参数方式初始化连接器,最后要调用{@link this.initialize()}方法初始化
|
*/
|
public DatabaseConnector(){}
|
|
/**
|
* 如果从外部传入数据源,则通过构造函数直接创建即可。
|
* @param dataSource
|
*/
|
public DatabaseConnector(DataSource dataSource){
|
if(dataSource == null){
|
throw new IllegalArgumentException("dataSource必须设置!");
|
}
|
this.dataSource = dataSource;
|
jdbcTemplate = new JdbcTemplate(dataSource);
|
namedJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
}
|
|
public DataSource getDataSource() {
|
// return (DataSource) JdbcInspector.getInstance().getPrimaryDataSourceMeta();
|
return this.dataSource;
|
}
|
|
public JdbcTemplate getJdbcTemplate() {
|
return jdbcTemplate;
|
}
|
|
public NamedParameterJdbcTemplate getNamedJdbcTemplate() {
|
return namedJdbcTemplate;
|
}
|
|
/**
|
* 返回是否管理模式,管理模式中没有数据库名字,用于处理各种数据库命令
|
* @return
|
*/
|
public boolean isManageMode() {
|
return manageMode;
|
}
|
|
/**
|
* 设置为管理模式:没有数据库名字
|
*/
|
public void setManageMode() {
|
this.manageMode = true;
|
this.setServiceName(StringUtils.EMPTY_STRING);
|
}
|
|
/**
|
* 执行命令:创建数据库
|
* @param databaseName
|
* @throws DatabaseException
|
*/
|
public void exeCreateDatabase(String databaseName) throws DatabaseException {
|
try{
|
jdbcTemplate.execute(new StringBuilder()
|
.append(SQL_CREATE_DB_PREF)
|
.append(databaseName)
|
.append(SQL_CREATE_DB_SUF).toString());
|
} catch(DataAccessException ex){
|
SQLException sqlEx = (SQLException)ex.getRootCause();
|
if(sqlEx.getErrorCode() == 1007){
|
throw new DatabaseExistException(databaseName);
|
} else
|
throw new DatabaseException("createDatabase异常", ex);
|
}
|
}
|
|
/**
|
* 根据SQL语句创建表结构
|
* @param sql
|
* @throws DatabaseException
|
*/
|
public void exeCreateTable(String sql) throws DatabaseException{
|
try{
|
jdbcTemplate.execute(sql);
|
logger.debug("执行创建表SQL = " + sql);
|
} catch(DataAccessException ex){
|
// SQLException sqlEx = (SQLException)ex.getRootCause();
|
// if(sqlEx.getErrorCode() == 1007){
|
// throw new DatabaseExistException("ddddddd");
|
// } else
|
throw new DatabaseException("createTable异常", ex);
|
}
|
}
|
|
/**
|
* 批量写入数据库记录
|
* @param sql
|
* @param spsArray
|
* @transaction 注意:方法名前缀为"exec"的会走事务处理,不过需要在DatabaseStore中设置参数
|
*/
|
public int execBatchInsert(String sql, MapSqlParameterSource[] spsArray){
|
int[] res = namedJdbcTemplate.batchUpdate(sql, spsArray);
|
return res[0];
|
}
|
|
/**
|
* 使用<code>JdbcTemplate</code>实现批量更新。</p>
|
* 通过?替换变量数据即可。
|
* @param sql
|
* @param batchArgs
|
*/
|
public void execBatchUpdateForJdbc(String sql, List<Object[]> batchArgs){
|
jdbcTemplate.batchUpdate(sql, batchArgs);
|
}
|
|
public void execute(String sql) throws DatabaseException{
|
try{
|
jdbcTemplate.execute(sql);
|
} catch(DataAccessException ex){
|
throw new DatabaseException("execute(sql)异常", ex);
|
}
|
}
|
|
// /**
|
// * 返回指定的数据库存在表数量
|
// * @param schema
|
// * @return
|
// */
|
// public int queryTableSize(String schema){
|
// return 0;
|
// }
|
|
/**
|
* 根据sql语句,检索列表数据,返回的一条记录是一个map对象。
|
* @param sql
|
* @param args
|
* @return
|
*/
|
public List<Map<String, Object>> queryForList(String sql, Object[] args){
|
return getJdbcTemplate().queryForList(sql, args);
|
}
|
|
public <T> List<T> queryForRowMapper(String sql, Object[] args, RowMapper<T> rowMapper){
|
return getJdbcTemplate().query(sql, args, rowMapper);
|
}
|
|
/**
|
* 查询自定义rowMapper对象,该方法主要使用<code>namedJdbcTemplate</code>来查询。<br>
|
* 因为对于有些类似:where in (:ids)的查询必须使用命名参数,使用<code>jdbcTemplate</code>则无法查询
|
* @param sql
|
* @param rowMapper
|
* @param paramSource
|
* @return
|
*/
|
public <T> List<T> queryForRowMapper(String sql, RowMapper<T> rowMapper, SqlParameterSource paramSource){
|
return namedJdbcTemplate.query(sql, paramSource, rowMapper);
|
}
|
|
/**
|
* 根据sql语句查询一个整数值,如:总数等
|
* @param sql
|
* @param args
|
* @return
|
*/
|
public int queryForInt(String sql, Object[] args){
|
// logger.debug("+++++++++++++++ jdbcTemplate = " + this.jdbcTemplate);
|
Integer maxValue = this.getJdbcTemplate().queryForObject(sql, Integer.class, args);
|
if(maxValue == null){
|
return 0;
|
}
|
return maxValue;
|
}
|
|
public long queryForLong(String sql, Object[] args){
|
Long maxValue = this.getJdbcTemplate().queryForObject(sql, Long.class, args);
|
if(maxValue == null){
|
return 0;
|
}
|
return maxValue;
|
}
|
|
/**
|
* 给定统计公式,返回单个统计值。
|
* @param sql
|
* @param args
|
* @param clazz
|
* @return
|
*/
|
public <E> E sqlMathQuery(String sql, Object[] args, Class<E> clazz){
|
return this.jdbcTemplate.queryForObject(sql, clazz, args);
|
}
|
|
/**
|
* 查询自定义rowMapper对象,该方法主要使用<code>namedJdbcTemplate</code>来查询。<br>
|
* 因为对于有些类似:where in (:ids)的查询必须使用命名参数,使用<code>jdbcTemplate</code>则无法查询
|
* @param sql
|
* @param rowMapper
|
* @param paramSource
|
* @return
|
*/
|
public <E> List<E> sqlListObjectWhereIn(String sql, RowMapper<E> rowMapper, SqlParameterSource paramSource){
|
return this.namedJdbcTemplate.query(sql, paramSource, rowMapper);
|
}
|
|
/**
|
* 实现简单的分页数据返回,需要提供对象转换接口,使用了Spring框架的<code>RowMapper</code>.</br>
|
* @param sql
|
* @param args
|
* @param rowMapper
|
// * @param pageIndex 当前页(如:第五页则值为5)
|
// * @param pageSize
|
* @param pageSql 分页的sql语句,不同数据库不一样。如:mysql为" select * from table [limit ? offset ?]"
|
* @return
|
*/
|
public <E> GenericPager<E> sqlSimpleQueryPager(String sql, Object[] args, RowMapper<E> rowMapper
|
// , int pageIndex, int pageSize
|
, String pageSql){
|
StringBuilder sqlCount = new StringBuilder();
|
sqlCount.append("select count(*) cnt from (");
|
sqlCount.append(sql);
|
sqlCount.append(") t");
|
int count = jdbcTemplate.queryForObject(sqlCount.toString(), Integer.class, args);
|
int pageIndex = ListPageContext.getCurrentPageIndex();
|
int pageSize = ListPageContext.getCurrentPageSize();
|
GenericPager<E> pager = ListPageContext.createGenericPager(pageIndex, pageSize, count);
|
// logger.debug("...........page count = " + count);
|
// logger.debug("..........."+sqlCount.toString());
|
// if(args != null){
|
// for(Object obj : args){
|
// logger.debug("参数:" + obj);
|
// }
|
// }
|
|
/* 设置分页参数 */
|
List<E> datas = jdbcTemplate.query(pageSql, rowMapper, getSqlPageArgs(args, (int)pager.getFirstRowIndexInPage(), pageSize));
|
// logger.debug("记录索引值:" + pager.getFirstRowIndexInPage() + ", " + pager.getPageIndex());
|
logger.debug(pageSql);
|
// logger.debug(datas);
|
return pager.setDatas(datas);
|
}
|
|
/**
|
* 组装并返回分页需要的参数数组
|
* @param args
|
* @param firstRowIndex
|
* @param pageSize
|
* @return
|
*/
|
protected Object[] getSqlPageArgs(Object[] args, int firstRowIndex, int pageSize){
|
Object[] params = null;
|
int j = 0;
|
if(args == null || args.length == 0){
|
params = new Object[2];
|
} else {
|
params = new Object[args.length+2];
|
for(; j<args.length; j++)
|
params[j] = args[j];
|
}
|
|
DatabaseType type = this.getDatabaseType();
|
if(type == DatabaseType.MYSQL || type == DatabaseType.DERBY){
|
// params[j] = firstRowIndex;
|
// params[j+1] = pageSize;
|
// 使用hibernate的dialect传入的分页参数,与直接使用sql拼写有区别,所以要调换下。
|
params[j] = pageSize;
|
params[j+1] = firstRowIndex;
|
} else if(type == DatabaseType.ORACLE || type == DatabaseType.DAMENG){
|
params[j] = firstRowIndex + pageSize;
|
params[j+1] = firstRowIndex;
|
} else if(type == DatabaseType.POSTGRES){
|
params[j] = pageSize;
|
params[j+1] = firstRowIndex;
|
} else if(type == DatabaseType.SQLSERVER){
|
throw new UnsupportedOperationException("not implements setPageParameters for sqlserver.");
|
}
|
if(logger.isDebugEnabled()){
|
for(Object obj : params){
|
logger.debug("pager param =" + obj);
|
}
|
}
|
return params;
|
}
|
|
/**
|
* 批量更新数据,不带任何参数
|
* @param sql 给定的SQL语句
|
*/
|
public void batchUpdate(String sql){
|
batchUpdate(sql, null);
|
}
|
|
/**
|
* 批量更新数据
|
* @param sql 给定的SQL语句
|
* @param parameters 参数集合,集合中每个参数都是数组,每次更新使用一个参数
|
*/
|
public void batchUpdate(String sql, final List<Object[]> parameters){
|
this.jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
|
|
@Override
|
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
if(parameters == null) return;
|
Object[] param = parameters.get(i);
|
if(param == null || param.length == 0){
|
return;
|
}
|
|
int _psize = param.length;
|
Object _p = null;
|
Class<?> _pc = null;
|
for(int j=1; j<_psize+1; j++){
|
_p = param[j-1];
|
if(_p == null){
|
throw new IllegalArgumentException("parameter in arrays can't be null!");
|
}
|
_pc = _p.getClass();
|
|
if(_pc.isPrimitive()){
|
if(_pc == int.class){
|
ps.setInt(j, ((Integer)_p).intValue());
|
} else if(_pc == float.class){
|
ps.setFloat(j, ((Float)_p).floatValue());
|
} else if(_pc == boolean.class){
|
ps.setBoolean(j, ((Boolean)_p).booleanValue());
|
} else if(_pc == long.class){
|
ps.setLong(j, ((Long)_p).longValue());
|
} else if(_pc == double.class){
|
ps.setDouble(j, ((Double)_p).doubleValue());
|
}
|
} else if(_pc == String.class){
|
ps.setString(j, _p.toString());
|
} else if(_pc == Integer.class){
|
ps.setInt(j, ((Integer)_p).intValue());
|
} else if(_pc == Float.class){
|
ps.setFloat(j, ((Float)_p).floatValue());
|
} else if(_pc == Boolean.class){
|
ps.setBoolean(j, ((Boolean)_p).booleanValue());
|
} else if(_pc == Long.class){
|
ps.setLong(j, ((Long)_p).longValue());
|
} else if(_pc == Double.class){
|
ps.setDouble(j, ((Double)_p).doubleValue());
|
} else {
|
ps.setObject(j, _p);
|
}
|
}
|
}
|
|
@Override
|
public int getBatchSize() {
|
if(parameters == null) return 0;
|
return parameters.size();
|
}
|
});
|
}
|
|
@Override
|
public Object invoke(Object... param) throws Exception {
|
int s = param.length;
|
if(s < 2){
|
throw new IllegalArgumentException("数据库查询参数缺失,至少2个:sql, args, rowMapper(option)");
|
}
|
if(s == 3){
|
return jdbcTemplate.query((String)param[0], (RowMapper<?>)param[2], (Object[])param[1]);
|
} else {
|
return jdbcTemplate.queryForList((String)param[0], (Object[])param[1]);
|
}
|
}
|
|
@Override
|
public void initialize() {
|
super.initialize();
|
if(!this.manageMode && StringUtils.isEmpty(getServiceName())){
|
throw new IllegalArgumentException("未提供数据库名称:serviceName");
|
}
|
|
Map<String, String> params = getParameters();
|
if(params.size() <= 0){
|
throw new IllegalArgumentException("未找到数据库连接参数");
|
}
|
if(params.get(OPTION_USER) == null || params.get(OPTION_PASSWORD) == null){
|
throw new IllegalArgumentException("未找到数据库连接参数:用户名或密码");
|
}
|
|
/* DefaultDataSource dataSource = new DefaultDataSource();
|
|
DefaultDataSource ds = (DefaultDataSource)dataSource;
|
ds.setDatabaseType(this.getDatabaseType().getTypeIndex());
|
ds.setIp(getUrl());
|
ds.setPort(getPort());
|
ds.setDatabaseName(getServiceName());
|
ds.setUsername(params.get(OPTION_USER));
|
ds.setPassword(params.get(OPTION_PASSWORD));
|
|
// 设置连接池参数
|
// dataSource.setMaxWait(10000);
|
// dataSource.setRemoveAbandoned(true);
|
// dataSource.setRemoveAbandonedTimeout(300);
|
// dataSource.setTimeBetweenEvictionRunsMillis(60000);
|
// dataSource.setNumTestsPerEvictionRun(10);
|
// dataSource.setMinEvictableIdleTimeMillis(10000);
|
// dataSource.setTestWhileIdle(true);
|
// if(params.get(OPTION_MAX_ACTIVE) != null){
|
// dataSource.setMaxActive(Integer.parseInt(params.get(OPTION_MAX_ACTIVE)));
|
// } else {
|
// dataSource.setMaxActive(10);
|
// }
|
// if(params.get(OPTION_MAX_IDLE) != null){
|
// dataSource.setMaxIdle(Integer.parseInt(params.get(OPTION_MAX_IDLE)));
|
// } else {
|
// dataSource.setMaxIdle(5);
|
// }
|
// if(params.get(OPTION_INIT_SIZE) != null){
|
// dataSource.setInitialSize(Integer.parseInt(params.get(OPTION_INIT_SIZE)));
|
// } else {
|
// dataSource.setInitialSize(2);
|
// }
|
|
// 连接池
|
if(params.get(OPTION_MAX_ACTIVE) != null){
|
ds.setMaximumPoolSize(Integer.parseInt(params.get(OPTION_MAX_ACTIVE)));
|
} else {
|
ds.setMaximumPoolSize(10);
|
}
|
if(params.get(OPTION_INIT_SIZE) != null){
|
ds.setMinimumIdle(Integer.parseInt(params.get(OPTION_INIT_SIZE)));
|
} else {
|
ds.setMinimumIdle(2);
|
}
|
ds.setIdleTimeout(300);
|
ds.setConnectionTimeout(12000);*/
|
|
// 2023-05-08 使用平台数据源。
|
// DataSource dataSource = (DataSource) JdbcInspector.getInstance().getPrimaryDataSourceMeta();
|
DataSource ds = this.acquireDruidDataSource(params);
|
|
jdbcTemplate = new JdbcTemplate(ds);
|
namedJdbcTemplate = new NamedParameterJdbcTemplate(ds);
|
this.dataSource = ds;
|
}
|
|
protected DataSource acquireDruidDataSource(Map<String, String> params){
|
MyDruidDataSource druidDataSource = new MyDruidDataSource();
|
druidDataSource.setIp(getUrl());
|
druidDataSource.setPort(getPort());
|
druidDataSource.setDatabaseType(this.getDatabaseType().getTypeIndex());
|
druidDataSource.setDatabaseName(getServiceName());
|
druidDataSource.setUsername(params.get(OPTION_USER));
|
druidDataSource.setPassword(params.get(OPTION_PASSWORD));
|
if(params.get(OPTION_MAX_ACTIVE) != null){
|
druidDataSource.setMaxActive(Integer.parseInt(params.get(OPTION_MAX_ACTIVE)));
|
} else {
|
druidDataSource.setMaxActive(20);
|
}
|
if(params.get(OPTION_INIT_SIZE) != null){
|
druidDataSource.setInitialSize(Integer.parseInt(params.get(OPTION_INIT_SIZE)));
|
} else {
|
druidDataSource.setInitialSize(5);
|
}
|
druidDataSource.setMinIdle(10);
|
druidDataSource.setMaxWait(60000);
|
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
|
druidDataSource.setMinEvictableIdleTimeMillis(30000);
|
druidDataSource.setMaxEvictableIdleTimeMillis(180000);
|
druidDataSource.setValidationQuery("select 1");
|
druidDataSource.setTestWhileIdle(true);
|
druidDataSource.setTestOnBorrow(false);
|
druidDataSource.setTestOnReturn(false);
|
druidDataSource.setMaxOpenPreparedStatements(64);
|
return druidDataSource;
|
}
|
|
@Override
|
public void destroy() {
|
// if(dataSource != null){
|
// try {
|
// if(dataSource instanceof DefaultDataSource){
|
// ((DefaultDataSource)dataSource).close();
|
// }
|
// } catch (Exception e) {
|
// logger.error("销毁 DatabaseConnector 失败:" + ((DefaultDataSource) dataSource).getDatabaseName(), e);
|
// }
|
// }
|
}
|
|
/**
|
* 返回数据库类型,子类实现
|
* @return
|
*/
|
protected abstract DatabaseType getDatabaseType();
|
}
|