#java #mysql #singleton #connection-pooling
#java #mysql #singleton #объединение в пул соединений
Вопрос:
У меня удивительная проблема, заключающаяся в том, что любое количество поисковых запросов не помогает. Я надеялся, что смогу уточнить здесь.
Я объявил Java singleton для объединения в пул соединений моей базы данных JDBC / MySQL, используя нетерпеливую инициализацию следующим образом:
public class SomeConnectionPool
{
private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
private static SomeDataSource sDS = null;
private static SomeConnectionPool sCP = new SomeConnectionPool ();
private SomeConnectionPool ()
{
// Configure DataSource, connect to DB
sDS = new SomeDataSource (config);
}
public static SomeConnectionPool getInstance ()
{
return sCP;
}
public static SomeDataSource getDataSource ()
{
return sDS;
}
}
В моем приложении я использую singleton следующим образом:
SomeDataSource sds = SomeConnectionPool.getInstance ().getDataSource ();
Connection connection = sds.getConnection ();
это работает, как и ожидалось.
Однако, если я изменю порядок элементов в SomeConnectionPool
классе следующим образом:
public class SomeConnectionPool
{
private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
private static SomeConnectionPool sCP = new SomeConnectionPool ();
private static SomeDataSource sDS = null;
... // the rest is the same code as before
}
Я получаю исключение нулевого указателя в строке, которая запрашивает a getConnection()
. Я вижу, что объекты connection выделяются в базе данных MySQL ( show processlist
перечисляет их).
Действительно ли порядок элементов имеет значение? Это противоречит моему предыдущему опыту. Чего мне не хватает?
Ответ №1:
В первом порядке :
private static SomeDataSource sDS = null;
private static SomeConnectionPool sCP = new SomeConnectionPool ();
Сначала вы инициализируете sDS
значение null, а затем присваиваете ему ненулевое значение в конструкторе SomeConnectionPool . Следовательно, это не null.
Во втором порядке :
private static SomeConnectionPool sCP = new SomeConnectionPool ();
private static SomeDataSource sDS = null;
SomeConnectionPool sCP = new SomeConnectionPool ();
инициализирует sDS в своем конструкторе, а позже SomeDataSource sDS = null;
сбрасывает его на null. Вот почему вы получаете исключение нулевого указателя ( SomeConnectionPool.getInstance ().getDataSource ();
возвращает null).
Комментарии:
1. Спасибо за все ответы, но это была безнадежная оплошность с моей стороны — я слишком долго смотрел на этот код, и поэтому я пропустил
=null
из-за слепого вырезания и вставки. Тем не менее, урокstatic
хорошо усвоен— Я запутался с переупорядочением инструкций Java — представлял, что это как-то связано с этим. Модераторы — я пытался удалить этот вопрос, но не смог.2. @Sonny Здесь считается плохой практикой удалять вопрос после получения ответа. Задаваемые здесь вопросы и ответы на них призваны помочь будущим посетителям.
3. Достаточно справедливо @Eran. Спасибо за быстрый ответ.
Ответ №2:
Вы должны понимать, что происходит при создании класса. При создании класса также создаются его статические члены, построчно.
Я думаю, что реальная проблема заключается в том, что вы объявили SomeDataSource
как статический, когда этого не должно быть. Однако, чтобы объяснить, что вызывает проблему, давайте продолжим ее рассмотрение.
В вашем первом примере
private static SomeDataSource sDS = null;
private static SomeConnectionPool sCP = new SomeConnectionPool ();
sDs = null. В результате создается sCP и создается sDs.
Когда вы меняете порядок, создается sCP, как и sDs. Затем следующая строка возвращает sDs обратно в null. На самом деле вы должны стараться избегать инициализации статики в конструкторах именно по этой причине.
Вместо этого вы должны иметь:
public class SomeConnectionPool
{
private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
private static SomeDataSource sDS = new SomeDataSource (config);
private static SomeConnectionPool sCP = new SomeConnectionPool ();
private SomeConnectionPool ()
{
// Configure DataSource, connect to DB
}
Однако типичный одноэлементный шаблон не имеет ничего статического, кроме экземпляра. Цель состоит в том, чтобы иметь логику, специфичную для экземпляра, но обеспечить, чтобы существовала только одна версия экземпляра. Например, более подходящим одноэлементным будет:
public class SomeConnectionPool
{
private static final Logger log = Logger.getLogger (SomeConnectionPool.class.getName());
private static SomeConnectionPool instance = new SomeConnectionPool ();
private SomeDataSource sDS = null;
private SomeConnectionPool ()
{
// Configure DataSource, connect to DB
this.sDs = new SomeDataSource (config);
}
public static SomeConnectionPool getInstance ()
{
return instance;
}
public SomeDataSource getDataSource ()
{
return this.sDs;
}
Обратите внимание на удаление static
в определенных местах.
Комментарии:
1. Это определенно странный шаблон. Нет данных элемента экземпляра или специфичной для экземпляра логики, и вызов «getInstance» вернет один и тот же объект всем вызывающим (после повторной инициализации SDS статического элемента). Похоже, что весь класс можно сделать статическим без изменения его поведения, по крайней мере, как показано?
2. @T3am5hark Это правда. Это очень странный случай. Я действительно этого не заметил. Я отредактирую свой ответ, чтобы показать более правильный шаблон singleton.
Ответ №3:
ДА. Изменение имеет смысл.
static
инициализаторы и тому подобное выполняются в порядке строк. Итак, раньше у вас было:
- Установить
sDS
вnull
// sDS is null
sCP = new SomeConnectionPool();
- Перейдите в конструктор
- Установить
sDS
вnew SomeDataSource(config);
// sDS is not null
- Конструктор выхода
- продолжить
Итак, ваш конечный результат — sDS
это не null
так. Но после того, как у вас было
sCP = new SomeConnectionPool();
- Перейдите в конструктор
- Установить
sDS
вnew SomeDataSource(config);
// sDS is not null
- Конструктор выхода
- Установить
sDS
вnull
// Now sDS is null
- продолжить
И теперь sDS
есть null
.