|
@@ -7,7 +7,6 @@ import org.dbsyncer.common.event.RowChangedEvent;
|
|
import org.dbsyncer.common.util.CollectionUtils;
|
|
import org.dbsyncer.common.util.CollectionUtils;
|
|
import org.dbsyncer.connector.config.DatabaseConfig;
|
|
import org.dbsyncer.connector.config.DatabaseConfig;
|
|
import org.dbsyncer.connector.constant.ConnectorConstant;
|
|
import org.dbsyncer.connector.constant.ConnectorConstant;
|
|
-import org.dbsyncer.connector.util.JDBCUtil;
|
|
|
|
import org.dbsyncer.listener.AbstractExtractor;
|
|
import org.dbsyncer.listener.AbstractExtractor;
|
|
import org.dbsyncer.listener.ListenerException;
|
|
import org.dbsyncer.listener.ListenerException;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
@@ -18,6 +17,7 @@ import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Method;
|
|
import java.sql.*;
|
|
import java.sql.*;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
@@ -48,7 +48,9 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
|
|
|
|
private static final String LSN_POSITION = "position";
|
|
private static final String LSN_POSITION = "position";
|
|
private static final long DEFAULT_POLL_INTERVAL_MILLIS = 36000;
|
|
private static final long DEFAULT_POLL_INTERVAL_MILLIS = 36000;
|
|
|
|
+ private static final int PREPARED_STATEMENT_CACHE_CAPACITY = 500;
|
|
private static final int OFFSET_COLUMNS = 4;
|
|
private static final int OFFSET_COLUMNS = 4;
|
|
|
|
+ private final Map<String, PreparedStatement> preparedStatementCache = new ConcurrentHashMap<>(PREPARED_STATEMENT_CACHE_CAPACITY);
|
|
private final Lock connectLock = new ReentrantLock();
|
|
private final Lock connectLock = new ReentrantLock();
|
|
private volatile boolean connected;
|
|
private volatile boolean connected;
|
|
private static Set<String> tables;
|
|
private static Set<String> tables;
|
|
@@ -56,6 +58,7 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
private Connection connection;
|
|
private Connection connection;
|
|
private Worker worker;
|
|
private Worker worker;
|
|
private Lsn lastLsn;
|
|
private Lsn lastLsn;
|
|
|
|
+ private String serverName;
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public void start() {
|
|
public void start() {
|
|
@@ -77,10 +80,10 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
enableTableCDC();
|
|
enableTableCDC();
|
|
readChangeTables();
|
|
readChangeTables();
|
|
readLastLsn();
|
|
readLastLsn();
|
|
- JDBCUtil.close(connection);
|
|
|
|
|
|
+ getTrustedServerNameAE();
|
|
|
|
|
|
worker = new Worker();
|
|
worker = new Worker();
|
|
- worker.setName(new StringBuilder("cdc-parser-").append(getTrustedServerNameAE()).append("_").append(RandomUtils.nextInt(100)).toString());
|
|
|
|
|
|
+ worker.setName(new StringBuilder("cdc-parser-").append(serverName).append("_").append(RandomUtils.nextInt(100)).toString());
|
|
worker.setDaemon(false);
|
|
worker.setDaemon(false);
|
|
worker.start();
|
|
worker.start();
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
@@ -95,18 +98,14 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
@Override
|
|
@Override
|
|
public void close() {
|
|
public void close() {
|
|
if (connected) {
|
|
if (connected) {
|
|
- try {
|
|
|
|
- connectLock.lock();
|
|
|
|
- if (null != worker && !worker.isInterrupted()) {
|
|
|
|
- worker.interrupt();
|
|
|
|
- worker = null;
|
|
|
|
- }
|
|
|
|
- disableTableCDC();
|
|
|
|
- JDBCUtil.close(connection);
|
|
|
|
- connected = false;
|
|
|
|
- } finally {
|
|
|
|
- connectLock.unlock();
|
|
|
|
|
|
+ if (null != worker && !worker.isInterrupted()) {
|
|
|
|
+ worker.interrupt();
|
|
|
|
+ worker = null;
|
|
}
|
|
}
|
|
|
|
+ disableTableCDC();
|
|
|
|
+ preparedStatementCache.values().forEach(this::close);
|
|
|
|
+ preparedStatementCache.clear();
|
|
|
|
+ connected = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -120,34 +119,29 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private void connect() throws SQLException {
|
|
|
|
- final DatabaseConfig config = (DatabaseConfig) connectorConfig;
|
|
|
|
- String driverClassName = config.getDriverClassName();
|
|
|
|
- String username = config.getUsername();
|
|
|
|
- String password = config.getPassword();
|
|
|
|
- String url = config.getUrl();
|
|
|
|
- connection = JDBCUtil.getConnection(driverClassName, url, username, password);
|
|
|
|
|
|
+ private void connect() {
|
|
|
|
+ connection = connectorFactory.connect((DatabaseConfig) connectorConfig);
|
|
}
|
|
}
|
|
|
|
|
|
- private String getTrustedServerNameAE() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
|
|
|
|
|
+ private void getTrustedServerNameAE() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
|
SQLServerConnection conn = (SQLServerConnection) connection;
|
|
SQLServerConnection conn = (SQLServerConnection) connection;
|
|
Class clazz = conn.getClass();
|
|
Class clazz = conn.getClass();
|
|
Method method = clazz.getDeclaredMethod("getTrustedServerNameAE");
|
|
Method method = clazz.getDeclaredMethod("getTrustedServerNameAE");
|
|
method.setAccessible(true);
|
|
method.setAccessible(true);
|
|
- return (String) method.invoke(conn, new Object[]{});
|
|
|
|
|
|
+ serverName = (String) method.invoke(conn, new Object[]{});
|
|
}
|
|
}
|
|
|
|
|
|
private void readLastLsn() {
|
|
private void readLastLsn() {
|
|
- if (!map.containsKey(LSN_POSITION)) {
|
|
|
|
|
|
+ if (!snapshot.containsKey(LSN_POSITION)) {
|
|
lastLsn = queryAndMap(GET_MAX_LSN, rs -> new Lsn(rs.getBytes(1)));
|
|
lastLsn = queryAndMap(GET_MAX_LSN, rs -> new Lsn(rs.getBytes(1)));
|
|
if (null != lastLsn && lastLsn.isAvailable()) {
|
|
if (null != lastLsn && lastLsn.isAvailable()) {
|
|
- map.put(LSN_POSITION, lastLsn.toString());
|
|
|
|
|
|
+ snapshot.put(LSN_POSITION, lastLsn.toString());
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
// Shouldn't happen if the agent is running, but it is better to guard against such situation
|
|
// Shouldn't happen if the agent is running, but it is better to guard against such situation
|
|
throw new ListenerException("No maximum LSN recorded in the database");
|
|
throw new ListenerException("No maximum LSN recorded in the database");
|
|
}
|
|
}
|
|
- lastLsn = Lsn.valueOf(map.get(LSN_POSITION));
|
|
|
|
|
|
+ lastLsn = Lsn.valueOf(snapshot.get(LSN_POSITION));
|
|
}
|
|
}
|
|
|
|
|
|
private void readTables() {
|
|
private void readTables() {
|
|
@@ -315,11 +309,10 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
}
|
|
}
|
|
|
|
|
|
private <T> T query(String sql, StatementPreparer statementPreparer, ResultSetMapper<T> mapper) {
|
|
private <T> T query(String sql, StatementPreparer statementPreparer, ResultSetMapper<T> mapper) {
|
|
- PreparedStatement ps = null;
|
|
|
|
ResultSet rs = null;
|
|
ResultSet rs = null;
|
|
T apply = null;
|
|
T apply = null;
|
|
try {
|
|
try {
|
|
- ps = connection.prepareStatement(sql);
|
|
|
|
|
|
+ final PreparedStatement ps = createPreparedStatement(sql);
|
|
if (null != statementPreparer) {
|
|
if (null != statementPreparer) {
|
|
statementPreparer.accept(ps);
|
|
statementPreparer.accept(ps);
|
|
}
|
|
}
|
|
@@ -330,12 +323,29 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
logger.error(e.getMessage());
|
|
logger.error(e.getMessage());
|
|
} finally {
|
|
} finally {
|
|
- close(ps);
|
|
|
|
close(rs);
|
|
close(rs);
|
|
}
|
|
}
|
|
return apply;
|
|
return apply;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private PreparedStatement createPreparedStatement(String preparedQueryString) {
|
|
|
|
+ try {
|
|
|
|
+ if(connection.isClosed()){
|
|
|
|
+ connect();
|
|
|
|
+ }
|
|
|
|
+ return preparedStatementCache.computeIfAbsent(preparedQueryString, query -> {
|
|
|
|
+ try {
|
|
|
|
+ return connection.prepareStatement(query);
|
|
|
|
+ } catch (SQLException e) {
|
|
|
|
+ throw new ListenerException(e);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ } catch (SQLException e) {
|
|
|
|
+ logger.error(e.getMessage());
|
|
|
|
+ throw new ListenerException(e.getCause());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
enum TableOperation {
|
|
enum TableOperation {
|
|
/**
|
|
/**
|
|
* 插入
|
|
* 插入
|
|
@@ -387,7 +397,6 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
public void run() {
|
|
public void run() {
|
|
while (!isInterrupted() && connected) {
|
|
while (!isInterrupted() && connected) {
|
|
try {
|
|
try {
|
|
- connect();
|
|
|
|
Lsn stopLsn = queryAndMap(GET_MAX_LSN, rs -> new Lsn(rs.getBytes(1)));
|
|
Lsn stopLsn = queryAndMap(GET_MAX_LSN, rs -> new Lsn(rs.getBytes(1)));
|
|
if (!stopLsn.isAvailable()) {
|
|
if (!stopLsn.isAvailable()) {
|
|
TimeUnit.MICROSECONDS.sleep(DEFAULT_POLL_INTERVAL_MILLIS);
|
|
TimeUnit.MICROSECONDS.sleep(DEFAULT_POLL_INTERVAL_MILLIS);
|
|
@@ -402,13 +411,9 @@ public class SqlServerExtractor extends AbstractExtractor {
|
|
pull(stopLsn);
|
|
pull(stopLsn);
|
|
|
|
|
|
lastLsn = stopLsn;
|
|
lastLsn = stopLsn;
|
|
- map.put(LSN_POSITION, lastLsn.toString());
|
|
|
|
|
|
+ snapshot.put(LSN_POSITION, lastLsn.toString());
|
|
} catch (InterruptedException e) {
|
|
} catch (InterruptedException e) {
|
|
break;
|
|
break;
|
|
- } catch (Exception e) {
|
|
|
|
- logger.error(e.getMessage());
|
|
|
|
- } finally {
|
|
|
|
- JDBCUtil.close(connection);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|