HHH-11017 - Migrate Integration Guide to AsciiDoctor
This commit is contained in:
parent
7c361fee3a
commit
7d14223531
|
@ -18,13 +18,11 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jboss.jdocbook:gradle-jdocbook:1.2.2"
|
||||
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "java"
|
||||
apply plugin: "jdocbook"
|
||||
apply plugin: 'org.asciidoctor.convert'
|
||||
|
||||
apply plugin: 'hibernate-matrix-testing'
|
||||
|
@ -52,10 +50,6 @@ dependencies {
|
|||
// asciidoctor 'org.asciidoctor:asciidoctorj:1.5.2'
|
||||
asciidoclet 'org.asciidoctor:asciidoclet:0.+'
|
||||
|
||||
jdocbookXsl "org.jboss.pressgang:pressgang-xslt-ns:${pressgangVersion}"
|
||||
jdocbookXsl "org.jboss.pressgang:pressgang-fonts:${pressgangVersion}"
|
||||
jdocbookStyles "org.jboss.pressgang:pressgang-jdocbook-style:${pressgangVersion}"
|
||||
|
||||
compile( libraries.jpa )
|
||||
compile( project( ':hibernate-jpamodelgen' ) )
|
||||
|
||||
|
@ -230,51 +224,6 @@ task aggregateJavadocs(type: Javadoc) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// jDocBook ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
jdocbook {
|
||||
// shared config
|
||||
format('html_single') {
|
||||
finalName = "index.html"
|
||||
stylesheet = "classpath:/xslt/org/hibernate/jdocbook/xslt/xhtml-single.xsl"
|
||||
}
|
||||
format('html') {
|
||||
finalName = "index.html"
|
||||
stylesheet = "classpath:/xslt/org/hibernate/jdocbook/xslt/xhtml.xsl"
|
||||
}
|
||||
|
||||
// book-specific config
|
||||
integrationsGuide {
|
||||
masterSourceDocumentName = 'Hibernate_Integrations_Guide.xml'
|
||||
}
|
||||
mappingGuide {
|
||||
masterSourceDocumentName = 'Hibernate_Mapping_Guide.xml'
|
||||
}
|
||||
}
|
||||
|
||||
[ 'integrationsGuide', 'mappingGuide'].each { bookName ->
|
||||
task "stageLocalStyles_$bookName"(type: Copy) {
|
||||
into project.file( "target/docbook/stage/$bookName" )
|
||||
from project.file( 'src/main/style' )
|
||||
includeEmptyDirs = false
|
||||
}
|
||||
|
||||
tasks[ "stageStyles_$bookName" ].dependsOn "stageLocalStyles_$bookName"
|
||||
|
||||
tasks[ "stageStyles_$bookName" ].doLast {
|
||||
logger.lifecycle( "Staging devguide-specific style resources")
|
||||
copy {
|
||||
into project.file( "target/docbook/stage/$bookName/images" )
|
||||
from project.file( "src/main/docbook/$bookName/en-US" )
|
||||
include '**/images/*.png'
|
||||
include '**/images/*.svg'
|
||||
includeEmptyDirs = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asciidoctor {
|
||||
// we do not want it creating its "default task"
|
||||
enabled = false
|
||||
|
@ -341,11 +290,11 @@ task renderMappingGuide(type: AsciidoctorTask, group: 'Documentation') {
|
|||
}
|
||||
}
|
||||
|
||||
// User Guides ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
final String[] versionComponents = version.split( '\\.' );
|
||||
final String majorMinorVersion = versionComponents[0] + '.' + versionComponents[1];
|
||||
|
||||
// User Guide ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
task renderUserGuide(type: AsciidoctorTask, group: 'Documentation') {
|
||||
description = 'Renders the User Guides in HTML format using Asciidoctor.'
|
||||
sourceDir = file( 'src/main/asciidoc/userguide' )
|
||||
|
@ -367,6 +316,28 @@ task renderUserGuide(type: AsciidoctorTask, group: 'Documentation') {
|
|||
}
|
||||
}
|
||||
|
||||
// Integration Guide ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
task renderIntegrationGuide(type: AsciidoctorTask, group: 'Documentation') {
|
||||
description = 'Renders the User Guides in HTML format using Asciidoctor.'
|
||||
sourceDir = file( 'src/main/asciidoc/integrationguide' )
|
||||
outputDir = new File("$buildDir/asciidoc/integrationguide/html_single")
|
||||
backends "html5"
|
||||
separateOutputDirs false
|
||||
options logDocuments: true
|
||||
attributes icons: 'font', experimental: true, 'source-highlighter': 'prettify', linkcss: true, stylesheet: "css/hibernate.css", majorMinorVersion: majorMinorVersion
|
||||
resources {
|
||||
from('src/main/asciidoc/integrationguide/') {
|
||||
include 'images/**'
|
||||
}
|
||||
from('src/main/style/asciidoctor') {
|
||||
include 'images/**'
|
||||
}
|
||||
from('src/main/style/asciidoctor') {
|
||||
include 'css/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// grouping tasks
|
||||
|
@ -376,13 +347,10 @@ buildDocs.dependsOn aggregateJavadocs
|
|||
buildDocs.dependsOn renderTopicalGuides
|
||||
buildDocs.dependsOn renderGettingStartedGuides
|
||||
buildDocs.dependsOn renderUserGuide
|
||||
// the jDocBook plugin already adds its main task as a dependency of the buildDocs task
|
||||
|
||||
buildDocs.dependsOn renderIntegrationGuide
|
||||
|
||||
buildDocsForPublishing.dependsOn aggregateJavadocs
|
||||
buildDocsForPublishing.dependsOn renderTopicalGuides
|
||||
buildDocsForPublishing.dependsOn renderGettingStartedGuides
|
||||
buildDocsForPublishing.dependsOn renderUserGuide
|
||||
// only html-single to match what Asciidoctor currently offers
|
||||
//buildDocsForPublishing.dependsOn 'renderDocBook_integrationsGuide_en-US_html_single '
|
||||
buildDocsForPublishing.dependsOn renderDocBook_integrationsGuide
|
||||
buildDocsForPublishing.dependsOn renderIntegrationGuide
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
= Hibernate ORM {majorMinorVersion} Integration Guide
|
||||
Steve Ebersole, Vlad Mihalcea
|
||||
:toc:
|
||||
:toclevels: 3
|
||||
|
||||
include::Preface.adoc[]
|
||||
|
||||
:numbered:
|
||||
|
||||
include::chapters/services/Services.adoc[]
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
[[preface]]
|
||||
== Preface
|
||||
|
||||
Hibernate is an http://en.wikipedia.org/wiki/Object-relational_mapping[Object/Relational Mapping] solution for Java environments.
|
||||
|
||||
Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities.
|
||||
It can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC.
|
||||
Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC.
|
||||
However, unlike many other persistence solutions, Hibernate does not hide the power of SQL from you and guarantees that your investment in relational technology and knowledge is as valid as always.
|
||||
|
||||
=== Audience
|
||||
|
||||
This guide is for software developers and architects who will be integrating Hibernate with Java EE application servers, Spring framework, caching solutions (e.g. Infinispan, Ehcache, Hazelcast).
|
|
@ -0,0 +1,418 @@
|
|||
[[services]]
|
||||
== Services and Registries
|
||||
:extrasdir: extras
|
||||
|
||||
Services and registries are new *as a formalized concept* starting in 4.0.
|
||||
But the functionality provided by the different Services have actually been around in Hibernate much, much longer. What is new is managing them, their lifecycles and dependencies through a lightweight, dedicated container we call a `ServiceRegistry`.
|
||||
The goal of this guide is to describe the design and purpose of these `Services` and `Registries`, as well as to look at details of their implementations where appropriate.
|
||||
It will also delve into the ways third-party integrators and applications can leverage and customize `Services` and `Registries`.
|
||||
|
||||
[[services-service]]
|
||||
=== What is a Service?
|
||||
|
||||
A services provides a certain types of functionality, in a pluggable manner.
|
||||
Specifically they are interfaces defining certain functionality and then implementations of those `Service` contract interfaces.
|
||||
The interface is known as the `Service` role; the implementation class is known as the `Service` implementation.
|
||||
The pluggability comes from the fact that the `Service` implementation adheres to contract defined by the interface of the `Service` role and that consumers of the `Service` program to the `Service` role, not the implementation.
|
||||
|
||||
Generally speaking, users can plug in alternate implementations of all standard `Service` roles (overriding); they can also define additional services beyond the base set of `Service` roles (extending).
|
||||
|
||||
Let's look at an example to better define what a `Service` is.
|
||||
Hibernate needs to be able to access JDBC `Connections` to the database.
|
||||
The way it obtains and releases these `Connections` is through the `ConnectionProvider` service.
|
||||
The `Service` is defined by the interface (service role) `org.hibernate.engine.jdbc.connections.spi.ConnectionProvider` which declares methods for obtaining and releasing the `Connections`.
|
||||
There are then multiple implementations of that `Service` contract, varying in how they actually manage the `Connections`.
|
||||
|
||||
Internally Hibernate always references `org.hibernate.engine.jdbc.connections.spi.ConnectionProvider` rather than specific implementations in consuming the `Service` (we will get to producing the `Service` later when we talk about registries).
|
||||
Because of that fact, other `ConnectionProvider` `Service` implementations could easily be plugged in.
|
||||
|
||||
There is nothing revolutionary here; programming to interfaces is generally accepted as good programming practice.
|
||||
What's interesting is the `ServiceRegistry` and the pluggable swapping of the different implementors.
|
||||
|
||||
[[services-contracts]]
|
||||
==== `Service` contracts
|
||||
|
||||
The basic requirement for a `Service` is to implement the marker interface `org.hibernate.service.Service`.
|
||||
Hibernate uses this internally for some basic type safety.
|
||||
|
||||
The `Service` can also implement a number of optional life-cycle related contracts:
|
||||
|
||||
`org.hibernate.service.spi.Startable`::
|
||||
allows the `Service` impl to be notified that it is being started and about to be put into use.
|
||||
`org.hibernate.service.spi.Stoppable`::
|
||||
allows the `Service` impl to be notified that it is being stopped and will be removed from use.
|
||||
`org.hibernate.service.spi.ServiceRegistryAwareService`::
|
||||
allows the `Service` to be injected with a reference to the registry that is managing it. See <<services-dependencies>> for more details.
|
||||
`org.hibernate.service.spi.Manageable`::
|
||||
marks the `Service` as manageable in JMX provided the JMX integration is enabled. This feature is still incomplete.
|
||||
Other::
|
||||
The different registry implementations also understand additional optional contracts specific to that registry. For details, see the details for each registry in <<services-registry>>.
|
||||
|
||||
[[services-dependencies]]
|
||||
==== `Service` dependencies
|
||||
|
||||
Services are allowed to declare dependencies on other services using either of two approaches.
|
||||
|
||||
`@org.hibernate.service.spi.InjectService`::
|
||||
+
|
||||
Any method on the `Service` implementation class accepting a single parameter and annotated with `@InjectService` is considered requesting injection of another service.
|
||||
+
|
||||
By default, the type of the method parameter is expected to be the `Service` role to be injected.
|
||||
If the parameter type is different than the `Service` role, the serviceRole attribute of the `@InjectService` annotation should be used to explicitly name the role.
|
||||
+
|
||||
By default, injected services are considered required, that is the start up will fail if a named dependent `Service` is missing.
|
||||
If the `Service` to be injected is optional, the required attribute of the `@InjectService` annotation should be declared as `false` (default is `true`).
|
||||
|
||||
`org.hibernate.service.spi.ServiceRegistryAwareService`::
|
||||
+
|
||||
The second approach is a pull approach where the `Service` implements the optional `Service` interface `org.hibernate.service.spi.ServiceRegistryAwareService` which declares a single `injectServices` method.
|
||||
+
|
||||
During startup, Hibernate will inject the `org.hibernate.service.ServiceRegistry` itself into services which implement this interface.
|
||||
The `Service` can then use the `ServiceRegistry` reference to locate any additional services it needs.
|
||||
|
||||
[[services-registry]]
|
||||
=== What is a `ServiceRegistry`?
|
||||
|
||||
A `ServiceRegistry`, at its most basic, hosts and manages Services.
|
||||
Its contract is defined by the `org.hibernate.service.ServiceRegistry` interface.
|
||||
|
||||
We already gave a basic overview and definition of services.
|
||||
But services have other interesting characteristics as well:
|
||||
|
||||
* Services have a lifecycle.
|
||||
* They have a scope.
|
||||
* Services might depend on other services.
|
||||
* They need to be produced (choose using one implementation over another).
|
||||
|
||||
The `ServiceRegistry` fulfills all these needs.
|
||||
|
||||
In a concise definition, the `ServiceRegistry` acts as a https://en.wikipedia.org/wiki/Inversion_of_control[Inversion-of-control (IoC)] container.
|
||||
|
||||
Why not just use an existing IoC framework?
|
||||
The main reason was that this had to be as light-weight and as small of a footprint as possible.
|
||||
The initial design also had called for `Services` to be swappable at runtime, which unfortunately had to be removed due to performance problems in the proxy-based swapping-solution; the plan is to investigate alternate ways to achieve swap-ability with better performance at a later date.
|
||||
|
||||
A `Service` is associated with a `ServiceRegistry`.
|
||||
The `ServiceRegistry` scopes the `Service`.
|
||||
The `ServiceRegistry` manages the lifecycle of the `Service`.
|
||||
The `ServiceRegistry` handles injecting dependencies into the `Service` (actually both a pull and a push/injection approach are supported).
|
||||
`ServiceRegistries` are also hierarchical, meaning a `ServiceRegistry` can have a parent `ServiceRegistry`.
|
||||
Services in one registry can depend on and utilize services in that same registry as well as any parent registries.
|
||||
|
||||
[[services-binding]]
|
||||
=== ServiceBinding
|
||||
|
||||
The association of a given `Service` to a given `ServiceRegistry` is called a binding and is represented by the `org.hibernate.service.spi.ServiceBinding` interface.
|
||||
Furthermore, the specific contract between a ServiceBinding and the `ServiceRegistry` is represented by the `org.hibernate.service.spi.ServiceBinding.ServiceLifecycleOwner` interface.
|
||||
|
||||
There are two ways a `Service` becomes associated (bound) to a `ServiceRegistry`.
|
||||
|
||||
* the `Service` can be directly instantiated and then handed to the `ServiceRegistry`
|
||||
* a `ServiceInitiator` can be given to the `ServiceRegistry` (which the `ServiceRegistry` will use if and when the `Service` is needed)
|
||||
|
||||
`ServiceRegistry` implementations register bindings through calls to the overloaded `org.hibernate.service.internal.AbstractServiceRegistryImpl#createServiceBinding` method accepting either a `Service` instance or a `ServiceInitiator` instance.
|
||||
|
||||
Each specific type of registry defines its own `ServiceInitiator` specialization.
|
||||
|
||||
[[services-registry-types]]
|
||||
=== Types of ServiceRegistries
|
||||
|
||||
Currently Hibernate utilizes three different `ServiceRegistry` implementations forming a hierarchy.
|
||||
Each type is a specialization for the purpose of type-safety, but they add no new functionality.
|
||||
|
||||
[[services-registry-boot]]
|
||||
==== `BootstrapServiceRegistry`
|
||||
|
||||
The `org.hibernate.boot.registry.BootstrapServiceRegistry` holds three `Service` and is normally built by means of the `org.hibernate.boot.registry.BootstrapServiceRegistryBuilder` factory class.
|
||||
The builder gives type safe access to customizing these three `Services`.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
This registry holds services that absolutely have to be available for most things in Hibernate to work.
|
||||
====
|
||||
|
||||
In normal usage, the `BootstrapServiceRegistry` has no parent.
|
||||
|
||||
The services of the `BootstrapServiceRegistry` cannot be extended (added to) nor overridden (replaced).
|
||||
|
||||
===== `ClassLoaderService`
|
||||
|
||||
The `Service` role for this `Service` is `org.hibernate.boot.registry.classloading.spi.ClassLoaderService`.
|
||||
This `Service` defines Hibernate's ability to interact with `ClassLoaders`.
|
||||
The manner in which Hibernate (or any library) should interact with `ClassLoaders` varies based on the runtime environment which is hosting the application.
|
||||
Application servers, OSGi containers, and other modular class loading systems impose very specific class-loading requirements.
|
||||
This `Service` provides Hibernate an abstraction from this environmental complexity.
|
||||
And just as important, it does so in a centralized, swappable manner.
|
||||
|
||||
The specific capabilities exposed on this `Service` include:
|
||||
|
||||
* Locating `Class` references by name. This includes application classes as well as _integration_ classes.
|
||||
* Locating resources (properties files, xml files, etc) as _classpath resources_
|
||||
* Interacting with `java.util.ServiceLoader`, Java's own `Service` provider discovery mechanism
|
||||
|
||||
===== `IntegratorService`
|
||||
|
||||
The `Service` role for this `Service` is `org.hibernate.integrator.spi.IntegratorService.`
|
||||
Applications, third-party integrators and others all need to integrate with Hibernate. Historically this used to require something (usually the application) to coordinate registering the pieces of each integration needed on behalf of each integration. The `org.hibernate.integrator.spi.Integrator` contract formalized this "integration SPI". The IntegratorService manages all known integrators.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The concept of "Integrator" is still being actively defined and developed. Expect changes in these SPIs.
|
||||
====
|
||||
|
||||
There are two ways an integrator becomes known.
|
||||
|
||||
* The integrator may be manually registered by calling `BootstrapServiceRegistryBuilder#with(Integrator)`
|
||||
* The integrator may be discovered, leveraging the standard Java `ServiceLoader` capability provided by the `ClassLoaderService`.
|
||||
`Integrators` would simply define a file named `/META-INF/services/org.hibernate.integrator.spi.Integrator` and make it available on the classpath.
|
||||
`ServiceLoader` covers the format of this file in detail, but essentially it lists classes by fully-qualified name that implement `Integrator` one per line.
|
||||
|
||||
===== `StrategySelector`
|
||||
|
||||
The `Service` role for this `Service` is `org.hibernate.boot.registry.selector.spi.StrategySelector`.
|
||||
Think of this as the _short naming_ service.
|
||||
Historically to configure Hibernate users would often need to give fully-qualified name references to internal Hibernate classes.
|
||||
Of course, this has caused lots of problems as we refactor internal code and move these classes around into different package structures.
|
||||
Enter the concept of short-naming, using a well defined and well known _short name_ for the strategy/implementation class.
|
||||
|
||||
The short name mappings in this `Service` can be managed, even by applications and integrators which can be very powerful.
|
||||
For more information on this aspect, see:
|
||||
|
||||
* `BootstrapServiceRegistryBuilder#applyStrategySelector`
|
||||
* `BootstrapServiceRegistryBuilder#applyStrategySelectors`
|
||||
* `org.hibernate.boot.registry.selector.StrategyRegistrationProvider` via `ServiceLoader` discovery
|
||||
* `StrategySelector#registerStrategyImplementor` / `StrategySelector#unRegisterStrategyImplementor`
|
||||
|
||||
[[services-registry-standard]]
|
||||
==== `StandardServiceRegistry`
|
||||
|
||||
The `org.hibernate.boot.registry.StandardServiceRegistry` defines the main Hibernate `ServiceRegistry`, building on the `BootstrapServiceRegistry` which is its parent.
|
||||
This registry is generally built using the `org.hibernate.boot.registry.StandardServiceRegistryBuilder` class.
|
||||
By default, it holds most of the `Services` used by Hibernate.
|
||||
For the full list of `Services` typically held in the `StandardServiceRegistry`, see the source code of `org.hibernate.service.StandardServiceInitiators`.
|
||||
|
||||
In normal usage, the parent of the StandardServiceRegistry is the BootstrapServiceRegistry.
|
||||
|
||||
The services of the StandardServiceRegistry can be extended (added to) and overridden (replaced).
|
||||
|
||||
===== `ConnectionProvider/MultiTenantConnectionProvider`
|
||||
|
||||
The `Service` providing Hibernate with `Connections` as needed.
|
||||
Comes in two distinct (and mutually exclusive) roles:
|
||||
|
||||
`org.hibernate.engine.jdbc.connections.spi.ConnectionProvider`::
|
||||
provides `Connections` in normal environments
|
||||
`org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider`::
|
||||
provides (tenant-specific) `Connections` in multi-tenant environments
|
||||
|
||||
===== `JdbcServices`
|
||||
|
||||
`org.hibernate.engine.jdbc.spi.JdbcServices` is an aggregator `Service` (a `Service` that aggregates other Services) exposing unified functionality around JDBC accessibility.
|
||||
|
||||
===== `TransactionCoordinatorBuilder`
|
||||
|
||||
`org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder` is used by Hibernate to integrate with and underlying transaction system.
|
||||
It is responsible for building `org.hibernate.resource.transaction.spi.TransactionCoordinator` instances for use by each Hibernate `Session`.
|
||||
|
||||
===== `JtaPlatform`
|
||||
|
||||
When using a JTA-based `TransactionCoordinatorBuilder`, the `org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform` `Service` provides Hibernate access to the JTA `TransactionManager` and `UserTransaction`, as well handling `Synchronization` registration.
|
||||
|
||||
===== `JndiService`
|
||||
|
||||
The `org.hibernate.engine.jndi.spi.JndiService` `Service` is used by Hibernate to interact with JNDI contexts.
|
||||
Hibernate's default `JndiService` assumes just a single `InitialContext`.
|
||||
|
||||
===== `RegionFactory`
|
||||
|
||||
The `org.hibernate.cache.spi.RegionFactory` `Service` defines the integration with third party cache implementors as second-level caching providers.
|
||||
|
||||
===== `SessionFactoryServiceRegistryFactory`
|
||||
|
||||
`org.hibernate.service.spi.SessionFactoryServiceRegistryFactory` is a `Service` that acts as a factory for building the third type of `ServiceRegistry` (the SessionFactoryServiceRegistry) which we will discuss next.
|
||||
I opted for the _factory as service_ approach because in the current design there is really not a good exposed hook-in spot for when the `SessionFactoryServiceRegistry` needs to be built.
|
||||
|
||||
[[services-registry-sf]]
|
||||
==== `SessionFactoryServiceRegistry`
|
||||
|
||||
`org.hibernate.service.spi.SessionFactoryServiceRegistry` is the third standard Hibernate `ServiceRegistry`.
|
||||
`SessionFactoryServiceRegistry` is designed to hold `Services` which need access to the `SessionFactory`.
|
||||
|
||||
Typically its parent registry is the `StandardServiceRegistry`.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Integrators, as it stands in 4.x, operate on the `SessionFactoryServiceRegistry`.
|
||||
====
|
||||
|
||||
Currently `SessionFactoryServiceRegistry` holds just four Services.
|
||||
|
||||
===== `EventListenerRegistry`
|
||||
|
||||
`org.hibernate.event.service.spi.EventListenerRegistry` is the main `Service` managed in the `SessionFactoryServiceRegistry`.
|
||||
The is the `Service` that manages all of Hibernate's event listeners.
|
||||
A major use-case for `Integrators` is to alter the listener registry.
|
||||
|
||||
If doing custom listener registration, it is important to understand the `org.hibernate.event.service.spi.DuplicationStrategy` and its effect on registration.
|
||||
The basic idea is to tell Hibernate:
|
||||
|
||||
* what makes a listener a duplicate
|
||||
* how to handle duplicate registrations (error, first wins, last wins)
|
||||
|
||||
===== `StatisticsImplementor`
|
||||
|
||||
`org.hibernate.stat.spi.StatisticsImplementor` is the SPI portion of the Statistics API; the collector portion, if you will.
|
||||
|
||||
===== `NativeQueryInterpreter`
|
||||
|
||||
`org.hibernate.engine.query.spi.NativeQueryInterpreter` is the `Service` Hibernate uses for interpreting native queries.
|
||||
Exists as a `Service` mainly so that integrations such as OGM can override it.
|
||||
|
||||
===== `CacheImplementor`
|
||||
|
||||
`org.hibernate.engine.spi.CacheImplementor` provides a way to customize the way Hibernate interacts with the second-level caching implementation.
|
||||
|
||||
[[services-custom]]
|
||||
=== Custom Services
|
||||
|
||||
So far we have focused on the Hibernate provided services.
|
||||
But applications and integrations can provide their own services as well, either
|
||||
|
||||
* providing a new implementation of a standard `Service` (overriding)
|
||||
* providing a whole new `Service` role (extending)
|
||||
|
||||
[[services-overriding]]
|
||||
==== Custom `Service` Implementations (overriding)
|
||||
|
||||
We discussed swappability of `Service` implementations above.
|
||||
Lets look at an example in practice.
|
||||
For the sake of illustration, lets say that we have developed a new `ConnectionProvider` integrating with the wonderful new latest-and-greatest connection pooling library. Let's look at the steps necessary to make that happen.
|
||||
|
||||
The first step is to develop the actual integration by implementing the `ConnectionProvider` contract.
|
||||
|
||||
[[services-overriding-custom-ConnectionProvider-example]]
|
||||
.Custom `ConnectionProvider` implementation
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/override/LatestAndGreatestConnectionProviderImpl.java[]
|
||||
----
|
||||
====
|
||||
|
||||
At this point we have a decision about how to integrate this new `ConnectionProvider` into Hibernate.
|
||||
As you might guess, there are multiple ways.
|
||||
|
||||
As a first option, we might just require that the code bootstrapping the `StandardServiceRegistry` do the integration.
|
||||
|
||||
[[services-overriding-override-via-StandardServiceRegistryBuilder-example]]
|
||||
.Overriding service implementation via `StandardServiceRegistryBuilder`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/override/ex1-direct.java[]
|
||||
----
|
||||
====
|
||||
|
||||
A second option, if our `LatestAndGreatestConnectionProviderImpl` should always be used, would be to provide a `org.hibernate.service.spi.ServiceContributor` implementation as well to handle the integration on the users behalf.
|
||||
|
||||
[[services-LatestAndGreatestConnectionProviderImplContributor-example]]
|
||||
.`LatestAndGreatestConnectionProviderImplContributor`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/override/ex2-contributor.java[]
|
||||
----
|
||||
====
|
||||
|
||||
We still need to be able to tell Hibernate to perform this integration for us.
|
||||
To do that we leverage Java's `ServiceLoader`.
|
||||
When building the `StandardServiceRegistry`, Hibernate will look for JDK `Service` providers of type `org.hibernate.service.spi.ServiceContributor` and automatically integrate them.
|
||||
We discussed this behavior above. Here we'd define a classpath resource named `META-INF/services/org.hibernate.service.spi.ServiceContributor`.
|
||||
This file will have just a single line naming our impl.
|
||||
|
||||
[[services-META-INF-example]]
|
||||
.`META-INF/services/org.hibernate.service.spi.ServiceContributor`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/override/ex2-meta-inf[]
|
||||
----
|
||||
====
|
||||
|
||||
A third option, if we simply want to make our `LatestAndGreatestConnectionProviderImpl` available as a configuration choice, we would again use a `ServiceContributor` but in a slightly different way.
|
||||
|
||||
[[services-LatestAndGreatestConnectionProviderImplContributor-variation-example]]
|
||||
.`LatestAndGreatestConnectionProviderImplContributor` variation
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/override/ex3-contributor.java[]
|
||||
----
|
||||
====
|
||||
|
||||
That all allows the application to pick our `LatestAndGreatestConnectionProviderImpl` by a short-name.
|
||||
|
||||
[[services-custom-service-short-name-example]]
|
||||
.Custom service short-name
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/override/ex3-app.java[]
|
||||
----
|
||||
====
|
||||
|
||||
[[services-extending]]
|
||||
==== Custom `Service` Roles (extending)
|
||||
|
||||
We can also have the `ServiceRegistry` host custom services (completely new `Service` roles).
|
||||
As an example, let's say our application publishes Hibernate events to a JMS Topic and that we want to leverage the Hibernate `ServiceRegistry` to host a `Service` representing our publishing of events.
|
||||
So, we will expand the `ServiceRegistry` to host this completely new `Service` role for us and manage its lifecycle.
|
||||
|
||||
[[services-EventPublishingService-service-role-example]]
|
||||
.The `EventPublishingService` service role
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/extend/EventPublishingService.java[]
|
||||
----
|
||||
====
|
||||
|
||||
[[services-EventPublishingService-implementation-example]]
|
||||
.The `EventPublishingService` implementation
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/extend/EventPublishingServiceImpl.java[]
|
||||
----
|
||||
====
|
||||
|
||||
[[services-alternate-EventPublishingService-implementation-example]]
|
||||
.An alternative `EventPublishingService` implementation
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/extend/DisabledEventPublishingServiceImpl.java[]
|
||||
----
|
||||
====
|
||||
|
||||
Because we have alternative implementations, it is a good idea to develop an initiator as well that can choose between them at runtime.
|
||||
|
||||
[[services-EventPublishingServiceInitiator-example]]
|
||||
.The `EventPublishingServiceInitiator`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/extend/EventPublishingServiceInitiator.java[]
|
||||
----
|
||||
====
|
||||
|
||||
We could have the application register the `EventPublishingServiceInitiator` with the `StandardServiceRegistryBuilder`, but it is much nicer to write a `ServiceContributor` to handle this for the application.
|
||||
|
||||
[[services-EventPublishingServiceContributor-example]]
|
||||
.The `EventPublishingServiceContributor`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{extrasdir}/extend/EventPublishingServiceContributor.java[]
|
||||
----
|
||||
====
|
|
@ -0,0 +1,12 @@
|
|||
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
|
||||
// pass in org.hibernate.integrator.spi.Integrator instances which are not
|
||||
// auto-discovered (for whatever reason) but which should be included
|
||||
.with( anExplicitIntegrator )
|
||||
// pass in a class-loader Hibernate should use to load application classes
|
||||
.withApplicationClassLoader( anExplicitClassLoaderForApplicationClasses )
|
||||
// pass in a class-loader Hibernate should use to load resources
|
||||
.withResourceClassLoader( anExplicitClassLoaderForResources )
|
||||
// see BootstrapServiceRegistryBuilder for rest of available methods
|
||||
...
|
||||
// finally, build the bootstrap registry with all the above options
|
||||
.build();
|
|
@ -0,0 +1,13 @@
|
|||
public class DisabledEventPublishingServiceImpl implements EventPublishingService {
|
||||
|
||||
public static DisabledEventPublishingServiceImpl INSTANCE =
|
||||
new DisabledEventPublishingServiceImpl();
|
||||
|
||||
private DisabledEventPublishingServiceImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(Event theEvent) {
|
||||
// nothing to do...
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
public interface EventPublishingService extends Service {
|
||||
|
||||
public void publish(Event theEvent);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
public class EventPublishingServiceContributor
|
||||
implements ServiceContributor {
|
||||
|
||||
@Override
|
||||
public void contribute(StandardServiceRegistryBuilder builder) {
|
||||
builder.addinitiator( eventpublishingserviceinitiator.instance );
|
||||
|
||||
// if we wanted to allow other strategies (e.g. a jms
|
||||
// queue publisher) we might also register short names
|
||||
// here with the strategyselector. the initiator would
|
||||
// then need to accept the strategy as a config setting
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
public class EventPublishingServiceImpl
|
||||
implements EventPublishingService, Configurable, Startable, Stoppable,
|
||||
ServiceRegistryAwareService {
|
||||
|
||||
private ServiceRegistryImplementor serviceRegistry;
|
||||
|
||||
private String jmsConnectionFactoryName;
|
||||
|
||||
private String destinationName;
|
||||
|
||||
private Connection jmsConnection;
|
||||
|
||||
private Session jmsSession;
|
||||
|
||||
private MessageProducer publisher;
|
||||
|
||||
@Override
|
||||
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
}
|
||||
|
||||
public void configure(Map configurationValues) {
|
||||
this.jmsConnectionFactoryName = configurationValues
|
||||
.get( JMS_CONNECTION_FACTORY_NAME_SETTING );
|
||||
this.destinationName = configurationValues
|
||||
.get( JMS_DESTINATION_NAME_SETTING );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
final JndiService jndiService = serviceRegistry
|
||||
.getService( JndiService.class );
|
||||
final ConnectionFactory jmsConnectionFactory = jndiService
|
||||
.locate( jmsConnectionFactoryName );
|
||||
|
||||
this.jmsConnection = jmsConnectionFactory.createConnection();
|
||||
this.jmsSession = jmsConnection.createSession(
|
||||
true,
|
||||
Session.AUTO_ACKNOWLEDGE
|
||||
);
|
||||
|
||||
final Destination destination = jndiService.locate( destinationName );
|
||||
|
||||
this.publisher = jmsSession.createProducer( destination );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(Event theEvent) {
|
||||
publisher.send( theEvent );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
publisher.close();
|
||||
jmsSession.close();
|
||||
jmsConnection.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
public class EventPublishingServiceInitiator
|
||||
implements StandardServiceInitiator<EventPublishingService> {
|
||||
|
||||
public static EventPublishingServiceInitiator INSTANCE =
|
||||
new EventPublishingServiceInitiator();
|
||||
|
||||
public static final String ENABLE_PUBLISHING_SETTING =
|
||||
"com.acme.EventPublishingService.enabled";
|
||||
|
||||
@Override
|
||||
public Class<R> getServiceInitiated() {
|
||||
return EventPublishingService.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R initiateService(
|
||||
Map configurationValues,
|
||||
ServiceRegistryImplementor registry) {
|
||||
|
||||
final boolean enabled = extractBoolean(
|
||||
configurationValues,
|
||||
ENABLE_PUBLISHING_SETTING
|
||||
);
|
||||
if ( enabled ) {
|
||||
return new EventPublishingServiceImpl();
|
||||
}
|
||||
else {
|
||||
return DisabledEventPublishingServiceImpl.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import java.lang.Override;
|
||||
|
||||
public class LatestAndGreatestConnectionProviderImpl
|
||||
implements ConnectionProvider, Startable, Stoppable, Configurable {
|
||||
|
||||
private LatestAndGreatestPoolBuilder lagPoolBuilder;
|
||||
|
||||
private LatestAndGreatestPool lagPool;
|
||||
|
||||
private boolean available = false;
|
||||
|
||||
@Override
|
||||
public void configure(Map configurationValues) {
|
||||
// extract our config from the settings map
|
||||
lagPoolBuilder = buildBuilder( configurationValues );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// start the underlying pool
|
||||
lagPool = lagPoolBuilder.buildPool();
|
||||
|
||||
available = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
available = false;
|
||||
|
||||
// stop the underlying pool
|
||||
lagPool.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
if ( !available ) {
|
||||
throwException(
|
||||
"LatestAndGreatest ConnectionProvider not available for use" )
|
||||
}
|
||||
|
||||
return lagPool.borrowConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection(Connection conn) throws SQLException {
|
||||
if ( !available ) {
|
||||
warn(
|
||||
"LatestAndGreatest ConnectionProvider not available for use" )
|
||||
}
|
||||
|
||||
if ( conn == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
lagPool.releaseConnection( conn );
|
||||
}
|
||||
|
||||
...
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
StandardServiceRegistryBuilder builder = ...;
|
||||
...
|
||||
builder.addService(
|
||||
ConnectionProvider.class,
|
||||
new LatestAndGreatestConnectionProviderImpl()
|
||||
);
|
||||
...
|
|
@ -0,0 +1,11 @@
|
|||
public class LatestAndGreatestConnectionProviderImplContributor1
|
||||
implements ServiceContributor {
|
||||
|
||||
@Override
|
||||
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
|
||||
serviceRegistryBuilder.addService(
|
||||
ConnectionProvider.class,
|
||||
new LatestAndGreatestConnectionProviderImpl()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor1
|
|
@ -0,0 +1,4 @@
|
|||
StandardServiceRegistryBuilder builder = ...;
|
||||
...
|
||||
builder.applySetting( "hibernate.connection.provider_class", "lag" );
|
||||
...
|
|
@ -0,0 +1,19 @@
|
|||
public class LatestAndGreatestConnectionProviderImplContributor
|
||||
implements ServiceContributor {
|
||||
|
||||
@Override
|
||||
public void contribute(
|
||||
standardserviceregistrybuilder serviceregistrybuilder) {
|
||||
|
||||
// here we will register a short-name for our service strategy
|
||||
strategyselector selector = serviceregistrybuilder
|
||||
.getbootstrapserviceregistry().
|
||||
.getservice( strategyselector.class );
|
||||
|
||||
selector.registerstrategyimplementor(
|
||||
connectionprovider.class,
|
||||
"lag"
|
||||
latestandgreatestconnectionproviderimpl.class
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor2
|
|
@ -0,0 +1,23 @@
|
|||
public class MyIntegrator implements org.hibernate.integrator.spi.Integrator {
|
||||
|
||||
public void integrate(
|
||||
Configuration configuration,
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
SessionFactoryServiceRegistry serviceRegistry) {
|
||||
// As you might expect, an EventListenerRegistry is the thing with which event listeners are registered It is a
|
||||
// service so we look it up using the service registry
|
||||
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
|
||||
|
||||
// If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an
|
||||
// implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this
|
||||
eventListenerRegistry.addDuplicationStrategy( myDuplicationStrategy );
|
||||
|
||||
// EventListenerRegistry defines 3 ways to register listeners:
|
||||
// 1) This form overrides any existing registrations with
|
||||
eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, myCompleteSetOfListeners );
|
||||
// 2) This form adds the specified listener(s) to the beginning of the listener chain
|
||||
eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst );
|
||||
// 3) This form adds the specified listener(s) to the end of the listener chain
|
||||
eventListenerRegistry.appendListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast );
|
||||
}
|
||||
}
|
|
@ -1,60 +1,41 @@
|
|||
[[preface]]
|
||||
== Preface
|
||||
|
||||
Developing Object-Oriented software that deals with data from Relational
|
||||
Databases can be cumbersome and resource consuming. Development costs
|
||||
are significantly higher due to a paradigm mismatch between how data is
|
||||
represented in objects versus relational databases. Hibernate is an
|
||||
Object/Relational Mapping (ORM) solution for Java environments. ORM
|
||||
refers to the technique of mapping data between an object model
|
||||
representation to a relational data model representation. See
|
||||
http://en.wikipedia.org/wiki/Object-relational_mapping[Wikipedia] for a
|
||||
good high-level discussion. Also, Martin Fowler's
|
||||
http://martinfowler.com/bliki/OrmHate.html[OrmHate] article takes a look
|
||||
at many of the mentioned mismatch problems.
|
||||
Working with both Object-Oriented software and Relational Databases can be cumbersome and time consuming.
|
||||
Development costs are significantly higher due to a paradigm mismatch between how data is represented in objects versus relational databases.
|
||||
Hibernate is an Object/Relational Mapping solution for Java environments.
|
||||
The term http://en.wikipedia.org/wiki/Object-relational_mapping[Object/Relational Mapping] refers to the technique of mapping data from an object model representation to a relational data model representation (and visa versa).
|
||||
|
||||
Although having a strong background in SQL is not required to use
|
||||
Hibernate, having a basic understanding of the concepts can help you
|
||||
understand Hibernate more quickly and fully. An understanding of data
|
||||
modeling principles is especially important. Both
|
||||
http://www.agiledata.org/essays/dataModeling101.html and
|
||||
http://en.wikipedia.org/wiki/Data_modeling are good starting points for
|
||||
understanding these data modeling principles.
|
||||
Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities.
|
||||
It can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC.
|
||||
Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC.
|
||||
However, unlike many other persistence solutions, Hibernate does not hide the power of SQL from you and guarantees that your investment in relational technology and knowledge is as valid as always.
|
||||
|
||||
Understanding the basics of transactions and design patterns such as
|
||||
"Unit of Work" <<Bibliography.adoc#PoEAA,PoEAA>> or "ApplicationTransaction" are important as well.
|
||||
These topics will be discussed in the documentation, but a prior
|
||||
understanding will certainly help.
|
||||
Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier.
|
||||
However, Hibernate can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the common task of result set translation from a tabular representation to a graph of objects.
|
||||
|
||||
Hibernate not only takes care of the mapping from Java classes to
|
||||
database tables (and from Java data types to SQL data types), but also
|
||||
provides data query and retrieval facilities. It can significantly
|
||||
reduce development time otherwise spent with manual data handling in SQL
|
||||
and JDBC. Hibernate’s design goal is to relieve the developer from 95%
|
||||
of common data persistence-related programming tasks by eliminating the
|
||||
need for manual, hand-crafted data processing using SQL and JDBC.
|
||||
However, unlike many other persistence solutions, Hibernate does not
|
||||
hide the power of SQL from you and guarantees that your investment in
|
||||
relational technology and knowledge is as valid as always.
|
||||
=== Get Involved
|
||||
|
||||
Hibernate may not be the best solution for data-centric applications
|
||||
that only use stored procedures to implement the business logic in the
|
||||
database, it is most useful with object-oriented domain models and
|
||||
business logic in the Java-based middle-tier. However, Hibernate can
|
||||
certainly help you to remove or encapsulate vendor-specific SQL code and
|
||||
will help with the common task of result set translation from a tabular
|
||||
representation to a graph of objects.
|
||||
* Use Hibernate and report any bugs or issues you find. See http://hibernate.org/issuetracker[Issue Tracker] for details.
|
||||
* Try your hand at fixing some bugs or implementing enhancements. Again, see http://hibernate.org/issuetracker[Issue Tracker].
|
||||
* Engage with the community using mailing lists, forums, IRC, or other ways listed in the http://hibernate.org/community[Community section].
|
||||
* Help improve or translate this documentation. Contact us on the developer mailing list if you have interest.
|
||||
* Spread the word. Let the rest of your organization know about the benefits of Hibernate.
|
||||
|
||||
See http://hibernate.org/orm/contribute/ for information on getting
|
||||
involved.
|
||||
=== Getting Started Guide
|
||||
|
||||
New users may want to first look through the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/quickstart/html_single/[Hibernate Getting Started Guide] for basic information as well as tutorials.
|
||||
There is also a series of http://docs.jboss.org/hibernate/orm/{majorMinorVersion}/topical/html_single/[topical guides] providing deep dives into various topics.
|
||||
|
||||
[TIP]
|
||||
[NOTE]
|
||||
====
|
||||
If you are just getting started with using Hibernate you may want to
|
||||
start with the Hibernate Getting Started Guide available from the
|
||||
http://hibernate.org/orm/documentation[documentation page]. It contains
|
||||
quick-start style tutorials as well as lots of introductory information.
|
||||
There is also a series of topical guides providing deep dives into
|
||||
various topics.
|
||||
While having a strong background in SQL is not required to use Hibernate, it certainly helps a lot because it all boils down to SQL statements.
|
||||
Probably even more important is an understanding of data modeling principles.
|
||||
You might want to consider these resources as a good starting point:
|
||||
|
||||
* http://en.wikipedia.org/wiki/Data_modeling[Data Modeling Wikipedia definition]
|
||||
* http://www.agiledata.org/essays/dataModeling101.html[Data Modeling 101]
|
||||
|
||||
Understanding the basics of transactions and design patterns such as _Unit of Work_ <<Bibliography.adoc#PoEAA,PoEAA>> or _Application Transaction_ are important as well.
|
||||
These topics will be discussed in the documentation, but a prior understanding will certainly help.
|
||||
====
|
||||
|
|
|
@ -38,8 +38,8 @@ task assembleDocumentation(type: Task, dependsOn: [rootProject.project( 'documen
|
|||
|
||||
// Integrations Guide
|
||||
copy {
|
||||
from "${rootProject.project( 'documentation' ).buildDir}/docbook/publish/integrationsGuide/en-US"
|
||||
into "${documentationDir}/integrationsGuide"
|
||||
from "${rootProject.project( 'documentation' ).buildDir}/asciidoc/integrationguide"
|
||||
into "${documentationDir}/integrationguide"
|
||||
}
|
||||
|
||||
// Getting-started Guide
|
||||
|
|
Loading…
Reference in New Issue