com.aliyun.openservices.ons.api.exactlyonce.manager.util.DBAccessUtil Maven / Gradle / Ivy
package com.aliyun.openservices.ons.api.exactlyonce.manager.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.util.JdbcUtils;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.logging.InternalLogger;
import com.aliyun.openservices.ons.api.exactlyonce.TxConstant;
import com.aliyun.openservices.ons.api.exactlyonce.aop.model.MQTxContext;
import com.aliyun.openservices.ons.api.exactlyonce.aop.model.MQTxRecord;
import com.aliyun.openservices.ons.api.exactlyonce.datasource.DataSourceConfig;
import com.aliyun.openservices.ons.api.exactlyonce.manager.MetricService;
import com.aliyun.openservices.ons.api.exactlyonce.manager.datebase.AbstractDBAccessor;
import com.aliyun.openservices.ons.api.exactlyonce.manager.datebase.LoadRecordDo;
import com.aliyun.openservices.ons.api.exactlyonce.manager.datebase.MysqlAccessor;
import com.aliyun.openservices.ons.api.exactlyonce.manager.datebase.SqlServerAccessor;
import com.aliyun.openservices.ons.api.impl.util.ClientLoggerUtil;
import com.aliyun.openservices.shade.org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
/**
* @author gongshi
*/
public class DBAccessUtil {
public static final InternalLogger LOGGER = ClientLoggerUtil.getClientLogger();
private final static ConcurrentHashMap dataSourcePool = new ConcurrentHashMap();
private final static ConcurrentHashMap translatorMap = new ConcurrentHashMap();
/**
* Max active connection for internal datasource
*/
private static final int INNER_DATASOURCE_MAX_ACTIVE = 2;
/**
* Init connection for internal datasource
*/
private static final int INNER_DATASOURCE_INIT_SIZE = 1;
/**
* Max idle connection for internal datasource
* 30000 is the minumum valid value for this parameter
*/
private static final int INNER_EVICT_CONNECTION_MILLIS = 30 * 1000;
private static AbstractDBAccessor getDBAccessor(DataSourceConfig config) throws Exception {
if (config == null || StringUtils.isEmpty(config.getDriver())) {
throw new Exception("datasource driver invalid " + config);
}
switch (DBType.parseTypeFromDriver(config.getDriver())) {
case MYSQL:
return MysqlAccessor.getInstance();
case SQLSERVER:
return SqlServerAccessor.getInstance();
case ORACLE:
case DB2:
default:
throw new Exception("unsupported db type" + config.getDriver());
}
}
private static Connection getInternalConnection(String url, String user, String passwd, String driver) {
String uniqKey = new StringBuilder(256)
.append(url).append(TxConstant.DATASOURCE_KEY_SPLITOR)
.append(user).append(TxConstant.DATASOURCE_KEY_SPLITOR)
.append(passwd).append(TxConstant.DATASOURCE_KEY_SPLITOR)
.append(driver).toString();
DataSource dataSource = dataSourcePool.get(uniqKey);
try {
if (dataSource == null) {
Map property = new HashMap();
property.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME, driver);
property.put(DruidDataSourceFactory.PROP_URL, url);
property.put(DruidDataSourceFactory.PROP_USERNAME, user);
property.put(DruidDataSourceFactory.PROP_PASSWORD, passwd);
property.put(DruidDataSourceFactory.PROP_MAXACTIVE, String.valueOf(INNER_DATASOURCE_MAX_ACTIVE));
property.put(DruidDataSourceFactory.PROP_INITIALSIZE, String.valueOf(INNER_DATASOURCE_INIT_SIZE));
property.put(DruidDataSourceFactory.PROP_MINEVICTABLEIDLETIMEMILLIS, String.valueOf(INNER_EVICT_CONNECTION_MILLIS));
dataSource = DruidDataSourceFactory.createDataSource(property);
((DruidDataSource)dataSource).setQueryTimeout(TxConstant.QUERY_TIMEOUT_SECOND);
DataSource dataSourceOld = dataSourcePool.putIfAbsent(uniqKey, dataSource);
if (dataSourceOld != null) {
((DruidDataSource)dataSource).close();
dataSource = dataSourceOld;
}
}
return dataSource.getConnection();
} catch (Exception e) {
LogUtil.error(LOGGER, "getInternalConnection fail, uniqKey:{}, err:{}", uniqKey, e.getMessage());
}
return null;
}
public static List queryAckedRecord(DataSourceConfig config, LoadRecordDo loadRecordDo) {
List recordList = null;
try {
AbstractDBAccessor accessor = getDBAccessor(config);
Connection connection = getInternalConnection(config.getUrl(), config.getUser(), config.getPasswd(),
config.getDriver());
if (accessor != null && connection != null) {
long begin = System.currentTimeMillis();
recordList = accessor.queryAckedRecord(connection, loadRecordDo);
MetricService.getInstance().incQueryAcked(begin);
}
} catch (Exception e) {
LogUtil.error(LOGGER, "query acked record fail, loadRecordDo:{}, err:{}", loadRecordDo, e.getMessage());
}
return recordList;
}
public static List queryExpiredRecord(DataSourceConfig config, Long timestamp, int count) {
List recordList = null;
try {
AbstractDBAccessor accessor = getDBAccessor(config);
Connection connection = getInternalConnection(config.getUrl(), config.getUser(), config.getPasswd(),
config.getDriver());
if (accessor != null & connection != null) {
long begin = System.currentTimeMillis();
recordList = accessor.queryExpiredRecord(connection, timestamp, count);
MetricService.getInstance().incQueryExpired(begin);
}
} catch (Exception e) {
LogUtil.error(LOGGER, "query acked record fail, timestamp:{}, count:{}, err:{}", timestamp, count, e.getMessage());
}
return recordList;
}
public static void insertTxRecord(Connection connection, DataSourceConfig config, MQTxRecord record) throws Exception {
AbstractDBAccessor accessor = getDBAccessor(config);
if (accessor == null || connection == null) {
throw new Exception("access db fail, config:" + config);
}
long begin = System.currentTimeMillis();
accessor.insertRecord(connection, record, false);
MetricService.getInstance().incInsertRecord(begin);
}
public static boolean isRecordExist(MQTxContext context) {
DataSourceConfig config = context.getDataSourceConfig();
String messageId = context.getMessageId();
try {
AbstractDBAccessor accessor = getDBAccessor(config);
Connection connection = getInternalConnection(config.getUrl(), config.getUser(), config.getPasswd(), config.getDriver());
if (accessor != null && connection != null) {
long begin = System.currentTimeMillis();
Long id = accessor.queryRecordCountByMsgId(connection, config, messageId);
MetricService.getInstance().incQueryMsgIdCount(begin);
if (id != null) {
return true;
}
}
} catch (Exception e) {
LogUtil.error(LOGGER, "query isRecordExist fail, msgId:{}, err:{}", context.getMessageId(), e.getMessage());
}
return false;
}
public static void deleteRecordById(DataSourceConfig config, List msgIds) throws Exception {
AbstractDBAccessor accessor = getDBAccessor(config);
Connection connection = getInternalConnection(config.getUrl(), config.getUser(), config.getPasswd(), config.getDriver());
if (accessor == null || connection == null) {
throw new Exception("access db fail, config:" + config);
}
long begin = System.currentTimeMillis();
accessor.deleteRecordById(connection, msgIds);
MetricService.getInstance().incDeleteRecord(begin);
}
public static boolean isRecordDupException(MQTxContext context, Exception e) {
if (!(e instanceof SQLException)) {
return false;
}
boolean isDup = isDuplicateKeyException(context.getDataSourceConfig().getProductName(), (SQLException)e);
if (isDup) {
LogUtil.info(LOGGER, "exception is cased by record duped, context:{}, err:{}", context, e.getMessage());
}
return isDup;
}
private static boolean isDuplicateKeyException(String productName, SQLException e) {
SQLExceptionTranslator sqlExceptionTranslator = translatorMap.get(productName);
if (sqlExceptionTranslator == null) {
sqlExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(productName);
SQLExceptionTranslator oldTranslator = translatorMap.putIfAbsent(productName, sqlExceptionTranslator);
if (oldTranslator != null) {
sqlExceptionTranslator = oldTranslator;
}
}
DataAccessException accessException = sqlExceptionTranslator.translate("", "", e);
return accessException instanceof DuplicateKeyException;
}
public enum DBType {
/**
* MYSQL database
*/
MYSQL(JdbcUtils.MYSQL, "com.mysql.jdbc.Driver"),
/**
* MS SQLSERVER database
*/
SQLSERVER(JdbcUtils.SQL_SERVER, "com.microsoft.sqlserver.jdbc.SQLServerDriver"),
/**
* ORACLE database
*/
ORACLE(JdbcUtils.ORACLE, "oracle.jdbc.driver.OracleDriver"),
/**
* IBM DB2 database
*/
DB2(JdbcUtils.DB2, "COM.ibm.db2.jdbc.app.DB2Driver");
private String dbType;
private String driver;
DBType(String dbType, String driver) {
this.driver = driver;
}
public static DBType parseTypeFromDriver(String driver) {
if (StringUtils.isEmpty(driver)) {
return null;
}
for (DBType type : DBType.values()) {
if (type.driver.equals(driver)) {
return type;
}
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy