Что такое гибернация? Основы реализации Hibernate Core
Опубликовано: 2013-04-17Hibernate — это проект среды Java с открытым исходным кодом. Выполняйте мощные объектно-реляционные сопоставления и запросы к базам данных с помощью HQL и SQL.
В целом, широко используемые библиотеки хорошо спроектированы и реализованы, и очень интересно поучиться у них некоторым передовым методам кодирования.
Давайте заглянем внутрь основной библиотеки hibernate и откроем для себя некоторые из ее ключей дизайна.
В этом посте JArchitect
анализирует Hibernate Core, чтобы углубиться в его дизайн и реализацию.
Пакет по функциям
Package-by-feature
использует пакеты для отражения набора функций. Он помещает все элементы, относящиеся к одной функции (и только этой функции), в один каталог/пакет. В результате получаются пакеты с высокой связностью и высокой модульностью, а также с минимальной связью между пакетами. Элементы, которые тесно связаны друг с другом, размещаются рядом друг с другом.
Ядро Hibernate содержит множество пакетов, каждый из которых связан с определенной функцией hql, sql и другими.
Связь
Низкая связанность желательна, потому что изменение в одной области приложения потребует меньшего количества изменений во всем приложении. В долгосрочной перспективе это может сэкономить много времени, усилий и средств, связанных с изменением и добавлением новых функций в приложение.
Вот три основных преимущества использования интерфейсов:
- Интерфейс предоставляет способ определения контракта, способствующего повторному использованию. Если объект реализует интерфейс, то этот объект должен соответствовать стандарту. Объект, который использует другой объект, называется потребителем. Интерфейс — это контракт между объектом и его потребителем.
- Интерфейс также обеспечивает уровень абстракции, облегчающий понимание программ. Интерфейсы позволяют разработчикам начать говорить об общем способе поведения кода, не вдаваясь в множество подробных деталей.
- Интерфейс обеспечивает низкую связь между компонентами, что позволяет легко защитить потребителя интерфейса от любых изменений реализации в классах, реализующих интерфейсы.
Давайте найдем все интерфейсы, определенные Hibernate Core, для этого мы используем CQLinq
для запроса базы кода.
1 |
from t in Types where t . IsInterface select t |
Если нашей основной целью является обеспечение низкой связанности, существует распространенная ошибка при использовании интерфейсов, которая может убить полезность их использования. Это использование конкретных классов вместо интерфейсов, и чтобы лучше объяснить эту проблему, давайте возьмем следующий пример:
Класс A реализует интерфейс IA, который содержит метод calculate(), потребительский класс C реализован следующим образом.
1 2 3 4 5 6 7 8 9 10 11 |
public class C < br > { … . public void calculate ( ) { … . . m_a . calculate ( ) ; … . } A m_a ; } |
Класс C вместо ссылки на интерфейс IA ссылается на класс A, в этом случае мы теряем преимущество низкой связи, и эта реализация имеет два основных недостатка:
- Если мы решим использовать другую реализацию IA, мы должны изменить код класса C.
- Если некоторые методы добавляются к A, не существующим в IA, и C их использует, мы также теряем контрактное преимущество использования интерфейсов.
C# представил языку возможность явной реализации интерфейса, чтобы гарантировать, что метод из IA никогда не будет вызываться из ссылки на конкретные классы, а только из ссылки на интерфейс. Этот метод очень полезен для защиты разработчиков от потери преимуществ использования интерфейсов.
С JArchitect мы можем проверять такие ошибки с помощью CQLinq
, идея состоит в том, чтобы искать все методы из конкретных классов, используемые непосредственно другими методами.
1 2 3 4 5 6 7 8 9 |
from m in Methods where m . NbMethodsCallingMe > 0 && m . ParentType . IsClass && ! m . ParentType . IsThirdParty && ! m . ParentType . IsAbstract let interfaces = m . ParentType . InterfacesImplemented from i in interfaces where i . Methods . Where ( a = > a . Name == m . Name && a . ParentType ! = m . ParentType ) . Count ( ) > 0 select new { m , m . ParentType , i } |
Например, с этой проблемой связан метод getEntityPersister из SessionFactoryImpl, который реализует интерфейс SessionFactoryImplementor.
Давайте найдем методы, вызывающие непосредственно SessionFactoryImpl.getEntityPersister.
1 2 3 |
from m in Methods where m . IsUsing ( "org.hibernate.internal.SessionFactoryImpl.getEntityPersister(String)" ) select new { m , m . NbBCInstructions } |

Такие методы, как SessionImpl.instantiate, вызывают getEntityPersister напрямую, а не через интерфейс, что нарушает преимущества использования интерфейсов. К счастью, ядро hibernate не содержит многих методов, имеющих эту проблему.
Соединение с внешними ясами
Когда используются внешние библиотеки, лучше проверить, можем ли мы легко заменить стороннюю библиотеку на другую, не влияя на все приложение, есть много причин, которые могут побудить нас изменить стороннюю библиотеку.
Другая библиотека может:
- Иметь больше возможностей
- Более могущественный
- Более безопасный
Давайте возьмем пример antlr lib
, которая использовалась для анализа запросов hql, и представим, что был создан еще один парсер, более мощный, чем antlr. Можем ли мы легко заменить antlr новым парсером?
Чтобы ответить на этот вопрос, давайте посмотрим, какие методы из hibernate используют его напрямую:
1 2 3 |
from m in Methods where m . IsUsing ( "antlr-2.7.7" ) select new { m , m . NbBCInstructions } |
И какие из них использовали его косвенно:
1 2 3 4 |
from m in Projects . WithNameNotIn ( "antlr-2.7.7" ) . ChildMethods ( ) let depth0 = m . DepthOfIsUsing ( "antlr-2.7.7" ) where depth0 > 1 orderby depth0 select new { m , depth0 } |
Многие методы используют antlr напрямую, что делает ядро hibernate тесно связанным с ним, а замена antlr другим — непростая задача. этот факт не означает, что у нас есть проблема в дизайне спящего режима, но мы должны быть осторожны при использовании сторонней библиотеки и хорошо проверять, должна ли сторонняя библиотека быть слабо связана с приложением или нет.
Сплоченность
Принцип единой ответственности гласит, что у класса должна быть одна и только одна причина для изменения. Такой класс называется сплоченным. Высокое значение LCOM
обычно указывает на плохо сплоченный класс. Существует несколько показателей LCOM. LCOM принимает значения в диапазоне [0-1]. LCOMHS (HS означает Henderson-Sellers) принимает значения в диапазоне [0-2]. Обратите внимание, что метрика LCOMHS часто считается более эффективной для обнаружения несвязных типов.
Значение LCOMHS выше 1 должно вызывать тревогу.
В целом классы, более связанные с целостностью, — это классы, имеющие много методов и полей.
Давайте искать типы, имеющие много методов и полей.
1 2 3 4 |
from t in Types where ( t . Methods . Count ( ) > 40 | | t . Fields . Count ( ) > 40 ) && t . IsClass orderby t . Methods . Count ( ) descending select new { t , t . InstanceMethods , t . Fields , t . LCOMHS } |
Этот запрос затрагивает лишь несколько типов, и для всех них LCOMHS меньше 1.
Использование аннотаций
Разработка на основе аннотаций избавляет Java-разработчиков от хлопот громоздкой настройки. И дайте нам мощную функцию, чтобы освободить исходный код от стандартного кода. Полученный код также с меньшей вероятностью будет содержать ошибки.
Давайте найдем все аннотации, определенные ядром hibernate.
1 |
from t in Types where t . IsAnnotationClass && ! t . IsThirdParty select t |
Определено множество аннотаций, что упрощает использование спящего режима разработчиками и позволяет избежать головной боли файлов конфигурации.
Заключение
Hibernate Core — хороший пример проектов с открытым исходным кодом, из которых можно извлечь уроки, не стесняйтесь заглянуть внутрь него.