Я работаю над проектом, который использует шаблон Service Locator, а также имеет статический класс, который содержит объект DataSource, который мы используем для выполнения всех транзакций нашей базы данных.Общая настройка выглядит следующим образом:
public class Environment {
//multiple app instances on one server
private static final HashMap<String, DataSource> appDatasources = new HashMap<>();
public static DataSource getDataSource(String appName){
return appDatasources.get(appName);
}
public static DataSource getDataSource() {
return appDatasources.get(getApplicationName());
}
public static String getApplicationName(){
return ServiceLocator.getAppName();
}
public static void createDatasource(String jdbc, String appName){
org.apache.tomcat.dbcp.dbcp.BasicDataSource ds = new org.apache.tomcat.dbcp.dbcp.BasicDataSource();
ds.setDriveClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
ds.setUrl(jdbc);
ds.setMaxActive(100);
ds.setMaxIdle(50);
ds.setInitialSize(10);
ds.setRemoveAbandoned(true);
ds.setRemoveAbandonedTimeout(10);
ds.setLogAbandoned(true);
appDatasources.put(appName, ds);
}
}
}
public class ServiceLocatorFactory(String appName) {
public static void registerServices(){
DataSource ds = Environment.getDataSource(appName);
ServiceLocator.replaceService("Service", new Service(ds);
...
...
}
}
Мы передаем DataSource в наши службы, и они передаются нашим объектам доступа к данным и сохраняются как переменные-члены, которые должны быть ссылкой на тот жеОбъект DataSource, созданный нами в Environment
.
public class Service {
private ServiceDAO dao;
public Service(DataSource ds){
dao = new ServiceDAO(ds);
}
}
public class ServiceDAO extends AbstractDAOService{
public ServiceDAO(DataSource ds){
super(ds);
}
}
public abstract class AbstractDAOService {
private final DataSource datasource;
public AbstractDAOService(DataSource ds){
this.datasource = ds;
}
protected DataSource getDataSource(){
return this.datasource;
}
protected int queryGetCount(ParameterQuery qry, String countColName){
ConnectionQuery q = new ConnectionQuery(getDataSource(), qry);
int retval = 0;
try {
ResultSet rs = q.getResultSet();
try {
if(rs.next()){
retval = rs.getInt(countColName);
}
} finally {
rs.close();
}
} catch(Exception ex) {
//handle exception
}
q.close();
return retval;
}
}
. Объект запроса соединения получает соединение из источника данных, а затем создает подготовленный оператор и выполняет его, чтобы получить набор результатов.Проблема, с которой мы сталкиваемся, заключается в том, что существует запрос, выполнение которого по этому конвейеру занимало огромное количество времени.Мы видели время выполнения в диапазоне от 17 секунд до 90 (!) Секунд для довольно простого запроса.При выполнении через SQL Server Management Studio запрос выполняется в миллисекундах.Мы добавили протоколирование, чтобы точно определить, где в коде что-то ломалось, и медлительность была на PreparedStatament.execute()
.
Мы заметили, что если мы заменим метод getDataSource()
в AbstractDAOSerice
на следующий
protected DataSource getDataSource(){
return Environment.getDataSource();
}
тогда запрос выполняется с той же скоростью, что и при выполнении через SSMS.Насколько мы понимаем, оба эти решения должны ссылаться на один и тот же объект, поэтому, хотя мы исправили проблему, мы хотим лучше понять , почему это была проблема с самого начала и как наши изменения исправили проблему.Любое руководство от экспертов Java приветствуется.
Для справки мы находимся на Java jdk1.8.0_144
Редактировать:
public class ConnectionQuery {
private Connection con;
private PreparedStatement stmt;
private ResultSet rs;
private final DataSource datasource;
private ParameterQuery qry;
public ConnectionQuery(DatasSource ds, ParamterQuery qry) {
this.datasource = ds;
this.qry = qry;
}
public ResultSet getResultSet() throws SQLException {
try {
Connection c = defineConnection(true);
this.stmt = this.getQuery.makeStatement(c, false);
this.stmt.execute();
this.rs = this.stmt.getResultSet();
} catch(SQLException se) {
this.close();
throw se;
}
return this.rs;
}
private Connection defineConnection(boolean readOnly) throws SQLException {
if(this.con == null || this.con.isClosed()) {
this.con = this.datasource.getConnection();
}
this.con.setReadOnly(readOnly);
if(this.transactionIsolation != 777){
this.con.setTransactionIsolation(this.transactionIsolation);
}
return this.con;
}
/**
* close all parts of the connection, the RecordSet, the Statement, and the Connection
*/
public void close() {
if(this.rs != null) {
try{
this.rs.close();
this.rs = null;
} catch (SQLException e) {
//warn
}
}
if(this.stmt != null) {
try{
this.stmt.close();
this.stmt = null;
} catch (SQLException e) {
//warn
}
}
if(this.con != null) {
try{
this.con.close();
this.con = null;
} catch (SQLException e) {
//warn
}
}
}
}