Hibernate.orgCommunity Documentation
Los mapeos objeto/relacional usualmente se definen en un documento XML. El documento de mapeo está diseñado para que se pueda leer y editar a mano. El lenguaje de mapeo está centrado en Java, lo que significa que los mapeos se construyen alrededor de declaraciones de clases persistentes y no alrededor de declaraciones de tablas.
Observe que, incluso aunque muchos de los usuarios de Hibernate eligen escribir el XML a mano, existe un número de herramientas para generar el documento de mapeo, incluyendo XDoclet, Middlegen y AndroMDA.
Este es un ejemplo de mapeo:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat"
discriminator-value="D">
<property name="name"
type="string"/>
</subclass>
</class>
<class name="Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping
>
Ahora vamos a discutir el contenido del documento de mapeo. Sólamente describiremos los elementos y atributos del documento que Hibernate utiliza en tiempo de ejecución. El documento de mapeo también comprende algunos atributos y elementos extra opcionales que afectan los esquemas de la base de datos exportados por la herramienta de exportación de esquemas (por ejemplo, el atributo not-null
).
Todos los mapeos XML deben declarar el tipo de documento que se muestra. El DTD en sí se puede encontrar en la URL mencionada anteriormente, en el directorio hibernate-x.x.x/src/org/hibernate
, o en hibernate3.jar
. Hibernate siempre buscará el DTD primero en la ruta de clase. Si el DTD realiza búsquedas utilizando una conexión de Internet, verifique que su declaración DTD frente al contenido de su ruta de clase.
Hibernate tratará primero de resolver los DTDs en su ruta de clase. La manera en que lo hace es registrando una implementación org.xml.sax.EntityResolver
personalizada con el SAXReader que utiliza para leer los archivos xml. Este EntityResolver
personalizado reconoce dos diferentes espacios de nombre del identificador del sistema.
un hibernate namespace
se reconoce cuando el resolvedor se encuentra con un identificador de sistema que inicia por http://hibernate.sourceforge.net/
. El resolvedor intenta resolver estas entidades por medio del cargador de clases, el cual cargó las clases de Hibernate.
un user namespace
se reconoce cuando el resolvedor se encuentra con un identificador del sistema utilizando un protocolo URL classpath://
. El resolvedor intentará resolver estas entidades por medio de (1) el cargador de clase del contexto del hilo actual y (2) el cargador de clase, el cual cargó las clases de Hibernate.
Este es un ejemplo de la utilización de los espacios de nombre del usuario:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC '-//Hibernate/Hibernate Mapping DTD 3.0//EN' 'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd' [
<!ENTITY version "3.5.6-Final">
<!ENTITY today "September 15, 2010">
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>
<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
<class>
&types;
</hibernate-mapping>
En donde types.xml
es un recurso en el paquete your.domain
y comprende un typedef personalizado.
Este elemento tiene varios atributos opcionales. Los atributos schema
y catalog
especifican que las tablas a las que se refiere en este mapeo pertenecen al esquema y/o catálogo mencionado(s). De especificarse, los nombres de tablas serán calificados por el nombre del esquema y del catálogo dados. De omitirse, los nombres de las tablas no serán calificados. El atributo default-cascade
especifica qué estilo de cascada se debe asumir para las propiedades y colecciones que no especifican un atributo cascade
. Por defecto, el atributo auto-import
nos permite utilizar nombres de clase sin calificar en el lenguaje de consulta.
<hibernate-mapping schema="schemaName" catal
og="catalogName" defau
lt-cascade="cascade_style" defau
lt-access="field|property|ClassName" defau
lt-lazy="true|false" auto-
import="true|false" packa
ge="package.name" />
| |
| |
| |
| |
| |
| |
|
Si tiene dos clases persistentes con el mismo nombre (sin calificar), debe establecer auto-import="false"
. Se presentará una excepción si usted intenta asignar dos clases al mismo nombre "importado".
El elemento hibernate-mapping
le permite anidar varios mapeos <class>
persistentes, como se mostró anteriormente. Sin embargo, es una buena práctica (y algunas herramientas esperan) que mapee sólamente una clase persistente, o a una sóla jerarquía de clases, en un archivo de mapeo y nombrarlo como la superclase persistente. Por ejemplo, Cat.hbm.xml
, Dog.hbm.xml
, o si utiliza herencia, Animal.hbm.xml
.
Puede declarar una clase persistente utilizando el elemento class
. Por ejemplo:
<class name="ClassName" table=
"tableName" discri
minator-value="discriminator_value" mutabl
e="true|false" schema
="owner" catalo
g="catalog" proxy=
"ProxyInterface" dynami
c-update="true|false" dynami
c-insert="true|false" select
-before-update="true|false" polymo
rphism="implicit|explicit" where=
"arbitrary sql where condition" persis
ter="PersisterClass" batch-
size="N" optimi
stic-lock="none|version|dirty|all" lazy="(16)true|false" entity(17)-name="EntityName" check=(18)"arbitrary sql check condition" rowid=(19)"rowid" subsel(20)ect="SQL expression" abstra(21)ct="true|false" node="element-name" />
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
(16) |
|
(17) |
|
(18) |
|
(19) |
|
(20) |
|
(21) |
|
Es perfectamente aceptable que la clase persistente mencionada sea una interfaz. Puede declarar clases que implementan esa interfaz utilizando el elemento <subclass>
. Puede persistir cualquier clase interna estática. Debe especificar el nombre de la clase utilizando la forma estándar, por ejemplo, e.g.Foo$Bar
.
Las clases inmutables, mutable="false"
, no pueden ser actualizadas o borradas por la aplicación. Esto le permite a Hibernate realizar ciertas optimizaciones menores de rendimiento.
El atributo opcional proxy
activa la inicialización perezosa de instancias persistentes de la clase. Hibernate inicialmente retornará proxies CGLIB que implementan la interfaz mencionada. El objeto persistente real será cargado cuando se invoque un método del proxy. Vea "Inicialización de colecciones y proxies" a continuación.
Por polimorfismo implícito se entiende que las instancias de la clase serán devueltas por una consulta que mencione cualquier superclase, o interfaz implementada, o la clase misma; y que las instancias de cualquier subclase de la clase serán retornadas por una petición que nombra a la clase misma. Por polimorfismo explícito se entiende que las instancias de la clase serán devueltas sólo por consultas que mencionen explícitamente la clase. Las consultas que mencionen la clase retornarán sólo instancias de subclases mapeadas dentro de esta declaración <class>
como una <subclass>
o <joined-subclass>
. Para la mayoría de los propósitos el valor por defecto, polymorphism="implicit"
, resulta apropiado. El polimorfismo explícito es útil cuando dos clases diferentes se encuentran mapeadas a la misma tabla. Esto permite tener una clase "liviana" que contenga un subconjunto de columnas de la tabla.
El atributo persister
le permite personalizar la estrategia de persistencia para la clase. Por ejemplo, puede especificar su propia subclase de org.hibernate.persister.EntityPersister
, o incluso puede proporcionar una implementación completamente nueva de la interfaz org.hibernate.persister.ClassPersister
que implemente, por ejemplo, la persistencia por medio de llamadas a procedimientos almacenados, serialización a archivos planos o LDAP. Para ver un ejemplo simple (de "persistencia" a una Hashtable
) consulte org.hibernate.test.CustomPersister
.
Los valores de dynamic-update
y dynamic-insert
no son heredados por las subclases y por lo tanto deben especificarse en los elementos <subclass>
o <joined-subclass>
. Aunque en algunos casos, estos ajustes pueden incrementar el rendimiento, de hecho en otros casos, podrían disminuirlo.
El uso de select-before-update
disminuirá el rendimiento. Es muy útil prevenir que se llame innecesariamente a un disparador de actualización de la base de datos al volver a unir un gráfico de instancias separadas a una Session
.
Si activa dynamic-update
, usted tendrá la opción de estrategias de bloqueo optimistas:
version
: chequea las columnas de versión/sello de fecha
all
: chequea todas las columnas
dirty
: chequea las columnas modificadas permitiendo algunas actualizaciones concurrentes
none
: no utilice bloqueo optimista
Le recomendamos mucho que utilice columnas de versión/sello de fecha para el bloqueo optimista con Hibernate. Esta estrategia optimiza el rendimiento y maneja correctamente las modificaciones realizadas a las instancias separadas, (por ejemplo, cuando se utiliza Session.merge()
).
Para un mapeo de Hibernate, no hay diferencia entre una vista y una tabla base. Esto es transparente a nivel de base de datos, aunque algunos DBMS no soportan correctamente las vistas, especialmente con las actualizaciones. A veces usted quiere utilizar una vista, pero no puede crear una en la base de datos (por ejemplo, con un esquema heredado). En este caso, usted puede mapear una entidad inmutable de sólo lectura a una expresión de subconsulta SQL dada.
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class
>
Declara las tablas con las cuales se debe sincronizar esta entidad, asegurándose de que el auto-vaciado ocurra correctamente y que las consultas frente a la entidad derivada no devuelvan datos desactualizados. El <subselect>
se encuentra disponible tanto como un atributo y como un elemento anidado de mapeo.
Las clases mapeadas tienen que declarar la columna de clave primaria de la tabla de la base de datos. La mayoría de las clases también tendrán una propiedad de estilo Javabeans que tenga el identificador único de una instancia. El elemento <id>
define el mapeo de esa propiedad a la columna de clave primaria.
<id name="propertyName" type="
typename" column
="column_name" unsave
d-value="null|any|none|undefined|id_value" access
="field|property|ClassName"> node="element-name|@attribute-name|element/@attribute|." <generator class="generatorClass"/> </id >
| |
| |
| |
| |
|
Si se omite el atributo name
, se asume que la clase no tiene propiedad identificadora.
El atributo unsaved-value
casi nunca se necesita en Hibernate3.
Hay una declaración <composite-id>
opcional para permitir acceso a los datos heredados con claves compuestas. Le disuadimos seriamente de su utilización para cualquier otra cosa.
El elemento hijo opcional <generator>
nombra una clase Java utilizada para generar identificadores únicos para instancias de la clase persistente. De requerirse algún parámetro para configurar o inicializar la instancia del generador, se pasa utilizando el elemento <param>
.
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table"
>uid_table</param>
<param name="column"
>next_hi_value_column</param>
</generator>
</id
>
Todos los generadores implementan la interfaz org.hibernate.id.IdentifierGenerator
. Esta es una interfaz muy simple. Algunas aplicaciones pueden decidir brindar sus propias implementaciones especializadas. Sin embargo, Hibernate provee un rango de implementaciones ya incorporadas. Los nombres de atajo para los generadores incorporados son los siguientes:
increment
genera indentificadores de tipo long
, short
o int
que sólamente son únicos cuando ningún otro proceso está insertando datos en la misma tabla. No lo utilice en un clúster.
identity
soporta columnas de identidad en DB2, MySQL, MS SQL Server, Sybase y HypersonicSQL. El identificador devuelto es de tipo long
, short
o int
.
sequence
usa una secuencia en DB2, PostgreSQL, Oracle, SAP DB, McKoi o un generador en Interbase. El identificador devuelto es de tipo long
, short
o int
.
hilo
utiliza un algoritmo alto/bajo para generar eficientemente identificadores de tipo long
, short
o int
, dada una tabla y columna como fuente de valores altos (por defecto hibernate_unique_key
y next_hi
respectivamente). El algoritmo alto/bajo genera identificadores que son únicos sólamente para una base de datos particular.
seqhilo
utiliza un algoritmo alto/bajo para generar eficientemente identificadores de tipo long
, short
o int
, dada una secuencia de base de datos.
uuid
utiliza un algoritmo UUID de 128 bits para generar identificadores de tipo cadena, únicos dentro de una red (se utiliza la direccón IP). El UUID se codifica como una cadena hexadecimal de 32 dígitos de largo.
guid
utiliza una cadena GUID generada por base de datos en MS SQL Server y MySQL.
native
selecciona identity
, sequence
o hilo
dependiendo de las capacidades de la base de datos subyacente.
assigned
deja a la aplicación asignar un identificador al objeto antes de que se llame a save()
. Esta es la estrategia por defecto si no se especifica un elemento <generator>
.
select
recupera una clave principal asignada por un disparador de base de datos seleccionando la fila por alguna clave única y recuperando el valor de la clave principal.
foreign
utiliza el identificador de otro objeto asociado. Generalmente se usa en conjunto cón a una asociación de clave principal <one-to-one>
.
sequence-identity
una estrategia de generación de secuencias especilizadas que utiliza una secuencia de base de datos para el valor real de la generación, pero combina esto junto con JDBC3 getGeneratedKeys para devolver el valor del identificador generado como parte de la ejecución de la declaración de inserción. Esta estrategia está soportada sólamente en los controladores 10g de Oracle destinados para JDK1.4. Los comentarios en estas declaraciones de inserción están desactivados debido a un error en los controladores de Oracle.
Los generadores hilo
y seqhilo
brindan dos implementaciones opcionales del algoritmo alto/bajo. La primera implementación necesita de una tabla "especial" de base de datos para tener el siguiente valor "alto" disponible. La segunda utiliza una secuencia del estilo de Oracle, donde se encuentre soportada.
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table"
>hi_value</param>
<param name="column"
>next_value</param>
<param name="max_lo"
>100</param>
</generator>
</id
>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence"
>hi_value</param>
<param name="max_lo"
>100</param>
</generator>
</id
>
Desafortunadamente, no puede utilizar hilo
cuando le provea su propia Connection
a Hibernate. Cuando Hibernate está utilizando una fuente de datos del servidor de aplicaciones para obtener conexiones alistadas con JTA, usted tiene que configurar el hibernate.transaction.manager_lookup_class
.
El UUID contiene: la dirección IP, el tiempo de iniciación de la MVJ, con una precisión de un cuarto de segundo, el tiempo de sistema y un valor de contador (único en la MVJ). No es posible obtener una dirección MAC o una dirección de memoria desde el código Java, así que esto es la mejor opción sin tener que utilizar JNI.
Para las bases de datos que soportan columnas de identidad (DB2, MySQL, Sybase, MS SQL), puede utilizar generación de claves identity
. Para las bases de datos que soportan las secuencias (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) puede utilizar la generación de claves del estilo sequence
. Ambas estrategias requieren dos consultas SQL para insertar un nuevo objeto. Por ejemplo:
<id name="id" type="long" column="person_id">
<generator class="sequence">
<param name="sequence"
>person_id_sequence</param>
</generator>
</id
>
<id name="id" type="long" column="person_id" unsaved-value="0">
<generator class="identity"/>
</id
>
Para desarrollos a través de plataformas, la estrategia native
eligirá entre las estrategias identity
, sequence
e hilo
, dependiendo de las capacidades de la base de datos subyacente.
Si quiere que la aplicación asigne los identificadores, en contraposición a que los genere Hibernate, puede utilizar el generador assigned
. Este generador especial utilizará el valor identificador ya asignado a la propiedad identificadora del objeto. Este generador se utiliza cuando la clave principal es una clave natural en vez de una clave sustituta. Este es el comportamiento por defecto si no especifica un elemento <generator>
.
El generador assigned
hace que Hibernate utilice unsaved-value="undefined"
. Esto fuerza a Hibernate a ir a la base de datos para determinar si una instancia es transitoria o separada, a menos de que haya una propiedad de versión o sello de fecha, o que usted defina Interceptor.isUnsaved()
.
Hibernate no genera DDL con disparadores. Es para los esquemas heredados sólamente.
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key"
>socialSecurityNumber</param>
</generator>
</id
>
En el ejemplo anterior, hay una propiedad única llamada socialSecurityNumber
, Esta está definida por la clase, como una clave natural y una clave sustituta llamada person_id
, cuyo valor es generado por un disparador.
Desde el lanzamiento 3.2.3, hay 2 nuevos generadores, los cuales representan una nueva reflexión sobre dos aspectos diferentes de la generación del identificador. El primer aspecto es qúe tan portátil es la base de datos; el segudno es la optimización. La optimización significa que no tiene que preguntarle a la base de datos por toda petición de un nuevo valor identificador. Estos dos nuevos generadores tienen el propósito de tomar el lugar de algunos de los generadores nombrados que describimos anteriormente, empezando por 3.3.x. Sin embargo, están incluídos en los lanzamientos actuales y puede ser referenciados por FQN.
El primero de estos nuevos generadores es org.hibernate.id.enhanced.SequenceStyleGenerator
, el cual tiene el propósito, primero, de ser el reemplazo para el generador sequence
y segundo, de ser un generador de portabilidad mejor que native
. Esto se debe a que native
generalmente escoge entre identity
y sequence
, los cuales tienen una gran diferencia semántica que puede crear problemas sutiles en las aplicaciones mirando la portabilidad. Sin embargo, org.hibernate.id.enhanced.SequenceStyleGenerator
, logra la portabilidad de una manera diferente. Escoge entre una tabla o una secuencia en la base de datos para almacenar sus valores en subida, dependiendo de las capacidades del dialecto que se está utilizando. La diferencia enter esto y native
es que el almacenamiento basado en tablas y secuencias tienen la misma semántica. De hecho, las secuencias son exactamente lo que Hibernate trata de emular con sus generadores basados en tablas. Este generador tiene un número de parámetros de configuración:
sequence_name
(opcional, por defecto es hibernate_sequence
): el nombre de la secuencia o la tabla a utilizar.
initial_value
(opcional, por defecto es 1
): el valor inicial a recuperarse de la secuencia/tabla. En términos de creación de secuencias, esto es análogo a la cláusula que usualmente se llama "STARTS WITH".
increment_size
(opcional - por defecto es 1
): el valor por el cual las llamadas subsecuentes a la secuencia/tabla deben diferir. En términos de creación de secuencias, esto es análogo a la cláusula que usualmente se llama "INCREMENT BY".
force_table_use
(opcional - por defecto es false
): ¿debemos forzar el uso de una tabla como la estructura de respaldo aunque puede que el dialecto soporte la secuencia?
value_column
(opcional - por defecto es next_val
): solo es relevante para estructuras de tablas, es el nombre de la columna en la tabla, la cual se usa para mantener el valor.
optimizer
(optional - defaults to none
): See Sección 5.1.6, “Optimización del generador del identificador”
El segundo de estos nuevos generadores es org.hibernate.id.enhanced.TableGenerator
, el cual tiene el propósito, primero, de reemplazar el generador table
, auqnue de hecho funciona como org.hibernate.id.MultipleHiLoPerTableGenerator
, y segundo, como una re-implementación de org.hibernate.id.MultipleHiLoPerTableGenerator
que utiliza la noción de los optimizadores enchufables. Esencialmente, este generador define una tabla capaz de mantener un número de valores de incremento diferentes de manera simultánea usando múltiples filas tecleadas claramente. Este generador tiene un número de parámetros de configuración:
table_name
(opcional - por defecto es hibernate_sequences
): el nombre de la tabla a utilizar.
value_column_name
(opcional - por defecto es next_val
): el nombre de la columna en la tabla que se utiliza para mantener el valor.
segment_column_name
(opcional - por defecto es sequence_name
): el nombre de la columna en la tabla que se utiliza para mantener la "llave segmento". Este es el valor que identifica que valor de incremento utilizar.
segment_value
(opcional - por defecto es default
): El valor "llave segmento" para el segmento desde el cual queremos sacar los valores de incremento para este generador.
segment_value_length
(opcional - por defecto es 255
): Se utiliza para la generación de esquemas; el tamaño de la columna a crear esta columna de llave de segmento.
initial_value
(opcional - por defecto es 1
): El valor inicial a recuperar de la tabla.
increment_size
(opcional - por defecto es 1
): El valor por el cual deben diferir las llamadas subsecuentes a la tabla.
optimizer
(optional - defaults to ): See Sección 5.1.6, “Optimización del generador del identificador”
For identifier generators that store values in the database, it is inefficient for them to hit the database on each and every call to generate a new identifier value. Instead, you can group a bunch of them in memory and only hit the database when you have exhausted your in-memory value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators (Sección 5.1.5, “Generadores mejorados del identificador” support this operation.
none
(generalmente este el es valor predeterminado si no se especifica un optimizador): esto no realizará ninguna optimización y accederá a la base de datos para toda petición.
hilo
: aplica un algoritmo hi/lo a los valores recuperados de la base de datos. Se espera que los valores de la base de datos para este optimizador sean secuenciales. Los valores recuperados de la estructura de la base de datos para este optimizador indican el "número del grupo". El increment_size
se multiplica por ese valor en la memoria para definir un grupo "hi value".
pooled
: como en el caso de hilo
, este optimizador trata de minimizar el número de hits a la base de datos. Sin embargo, aquí simplemente almacenamos el valor inicial para el "siguiente grupo" en la estructura de la base de datos en lugar de un valor secuencial en combinación con un algoritmo de agrupamiento en-memoria. Aquí, increment_size
ser refiere a los valores que provienen de la base de datos.
<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
access="field|property|ClassName">
node="element-name|."
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName" class="ClassName" column="column_name"/>
......
</composite-id
>
Una tabla con clave compuesta se puede mapear con múltiples propiedades de la clase como propiedades identificadoras. El elemento <composite-id>
acepta los mapeos de propiedad <key-property>
y los mapeos <key-many-to-one>
como elementos hijos.
<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id
>
La clase persistente tiene que sobrescribir equals()
y hashCode()
para implementar la igualdad del identificador compuesto. También tiene que implementar Serializable
.
Desafortunadamente, este enfoque significa que un objeto persistente es su propio identificador. No existe otra "asa" conveniente más que el objeto mismo. Debe instanciar una instancia de la clase persistente y poblar sus propiedades identificadoras antes de que pueda load()
el estado persistente asociado a una clave compuesta. Este enfoque lo denominamos un identificador compuesto incluído y no lo recomendamos para aplicaciones serias.
Un segundo enfoque es lo que denominamos un identificador compuesto mapeado, en donde las propiedades del identificador nombradas dentro del elemento <composite-id>
son duplicadas tanto en la clase persistente como en la clase identificadora separada.
<composite-id class="MedicareId" mapped="true">
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id
>
En este ejemplo, tanto la clase identificadora compuesta MedicareId
como la clase de entidad misma tienen propiedades denominadas medicareNumber
y dependent
. La clase identificadora tiene que sobrescribir equals()
y hashCode()
e implementar Serializable
. La desventaja principal de este enfoque es la duplicación de código.
Los siguientes atributos se utilizan para especificar un identificador compuesto mapeado:
mapped
(opcional, por defecto es false
): indica que se utiliza un identificador compuesto mapeado y que los mapeos de propiedad contenidos se refieren tanto a la clase de entidad como a la clase identificadora compuesta.
class
(opcional, pero requerida por un identificador compuesto mapeado): La clase se utiliza como un identificador compuesto.
We will describe a third, even more convenient approach, where the composite identifier is implemented as a component class in Sección 8.4, “Componentes como identificadores compuestos”. The attributes described below apply only to this alternative approach:
name
(opcional, se necesita para este enfoque): Una propiedad de tipo componente que tiene el identificador compuesto. Consulte el capítulo 9 para obtener mayor información.
access
(opcional - por defecto es property
): La estrategia que Hibernate utiliza para acceder al valor de la propiedad.
class
(opcional - por defecto es el tipo de propiedad determinado por la reflección): la clase componente utilizada como un identificador compuesto. Vea la siguiente sección para obtener mayor información.
Este tercer enfoque, un componente identificador es el que recomendamos para casi todas las aplicaciones.
Se necesita el elemento <discriminator>
para la persistencia polimórfica utilizando la estrategia de mapeo de tabla-por-jerarquía-de-clases. Declara una columna discriminadora de la tabla. La columna discriminidora contiene valores de marca que le dicen a la capa de persistencia qué subclase instanciar para una fila en particular. Se puede utilizar un conjunto restringido de tipos: string
, character
, integer
, byte
, short
, boolean
, yes_no
, true_false
.
<discriminator column="discriminator_column" type="
discriminator_type" force=
"true|false" insert
="true|false" formul
a="arbitrary sql expression" />
| |
| |
| |
| |
|
Los valores reales de la columna discriminadora están especificados por el atributo discriminator-value
de los elementos <class>
y <subclass>
.
El atributo force
es sólamente útil si la tabla contiene filas con valores discriminadores "extra" que no estén mapeados a una clase persistente. Generalmente este no es el caso.
El atributo formula
le permite declarar una expresión SQL arbitraria que será utilizada para evaluar el tipo de una fila. Por ejemplo:
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
El elemento <version>
es opcional e indica que la tabla contiene datos versionados. Esto es particularmente útil si planea utilizar transacciones largas. Vea a continuación para obtener mayor información:
<version column="version_column" name="
propertyName" type="
typename" access
="field|property|ClassName" unsave
d-value="null|negative|undefined" genera
ted="never|always" insert
="true|false" node="element-name|@attribute-name|element/@attribute|." />
| |
| |
| |
| |
| |
| |
|
Los números de versión pueden ser de tipo Hibernate long
, integer
, short
, timestamp
o calendar
.
Una propiedad de versión o de sello de fecha nunca debe ser nula para una instancia separada. Hibernate detectará cualquier instancia con una versión o sello de fecha nulo como transitoria, sin importar qué otras estrategias unsaved-value
se hayan especificado. El declarar una propiedad de versión o sello de fecha nulable es una forma fácil de evitar cualquier problema con la re-unión transitiva en Hibernate. Es especialmente útil para la gente que utiliza identificadores asignados o claves compuestas.
El elemento opcional <timestamp>
indica que la tabla contiene datos con sellos de fecha. Esto brinda una alternativa al versionado. Los sellos de tiempo (timestamps) son por naturaleza una implementación menos segura del bloqueo optimista. Sin embargo, a veces la aplicación puede usar los sellos de fecha de otras maneras.
<timestamp column="timestamp_column" name="
propertyName" access
="field|property|ClassName" unsave
d-value="null|undefined" source
="vm|db" genera
ted="never|always" node="element-name|@attribute-name|element/@attribute|." />
| |
| |
| |
| |
| |
|
<Timestamp>
es equivalente a <version type="timestamp">
. Y <timestamp source="db">
es equivalente a <version type="dbtimestamp">
.
El elemento <property>
declara una propiedad persistente estilo JavaBean de la clase.
<property name="propertyName" column
="column_name" type="
typename" update
="true|false" insert
="true|false" formul
a="arbitrary SQL expression" access
="field|property|ClassName" lazy="
true|false" unique
="true|false" not-nu
ll="true|false" optimi
stic-lock="true|false" genera
ted="never|insert|always" node="element-name|@attribute-name|element/@attribute|." index="index_name" unique_key="unique_key_id" length="L" precision="P" scale="S" />
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
escribanombre puede ser:
El nombre de un tipo básico de Hibernate: integer, string, character, date, timestamp, float, binary, serializable, object, blob
, etc.
El nombre de una clase Java con un tipo básico predeterminado: int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob
, etc.
El nombre de una clase Java serializable.
El nombre declase de un tipo personalizado: com.illflow.type.MyCustomType
etc.
Si no especifica un tipo, Hibernate utilizará reflección sobre la propiedad mencionada para deducir el tipo Hibernate correcto. Hibernate intentará interpretar el nombre de la clase de retorno del getter de la propiedad utilizando las reglas 2, 3 y 4 en ese mismo orden. En algunos casos necesitará el atributo type
. Por ejemplo, para distinguir entre Hibernate.DATE
y Hibernate.TIMESTAMP
, o especificar un tipo personalizado.
El atributo access
le permite controlar el cómo Hibernate accederá a la propiedad en tiempo de ejecución. Por defecto, Hibernate llamará al par de getter/setter de la propiedad. Si usted especifica access="field"
, Hibernate se saltará el par get/set y accederá al campo directamente utilizando reflección. Puede especificar su propia estrategia de acceso a la propiedad mencionando una clase que implemente la interfaz org.hibernate.property.PropertyAccessor
.
Una funcionalidad especialmente poderosa son las propiedades derivadas. Estas propiedades son, por definición, de sólo lectura. El valor de la propiedad se computa en tiempo de carga. Usted declara la computación como una expresión SQL y ésta se traduce como una cláusula de subconsulta SELECT
en la consulta SQL que carga una instancia:
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
Puede referenciar la tabla de las entidades sin declarar un alias o una columna particular. En el ejemplo dado sería customerId
. También puede utilizar el elemento anidado de mapeo <formula>
si no quiere utilizar el atributo.
Una asociación ordinaria a otra clase persistente se declara utilizando el elemento many-to-one
. El modelo relacional es una asociación muchos-a-uno; una clave foránea en una tabla referencia la columna (o columnas) de la clave principal de la tabla destino.
<many-to-one name="propertyName" column
="column_name" class=
"ClassName" cascad
e="cascade_style" fetch=
"join|select" update
="true|false" insert
="true|false" proper
ty-ref="propertyNameFromAssociatedClass" access
="field|property|ClassName" unique
="true|false" not-nu
ll="true|false" optimi
stic-lock="true|false" lazy="
proxy|no-proxy|false" not-fo
und="ignore|exception" entity
-name="EntityName" formul
a="arbitrary SQL expression" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" index="index_name" unique_key="unique_key_id" foreign-key="foreign_key_name" />
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
Setting a value of the cascade
attribute to any meaningful value other than none
will propagate certain operations to the associated object. The meaningful values are divided into three categories. First, basic operations, which include: persist, merge, delete, save-update, evict, replicate, lock and refresh
; second, special values: delete-orphan
; and third, all
comma-separated combinations of operation names: cascade="persist,merge,evict"
or cascade="all,delete-orphan"
. See Sección 10.11, “Persistencia transitiva” for a full explanation. Note that single valued, many-to-one and one-to-one, associations do not support orphan delete.
Este es un ejemplo de una declaración típica muchos-a-uno
:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
El atributo property-ref
se debe utilizar sólamente para el mapeo de datos heredados donde una clave foránea referencia una clave única de la tabla asociada, distinta de la clave principal. Este es un modelo relacional complicado y confuso. Por ejemplo, si la clase Product
tuviera un número único serial que no es la clave principal, el atributo unique
controla la generación de DDL de Hibernate con la herramienta SchemaExport.
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
Entonces el mapeo para OrderItem
puede utilizar:
<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>
Sin embargo, esto ciertamente no se aconseja.
Si la clave única referenciada abarca múltiples propiedades de la entidad asociada, debe mapear las propiedades dentro de un elemento nombrado <properties>
.
Si la clave única referenciada es propiedad de un componente, usted puede especificar una ruta de propiedad:
<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>
Una asociación uno-a-uno (one-to-one) a otra clase persistente se declara utilizando un elemento one-to-one
.
<one-to-one name="propertyName" class=
"ClassName" cascad
e="cascade_style" constr
ained="true|false" fetch=
"join|select" proper
ty-ref="propertyNameFromAssociatedClass" access
="field|property|ClassName" formul
a="any SQL expression" lazy="
proxy|no-proxy|false" entity
-name="EntityName" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" foreign-key="foreign_key_name" />
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
Existen dos variedades de asociaciones uno-a-uno:
asociaciones de clave primaria
asociaciones de clave foránea única
Las asociaciones de claves principales no necesitan una columna extra de la tabla. Si dos filas están relacionadas por la asociación entonces las dos filas de tablas comparten el mismo valor de clave principal. Para que dos objetos estén relacionados por una asociación de clave principal, asegúrese de que se les asigne el mismo valor de identificador.
Para una asociación de clave principal, agregue los siguientes mapeos a Employee
y Person
respectivamente:
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
Asegúrese de que las claves principales de las filas relacionadas en las tablas PERSON y EMPLOYEE sean iguales. Utilizamos una estrategia especial de generación de identificador de Hibernate denominada foreign
:
<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property"
>employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class
>
A una instancia recién guardada de Person
se le asigna el mismo valor de clave principal que se le asignó a la instancia Employee
referida por la propiedad employee
de esa Person
.
Opcionalmente, una clave foránea con una restricción de unicidad, desde Employee
a Person
, se puede expresar como:
<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>
Esta asociación puede hacerse bidireccional agregando lo siguiente al mapeo de Person
:
<one-to-one name="employee" class="Employee" property-ref="person"/>
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id
>
Aunque recomendamos el uso de claves delegadas como claves principales, debe tratar de identificar claves naturales para todas las entidades. Una clave natural es una propiedad o combinación de propiedades que es única y no nula. También es inmutable. Mapea las propiedades de la clave natural dentro del elemento <natural-id>
. Hibernate generará las restricciones de nulabilidad y de clave única necesarias y su mapeo será más auto-documentado.
Le recomendamos bastante que implemente equals()
y hashCode()
para comparar las propiedades de clave natural de la entidad.
Este mapeo no está concebido para la utilización con entidades que tienen claves principales naturales.
mutable
(opcional - por defecto es false
): Por defecto, se asume que las propiedades de identificadores naturales son inmutables (constantes).
El elemento <component>
mapea propiedades de un objeto hijo a columnas de la tabla de la clase padre. Los componentes pueden, a su vez, declarar sus propias propiedades, componentes o colecciones. Vea a continuación los "componentes":
<component name="propertyName" class=
"className" insert
="true|false" update
="true|false" access
="field|property|ClassName" lazy="
true|false" optimi
stic-lock="true|false" unique
="true|false" node="element-name|." > <property ...../> <many-to-one .... /> ........ </component >
| |
| |
| |
| |
| |
| |
| |
|
Las etiquetas hijas <property>
mapean propiedades de la clase hija a las columnas de la tabla.
El elemento <component>
permite un subelemento <parent>
que mapea una propiedad de la clase del componente como una referencia a la entidad contenedora.
The <dynamic-component>
element allows a Map
to be mapped as a component, where the property names refer to keys of the map. See Sección 8.5, “Componentes dinámicos” for more information.
El elemento <properties>
permite la definición de un grupo de propiedades lógico con nombre de una clase. El uso más importante de la contrucción es que permite que una combinación de propiedades sea el objetivo de una property-ref
. También es una forma práctica de definir una restricción de unicidad multicolumna. Por ejemplo:
<properties name="logicalName" insert
="true|false" update
="true|false" optimi
stic-lock="true|false" unique
="true|false" > <property ...../> <many-to-one .... /> ........ </properties >
| |
| |
| |
| |
|
Por ejemplo, si tenemos el siguiente mapeo de <properties>
:
<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class
>
Puede que tenga alguna asociación de datos heredados que se refiera a esta clave única de la tabla de Person
, en lugar de la clave principal:
<many-to-one name="person"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one
>
No recomendamos el uso de este tipo de cosas fuera del contexto del mapeo de datos heredados.
La persistencia polimórfica requiere la declaración de cada subclase de la clase persistente raíz. Para la estrategia de mapeo tabla-por-jerarquía-de-clases, se utiliza la declaración <subclass>
. Por ejemplo:
<subclass name="ClassName" discri
minator-value="discriminator_value" proxy=
"ProxyInterface" lazy="
true|false" dynamic-update="true|false" dynamic-insert="true|false" entity-name="EntityName" node="element-name" extends="SuperclassName"> <property .... /> ..... </subclass >
| |
| |
| |
|
Cada subclase debe declarar sus propias propiedades persistentes y subclases. Se asume que las propiedades <version>
y <id>
son heredadas de la clase raíz. Cada subclase en una jerarquía tiene que definir un discriminator-value
único. Si no se especifica ninguno entonces se utiliza el nombre completamente calificado de clase Java.
For information about inheritance mappings see Capítulo 9, Mapeo de herencias.
Se puede mapear cada subclase a su propia tabla. Esto se llama una estrategia de mapeo tabla-por-subclase. El estado heredado se recupera uniendo con la tabla de la superclase. Para hacer esto utilice elemento <joined-subclass>
. Por ejemplo:
<joined-subclass name="ClassName" table=
"tablename" proxy=
"ProxyInterface" lazy="
true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <key .... > <property .... /> ..... </joined-subclass >
| |
| |
| |
|
No se necesita una columna discriminadora para esta estrategia de mapeo. Sin embargo, cada subclase debe declarar una columna de tabla que tenga el identificador del objeto utilizando el elemento <key>
. El mapeo mencionado al comienzo del capítulo se reescribiría así:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping
>
For information about inheritance mappings see Capítulo 9, Mapeo de herencias.
Una tercera opción es mapear sólo las clases concretas de una jerarquía de herencia a tablas. Esta se llama la estrategia clase concreta por tabla). Cada tabla define todos los estados persistentes de la clase, incluyendo el estado heredado. En Hibernate, no es necesario mapear dichas jerarquías de herencia. Puede mapear cada clase con una declaración <class>
separada. Sin embargo, si desea utilizar asociaciones polimórficas (por ejemplo, una asociación a la superclase de su jerarquía), necesita utilizar el mapeo <union-subclass>
. Por ejemplo:
<union-subclass name="ClassName" table=
"tablename" proxy=
"ProxyInterface" lazy="
true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" abstract="true|false" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <property .... /> ..... </union-subclass >
| |
| |
| |
|
No se necesita una columna o una columna clave discriminadora para esta estrategia de mapeo.
For information about inheritance mappings see Capítulo 9, Mapeo de herencias.
Al utilizar el elemento <join>
, es posible mapear las propiedades de una clase a varias tablas que tengan una relación uno-a-uno. Por ejemplo:
<join table="tablename" schema
="owner" catalo
g="catalog" fetch=
"join|select" invers
e="true|false" option
al="true|false"> <key ... /> <property ... /> ... </join >
| |
| |
| |
| |
| |
|
Por ejemplo, la información domiciliaria de una persona se puede mapear a una tabla separada, preservando a la vez la semántica de tipo de valor para todas las propiedades:
<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID"
>...</id>
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...
Con frecuencia, esta funcionalidad sólamente es útil para los modelos de datos heredados. Recomendamos menos tablas que clases y un modelo de dominio más detallado. Sin embargo, es útil para cambiar entre estrategias de mapeo de herencias en una misma jerarquía, como se explica más adelante.
Hasta ahora hemos visto el elemento <key>
unas cuantas veces. Aparece en cualquier sitio en que el elemento padre de mapeo defina una unión a una nueva tabla que referencie la clave principal de la tabla original. También define la clave foránea en la tabla unida:
<key column="columnname" on-del
ete="noaction|cascade" proper
ty-ref="propertyName" not-nu
ll="true|false" update
="true|false" unique
="true|false" />
| |
| |
| |
| |
| |
|
Para los sistemas en donde el rendimiento es importante, todas las claves deben ser definidas on-delete="cascade"
. Hibernate utiliza una restricción ON CASCADE DELETE
a nivel de base de datos, en vez de muchas declaraciones DELETE
individuales. Tenga en cuenta que esta funcionalidad evita la estrategia de bloqueo optimista normal de Hibernate para datos versionados.
Los atributos not-null
y update
son útiles al mapear una asociación uno a muchos unidireccional. Si mapea una unidireccional uno a muchos a una clave foránea no nulable, tiene que declarar la columna clave utilizando <key not-null="true">
.
Los elementos de mapeo que acepten un atributo column
aceptarán opcionalmente un subelemento <column>
. De manera similar, <formula>
es una alternativa al atributo formula
. Por ejemplo:
<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"
read="SQL expression"
write="SQL expression"/>
<formula
>SQL expression</formula
>
La mayoría de los atributos en column
proporcionan una manera de personalizar el DDL durante la generación del esquema automático. Los atributos read
y write
le permiten especificar SQL personalizado que Hibernate utilizará para acceder el valor de la columna. Para obtener mayor información sobre esto, consulte la discusión sobre expresiones de lectura y escritura de columnas.
Los elementos column
y formula
incluso se pueden combinar dentro del mismo mapeo de propiedad o asociación para expresar, por ejemplo, condiciones de unión exóticas.
<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula
>'MAILING'</formula>
</many-to-one
>
Si su aplicación tiene dos clases persistentes con el mismo nombre y no quiere especificar el nombre del paquete completamenta calificado en las consultas Hibernate, las clases pueden ser "importadas" explícitamente, en lugar de depender de auto-import="true"
. Incluso puede importar clases e interfaces que no estén mapeadas explícitamente:
<import class="java.lang.Object" rename="Universe"/>
<import class="ClassName" rename
="ShortName" />
| |
|
Hay un tipo más de mapeo de propiedad. El elemento de mapeo <any>
define una asociación polimórfica a clases desde múltiples tablas. Este tipo de mapeo necesita más de una columna. La primera columna contiene el tipo de la entidad asociada. Las columnas restantes contienen el identificador. Es imposible especificar una restricción de clave foránea para este tipo de asociación. Esta no es la manera usual de mapear asociaciones polimórficas y sólamente debe usar esto en casos especiales. Por ejemplo, para registros de auditoría, datos de sesión de usuario, etc.
El atributo meta-type
le permite especificar a la aplicación un tipo personalizado que mapea los valores de columnas de la base de datos a clases persistentes que tengan propiedades identificadoras del tipo especificado por id-type
. Tiene que especificar el mapeo de valores del meta-tipo a nombres de clase.
<any name="being" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any
>
<any name="propertyName" id-typ
e="idtypename" meta-t
ype="metatypename" cascad
e="cascade_style" access
="field|property|ClassName" optimi
stic-lock="true|false" > <meta-value ... /> <meta-value ... /> ..... <column .... /> <column .... /> ..... </any >
| |
| |
| |
| |
| |
|
En relación con el servicio de persistencia, los objetos a nivel de lenguaje Java se clasifican en dos grupos:
Una entidad existe independientemente de cualquier otro objeto que referencie a la entidad. Compare esto con el modelo habitual de Java en donde un objeto no referenciado es recolectado como basura. Las entidades deben ser guardadas y borradas explícitamente. Sin embargo, los grabados y borrados se pueden tratar en cascada desde una entidad padre a sus hijos. Esto es diferente al modelo de persistencia de objetos por alcance (ODMG) y corresponde más a cómo se utilizan habitualmente los objetos de aplicación en sistemas grandes. Las entidades soportan referencias circulares y compartidas, que también pueden ser versionadas.
El estado persistente de una entidad consta de las referencias a otras entidades e instancias de tipo valor. Los valores son primitivos: colecciones (no lo que está dentro de la colección), componentes y ciertos objetos inmutables. A diferencia de las entidades, los valores en particular las colecciones y los componentes, son persistidos y borrados por alcance. Como los objetos valor y primitivos son persistidos y borrados junto con sus entidades contenedoras, no se pueden versionar independientemente. Los valores no tienen identidad independiente, por lo que dos entidades o colleciones no los pueden compartir.
Hasta ahora, hemos estado utilizando el término "clase persistente" para referirnos a entidades. Continuaremos haciéndolo así. Sin embargo, no todas la clases con estado persistente definidas por el usuario son entidades. Un componente es una clase definida por el usuario con semántica de valor. Una propiedad Java de tipo java.lang.String
también tiene semántica de valor. Dada esta definición, podemos decir que todos los tipo (clases) provistos por el JDK tienen una semántica de tipo valor en Java, mientras que los tipos definidos por el usuario se pueden mapear con semántica de tipo valor o de entidad. La desición corre por cuenta del desarrollador de la aplicación. Una clase entidad en un modelo de dominio son las referencias compartidas a una sola instancia de esa clase, mientras que la composición o agregación usualmente se traducen a un tipo de valor.
Volveremos a revisar ambos conceptos a lo largo de este manual de referencia.
EL desafío es mapear el sistema de tipos de Java ( la definición de entidades y tipos de valor de los desarrolladores al sistema de tipos de SQL/la base de datos. El puente entre ambos sistemas lo brinda Hibernate. Para las entidades utilizamos <class>
, <subclass>
, etc. Para los tipos de valor utilizamos <property>
, <component>
, etc, usualmente con un atributo type
. El valor de este atributo es el nombre de un tipo de mapeo de Hibernate. Hibernate proporciona un rango de mapeos para tipos de valores del JDK estándar. Puede escribir sus propios mapeos de tipo e implementar sus estrategias de conversión personalizadas.
Todos los tipos incorporados de Hibernate soportan la semántica de nulos, a excepción de las colecciones.
Los tipos de mapeo básicos incorporados se pueden categorizar así:
integer, long, short, float, double, character, byte, boolean, yes_no, true_false
Mapeos de tipos de primitivos de Java o de clases de envoltura a los tipos de columna SQL (específica del vendedor). boolean, yes_no
y true_false
son codificaciones alternativas a boolean
de Java o java.lang.Boolean
.
string
Un mapeo del tipo java.lang.String
a VARCHAR
(u Oracle VAARCHAR2
).
date, time, timestamp
Mapeos de tipo desde java.util.Date
y sus subclases a tipos SQL DATE
, TIME
y TIMESTAMP
(o equivalente).
calendar, calendar_date
Mapeos de tipo desde java.util.Date
y tipos SQL TIMESTAMP
y DATE
(o equivalente).
big_decimal, big_integer
Mapeos de tipo desde java.math.BigDecimal
y java.math.BigInteger
a NUMERIC
(o NUMBER
de Oracle).
locale, timezone, currency
Mapeos de tipo desde java.util.Locale
, java.util.TimeZone
y java.util.Currency
a VARCHAR
(o VARCHAR2
de Oracle). Las instancias de Locale
y Currency
son mapeadas a sus códigos ISO. Las instancias de TimeZone
son mapeadas a sus ID
.
class
Un mapeo de tipo java.lang.Class
a VARCHAR
(o VARCHAR2
de Oracle). Una Class
es mapeada a su nombre completamente calificado.
binary
Mapea arreglos de bytes a un tipo binario SQL apropiado.
text
Mapea cadenas largas de Java al tipo SQL CLOB
o TEXT
.
serializable
Mapea tipos serializables Java a un tipo binario SQL apropiado. También puede indicar el tipo serializable
de Hibernate con el nombre de una clase o interfaz serializable Java que no sea por defecto un tipo básico.
clob, blob
Mapeos de tipo para las clases JDBC java.sql.Clob
y java.sql.Blob
. Estos tipos pueden ser inconvenientes para algunas aplicaciones, pues el objeto blob o clob no pueden ser reusados fuera de una transacción. Además, el soporte del controlador suele ser malo e inconsistente.
imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary
Los mapeos de tipo para lo que usualmente se considera tipos Java mutables. Aquí es donde Hibernate realiza ciertas optimizaciones apropiadas sólamente para tipos Java inmutables y la aplicación trata el objeto como inmutable. Por ejemplo, no debe llamar Date.setTime()
para una instancia mapeada como imm_timestamp
. Para cambiar el valor de la propiedad y hacer que ese cambio sea persistente, la aplicación tiene que asignar un objeto nuevo, no idéntico, a la propiedad.
Los identificadores únicos de entidades y colecciones pueden ser de cualquier tipo básico excepto binary
, blob
y clob
. Los identificadores compuestos también están permitidos, a continuación encontrará mayor información.
Los tipos de valor básicos tienen sus constantes Type
correspondientes definidas en org.hibernate.Hibernate
. Por ejemplo, Hibernate.STRING
representa el tipo string
.
Es relativamente fácil para los desarrolladores crear sus propios tipos de valor. Por ejemplo, puede que quiera persistir propiedades del tipo java.lang.BigInteger
a columnas VARCHAR
. Hibernate no provee un tipo incorporado para esto. Los tipos personalizados no están limitados a mapear una propiedad o elemento de colección a una sola columna de tabla. Así, por ejemplo, podría tener una propiedad Java getName()
/setName()
de tipo java.lang.String
que es persistida a las columnas FIRST_NAME
, INITIAL
, SURNAME
.
Para implementar un tipo personalizado, implemente org.hibernate.UserType
o org.hibernate.CompositeUserType
y declare las propiedades utilizando el nombre de clase completamente calificado del tipo. Revise org.hibernate.test.DoubleStringType
para ver qué clases de cosas son posibles.
<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
<column name="first_string"/>
<column name="second_string"/>
</property
>
Observe el uso de etiquetas <column>
para mapear una propiedad a múltiples columnas.
Las interfaces CompositeUserType
, EnhancedUserType
, UserCollectionType
, y UserVersionType
brindan soporte para usos más especializados.
Incluso usted puede proporcionar parámetros a un UserType
en el archivo de mapeo. Para hacer esto, su UserType
tiene que implementar la interfaz org.hibernate.usertype.ParameterizedType
. Para brindar parámetros a su tipo personalizado, puede utilizar el elemento <type>
en sus archivos de mapeo.
<property name="priority">
<type name="com.mycompany.usertypes.DefaultValueIntegerType">
<param name="default"
>0</param>
</type>
</property
>
Ahora el UserType
puede recuperar el valor del parámetro denominado default
del objeto Properties
que se le pasa.
Si utiliza cierto UserType
muy frecuentemente, puede ser útil el definir un nombre más corto para este. Puede hacer esto utilizando el elemento <typedef>
. Los typedefs asignan un nombre a un tipo personalizado y también pueden contener una lista de valores predeterminados de parámetros si el tipo se encuentra parametrizado.
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
<param name="default"
>0</param>
</typedef
>
<property name="priority" type="default_zero"/>
También es posible sobrescribir los parámetros provistos en un typedef sobre una base de caso por caso utilizando parámetros de tipo en el mapeo de la propiedad.
Aunque el amplio espectro de tipos incorporados y de soporte para los componentes de Hibernate significa que necesitará usar un tipo personalizado muy raramente, se considera como una buena práctica el utilizar tipos personalizados para clases no-entidades que aparezcan frecuentemente en su aplicación. Por ejemplo, una clase MonetaryAmount
es una buena candidata para un CompositeUserType
, incluso cuando puede ser fácilmente mapeada como un componente. Un razón para esto es la abstracción. Con un tipo personalizado, sus documentos de mapeo estarán protegidos contra posibles cambios futuros en la forma de representar valores monetarios.
Es posible proporcionar más de un mapeo para una clase persistente en particular. En este caso usted debe especificar un nombre de entidad para aclarar entre las instancias de las dos entidades mapeadas. Por defecto, el nombre de la entidad es el mismo que el nombre de la clase. Hibernate le deja especificar el nombre de entidad al trabajar con objetos persistentes, al escribir consultas, o al mapear asociaciones a la entidad mencionada.
<class name="Contract" table="Contracts" entity-name="CurrentContract"> ... <set name="history" inverse="true" order-by="effectiveEndDate desc"> <key column="currentContractId"/> <one-to-many entity-name="HistoricalContract"/> </set> </class> <class name="Contract" table="ContractHistory" entity-name="HistoricalContract"> ... <many-to-one name="currentContract" column="currentContractId" entity-name="CurrentContract"/> </class >
Las asociaciones ahora se especifican utilizando entity-name
en lugar de class
.
Puede forzar a Hibernate a que utilice comillas con un identificador en el SQL generado encerrando el nombre de tabla o de columna entre comillas sencillas en el documento de mapeo. Hibernate utilizará el estilo de comillas para el Dialect
SQL. Usualmente comillas dobles, a excepción de corchetes para SQL Server y comillas sencillas para MySQL.
<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class
>
XML no es para todo el mundo, así que hay algunas formas opcionales de definir metadatos de mapeo O/R en Hibernate.
Muchos usuarios de Hibernate prefieren incluir la información de mapeo directamente en el código fuente usando las @hibernate.tags
XDoclet. No abordaremos este enfoque en este manual de referencia ya que se considera como parte de XDoclet. Sin embargo, incluímos el siguiente ejemplo de la clase Cat
con los mapeos XDoclet:
package eg;
import java.util.Set;
import java.util.Date;
/**
* @hibernate.class
* table="CATS"
*/
public class Cat {
private Long id; // identifier
private Date birthdate;
private Cat mother;
private Set kittens
private Color color;
private char sex;
private float weight;
/*
* @hibernate.id
* generator-class="native"
* column="CAT_ID"
*/
public Long getId() {
return id;
}
private void setId(Long id) {
this.id=id;
}
/**
* @hibernate.many-to-one
* column="PARENT_ID"
*/
public Cat getMother() {
return mother;
}
void setMother(Cat mother) {
this.mother = mother;
}
/**
* @hibernate.property
* column="BIRTH_DATE"
*/
public Date getBirthdate() {
return birthdate;
}
void setBirthdate(Date date) {
birthdate = date;
}
/**
* @hibernate.property
* column="WEIGHT"
*/
public float getWeight() {
return weight;
}
void setWeight(float weight) {
this.weight = weight;
}
/**
* @hibernate.property
* column="COLOR"
* not-null="true"
*/
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
/**
* @hibernate.set
* inverse="true"
* order-by="BIRTH_DATE"
* @hibernate.collection-key
* column="PARENT_ID"
* @hibernate.collection-one-to-many
*/
public Set getKittens() {
return kittens;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kittens.add(kitten);
}
/**
* @hibernate.property
* column="SEX"
* not-null="true"
* update="false"
*/
public char getSex() {
return sex;
}
void setSex(char sex) {
this.sex=sex;
}
}
Para obtener más ejemplos de XDoclet e Hibernate consulte el sitio web de Hibernate.
JDK 5.0 introdujo anotaciones del estilo XDoclet a nivel del lenguaje con chequeo seguro de tipos en tiempo de compilación. Este mecanismo es más potente que las anotaciones XDoclet y es mejor soportado por herramientas e IDEs. IntelliJ IDEA, por ejemplo, soporta auto-completación además de resalte de sintaxis de las anotaciones JDK 5.0. La nueva revisión de la especificación de EJB (JSR-220) utiliza anotaciones JDK 5.0 como el mecanismo principal de metadatos para beans de entidad. Hibernate3 implementa el EntityManager
del JSR-220 (la API de persistencia). El soporte para metadatos de mapeo está disponible por medio del paquete Anotaciones de Hibernate, como una descarga separada. Tanto los metadatos de EJB3 (JSR-220) como de Hibernate3 se encuentran soportados.
Este es un ejemplo de una clase POJO anotada como un bean de entidad EJB:
@Entity(access = AccessType.FIELD)
public class Customer implements Serializable {
@Id;
Long id;
String firstName;
String lastName;
Date birthday;
@Transient
Integer age;
@Embedded
private Address homeAddress;
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="CUSTOMER_ID")
Set<Order
> orders;
// Getter/setter and business methods
}
El soporte para las anotaciones JDK 5.0 (y JSR-220) todavía se encuentra en progreso. Para obtener más información consulte al módulo de anotaciones de Hibernate.
Las propiedades generadas son propiedades cuyos valores son generados por la base de datos. Usualmente, las aplicaciones de Hibernate necesitaban refrescar
los objetos que contenian cualquier propiedad para la cual la base de datos generará valores. Sin embargo, el marcar propiedades como generadas deja que la aplicación delegue esta responsabilidad a Hibernate. Cuando Hibernate emite un INSERT or UPDATE SQL para una entidad la cual ha definido propiedades generadas, inmediatamente emite un select para recuperar los valores generados.
Las propiedades marcadas como generadas tienen que ser además no insertables y no actualizables. Sólamente las versiones, sellos de fecha, y propiedades simples se pueden marcar como generadas.
never
(por defecto): el valor dado de la propiedad no es generado dentro de la base de datos.
insert
: el valor dado de la propiedad es generado en insert, pero no es regenerado en las actualizaciones posteriores. Las propiedades como fecha-creada (created-date) se encuentran dentro de esta categoría. Aunque las propiedades versión y sello de fecha se pueden marcar como generadas, esta opción no se encuentra disponible.
always
: el valor de la propiedad es generado tanto en insert como en update.
Hibernate allows you to customize the SQL it uses to read and write the values of columns mapped to simple properties. For example, if your database provides a set of data encryption functions, you can invoke them for individual columns like this:
<!-- XML : generated by JHighlight v1.0 (http://jhighlight.dev.java.net) --> <span class="xml_tag_symbols"><</span><span class="xml_tag_name">property</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"creditCardNumber"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">column</span><span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"credit_card_num"</span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_attribute_name">read</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"decrypt(credit_card_num)"</span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_attribute_name">write</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"encrypt(?)"</span><span class="xml_tag_symbols">/></span><span class="xml_plain"></span><br /> <span class="xml_tag_symbols"></</span><span class="xml_tag_name">property</span><span class="xml_plain"></span><br /> <span class="xml_tag_symbols">></span><span class="xml_plain"></span><br />
Hibernate aplica las expresiones personalizadas de manera automática cuando la propiedad se referencia en una petición. Esta funcionalidad es similar a una propiedad derivada formula
con dos diferencias:
Esta propiedad está respaldada por una o más columnas que se exportan como parte de la generación automática del esquema.
La propiedad es de lectura y escritura no de sólo lectura.
Si se especifica la expresión write
debe contener exactamente un parémetro de sustitución '?' para el valor.
Los objetos de bases de datos auxiliares permiten la creación - CREATE - y eliminación - DROP - de objetos de bases de datos arbitrarios. Junto con las herramientas de evolución del esquema de Hibernate, tienen la habilidad de definir de manera completa el esquema de un usuario dentro de los archivos de mapeo de Hibernate. Aunque están diseñados específicamente para crear y eliminar cosas como disparadores - triggers- o procedimientos almacenados, realmente cualquier comando SQL se puede ejecutar por medio de un método java.sql.Statement.execute()
aquí es válido (por ejemplo, ALTERs, INSERTS, etc). Básicamente, hay dos modos para definir objetos de bases de datos auxiliares:
El primer modo es para numerar explícitamente los comandos CREATE y DROP en el archivo de mapeo:
<hibernate-mapping>
...
<database-object>
<create
>CREATE TRIGGER my_trigger ...</create>
<drop
>DROP TRIGGER my_trigger</drop>
</database-object>
</hibernate-mapping
>
El segundo modo es para proporcionar una clase personalizada que construye los comandos CREATE y DROP. Esta clase personalizada tiene que implementar la interfaz org.hibernate.mapping.AuxiliaryDatabaseObject
.
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
</database-object>
</hibernate-mapping
>
Adicionalmente, estos objetos de la base de datos se pueden incluir de manera opcional de forma que aplique sólamente cuando se utilicen ciertos dialectos.
<hibernate-mapping>
...
<database-object>
<definition class="MyTriggerDefinition"/>
<dialect-scope name="org.hibernate.dialect.Oracle9iDialect"/>
<dialect-scope name="org.hibernate.dialect.Oracle10gDialect"/>
</database-object>
</hibernate-mapping
>
Copyright © 2004 Red Hat, Inc.