Каков наилучший способ дублировать / расширить функциональность статического класса? - PullRequest
5 голосов
/ 17 февраля 2009

Приложение, над которым я работаю, имеет единственный класс, который поддерживает соединение с базой данных. Все члены этого класса являются статическими для обеспечения одноэлементного шаблона, поэтому фактическая логика соединения выполняется в блоке статического инициализатора:

public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
        sessionFactory = new Configuration().configure().buildSessionFactory();
    }

    static void openSession() {
        //open session with sessionFactory
    }

    public static Session currentSession() {
        //return the currently open session
    }

    static void closeSession() {
        //close the currently open session
    }

}

Однако теперь приложению необходимо открыть соединение с базой данных для второй базы данных. То, как этот класс структурирован сейчас, единственный способ поддерживать второе соединение при сохранении вышеуказанного шаблона - создать второй класс (что-то вроде SecondHibernateUtil) и изменить одну строку конфигурации в блоке инициализатора. Это похоже на действительно расточительное количество копий / вставок.

Может кто-нибудь предложить способ, которым я мог бы переделать эту настройку, чтобы поддерживать несколько соединений одновременно, не будучи чрезмерно разрушительным для кода, уже вызывающего существующие методы?

Ответы [ 9 ]

2 голосов
/ 17 февраля 2009

Сохраните статические методы и разрешите им делегировать соответствующие методы экземпляра экземпляра по умолчанию.

Для вторичных баз данных позволяют создавать больше экземпляров любыми способами и получать к ним доступ через методы их экземпляров.

Затем вы можете избавиться от вызовов статических методов, а когда все они исчезнут, удалить статические методы.

2 голосов
/ 17 февраля 2009

Возможно, это не должно было быть статичным.

1 голос
/ 18 февраля 2009

Я рассматривал этот вопрос очень давно, и из-за того, что вы задаете, я решил никогда не создавать статические классы, подобные этому. С ним на самом деле 2 проблемы ...

Во-первых, он не предназначен для повторного использования (как вы заметили), а исправление - это сложный рефакторинг.

Во-вторых, у вас не может быть второго экземпляра (похоже, программисты предполагают, что есть только одна вещь почти столько же, сколько они предполагают, что им никогда не понадобится менять первые две цифры даты!)

Если вы решите, что вам нужен второй экземпляр с фабричным шаблоном (или что-то вроде пружины), это тривиальное изменение - просто передайте переменную фабрике, чтобы указать, какой экземпляр вам нужен, - но для рефакторинга из статического класс просто сука.

Вы должны быть осторожны, чтобы не злоупотреблять фабричными шаблонами. Если вы действительно склоняетесь в этом направлении, изучите и примите весной - это будет сочетаться с вашими естественными тенденциями, чтобы улучшить ваш код на пределе.

1 голос
/ 18 февраля 2009

Ну, сейчас самое время сделать все правильно.

Невозможно добавить второе соединение и сохранить описанный вами шаблон.

Вместо этого вы можете попытаться сделать все правильно.

У вас есть два варианта.

Первый вариант . Держите беспорядок и сделайте его немного хуже (не сообщая своим клиентам)

  1. Создайте служебный класс, который будет обрабатывать соединения для вас. На этот раз не ограничивайтесь только двумя. Вместо этого используйте карту и не делайте ее статичной.
  2. Измените свой статический класс, используя этот новый служебный объект.
  3. Создайте новую утилиту подключения (HibernateUtility2) и используйте этот новый объект утилиты.

    // Create a new utility class using a map instead.
    class HibernateUtility { 
    
        private Map<String, SessionFactory> factories;
        private HibernateUtility instance;
    
        public  static HibernateUtility getInstance() { 
            if( instance == null ) { 
            instance = new HibernateUtility();
            }
            return instance;
        }
    
        private HibernateUtility() {}
    
        private void check( String whichConnection ) { 
            if( !factories.containsKey( whichConnection ) ) {
                // start the connection
                factories.put( whichConnection, new Configu..
                //... etc etc ().configure().buildSessionFactory() );
            }
        }
    
        void openSession( String whichConnection ) {
            check( whichConnection );
            //open session with sessionFactory
            factories.get( whichConnection ).open(); //etcetc
        }
    
        Session currentSession( whichConnection ) {
            check( whichConnection );
            //return the currently open session
            factories.get( whichConnection ) .....
        }
    
        void closeSession( String whichConnection ) {
            check( whichConnection );
            //close the currently open session
            factories.get( whichConnection );
        }       
    }
    
    //------------------------------------------
    // Make your existing class call that class
    public class HibernateUtil {
    
        static void openSession() {
            //open session with sessionFactory
            HibernateUtility.getInstance( "one" ).openSession();
        }
    
        public static Session currentSession() {
            //return the currently open session
            HibernateUtility.getInstance( "one" ).currentSession();
        }
    
        static void closeSession() {
            //close the currently open session
            HibernateUtility.getInstance( "one" ).closeSession();
        }
    }
    
    //------------------------------------------            
    // Make the new connection use that class too.
    public class HibernateUtil {
    
        static void openSession() {
            //open session with sessionFactory
            HibernateUtility.getInstance( "two" ).openSession();
        }
    
        public static Session currentSession() {
            //return the currently open session
            HibernateUtility.getInstance( "two" ).currentSession();
        }
    
        static void closeSession() {
            //close the currently open session
            HibernateUtility.getInstance( "two" ).closeSession();
        }
    }
    

Таким образом, существующий клиентский код не сломается.

Второй вариант . Очистите бардак и измените код соответствующим образом.

Во втором варианте вы создаете объект, очень похожий на HibernateUtility, описанный ранее, но на этот раз вы не добавляете методы openSession или closeSession , которые не принадлежат к этому интерфейсу.

Используя этот нестатический метод, вы сможете создавать разные экземпляры, которые могут подключаться к разным конфигурациям.

Вы просто используете currentSession , который, если сеанс не существует, создает новый и получает в качестве параметра идентификатор (очень похожий на «один» и «два» в предыдущем примере)

В этом втором варианте вы должны изменить свой клиентский код, чтобы использовать правильную реализацию.

Примечание: Что-то не так с форматированием кода в SO, мне потребовалось некоторое время, чтобы отформатировать его таким, какой он есть, и это не очень хорошо выглядит :(

0 голосов
/ 16 июля 2012

Я не знаю общего решения этой проблемы, но здесь вы можете объявить нестатические SessionFactory s и установить конфигурации в вашем коде, а не в вашем hibernate.cfg.xml файле! так что вы можете иметь более одного SessionFactory для подключения к нескольким базам данных для более разных отображений.

0 голосов
/ 18 февраля 2009

Наименее разрушительный способ исправить это - добавить в класс 2-ю статическую переменную, инициализировать 2-й SessionFactory в статическом блоке, а затем изменить статический метод «currentSession», чтобы иметь параметр String для выбора правильного экземпляра на основе вашего бизнеса / кодовая логика.

0 голосов
/ 18 февраля 2009

Есть два возможных ответа; давайте посмотрим на оба.

Один - преобразовать его в нестатический. Возможно, это было бы лучше, чем статический интерфейс, поскольку у вас есть конкретный объект (SessionFactory) за интерфейсом. Преимущество этого состоит в том, что вы откладываете создание SessionFactory до тех пор, пока вам это не понадобится (и никогда не создадите его, если вы этого не делаете). Создайте статический метод для получения объекта HibernateUtil по умолчанию, создав его, если он не существует. Вы можете или не можете разрешить второй HibernateUtil.

Другой способ - создать совершенно новый статический объект HibernateUtil2, который при необходимости делегирует HibernateUtil. Однако обратите внимание, что они не будут связаны друг с другом каким-либо наследственным образом, потому что статика не может использоваться в интерфейсах.

Я бы порекомендовал первый маршрут, при прочих равных условиях, но ваш конкретный случай может оправдать второй.

0 голосов
/ 17 февраля 2009

Учись весне. Если вы уже используете Hibernate, вам не составит труда освоить достаточно Spring для решения этой проблемы. Сотрудники Spring Source уже решили эту проблему лучше, чем когда-либо.

Транзакции Spring действительны для JDBC, Hibernate, iBatis или любых других технологий персистентности, которые поддерживает Spring. Если вы уже используете Spring, не изобретайте то, что они уже сделали лучше. Используйте свои транзакции.

Если у вас более одной базы данных, и вы выполняете запись в обе единицы как одну единицу работы, вы знаете достаточно, чтобы использовать драйверы XA для обоих, верно?

0 голосов
/ 17 февраля 2009

Не очень хороший способ, иногда нет другого выбора, кроме как просто скопировать всю продажу класса и жить с дублированием.

Я знаю, это звучит действительно плохо но, черт возьми, сколько раз этот класс действительно изменялся с момента его копирования из документации по спящему режиму?

Я уверен, что кто-то собирается перевести все приложение на управление пружиной SessionFactory и все такое, но если ваше приложение не Hibernate "Hello World", то я думаю, что это может занять немного больше пару часов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...