Hibernate.orgCommunity Documentation
Persistent classes are classes in an application that implement the entities of the business problem (e.g. Customer and Order in an E-commerce application). The term "persistent" here means that the classes are able to be persisted, not that they are in the persistent state (see Sección 11.1, “Estados de objeto de Hibernate” for discussion).
Hibernate works best if these classes follow some simple rules, also known as the Plain Old Java Object (POJO) programming model. However, none of these rules are hard requirements. Indeed, Hibernate assumes very little about the nature of your persistent objects. You can express a domain model in other ways (using trees of java.util.Map
instances, for example).
Ejemplo 4.1. Simple POJO representing a cat
package eg;
import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private Date birthdate;
private Color color;
private char sex;
private float weight;
private int litterId;
private Cat mother;
private Set kittens = new HashSet();
private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}
void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
void setSex(char sex) {
this.sex=sex;
}
public char getSex() {
return sex;
}
void setLitterId(int id) {
this.litterId = id;
}
public int getLitterId() {
return litterId;
}
void setMother(Cat mother) {
this.mother = mother;
}
public Cat getMother() {
return mother;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
public Set getKittens() {
return kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kitten.setMother(this);
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
}
En las siguientes secciones vamos a explorar en mayor detalle las cuatro reglas principales de las clases persistentes.
Cat
has a no-argument constructor. All persistent classes must have a default constructor (which can be non-public) so that Hibernate can instantiate them using
. It is recommended that this constructor be defined with at least package visibility in order for runtime proxy generation to work properly. java.lang.reflect.Constructor
.newInstance()
Historically this was considered option. While still not (yet) enforced, this should be considered a deprecated feature as it will be completely required to provide a identifier property in an upcoming release.
Cat
has a property named id
. This property maps to the primary key column(s) of the underlying database table. The type of the identifier property can be any "basic" type (see ???). See Sección 9.4, “Componentes como identificadores compuestos” for information on mapping composite (multi-column) identifiers.
Identifiers do not necessarily need to identify column(s) in the database physically defined as a primary key. They should just identify columns that can be used to uniquely identify rows in the underlying table.
Le recomendamos que declare propiedades identificadoras nombradas-consistentemente en clases persistentes. y que utilice un tipo nulable (por ejemplo, no primitivo).
A central feature of Hibernate, proxies (lazy loading), depends upon the persistent class being either non-final, or the implementation of an interface that declares all public methods. You can persist final
classes that do not implement an interface with Hibernate; you will not, however, be able to use proxies for lazy association fetching which will ultimately limit your options for performance tuning. To persist a final
class which does not implement a "full" interface you must disable proxy generation. See Ejemplo 4.2, “Disabling proxies in hbm.xml” and Ejemplo 4.3, “Disabling proxies in annotations”.
If the final
class does implement a proper interface, you could alternatively tell Hibernate to use the interface instead when generating the proxies. See Ejemplo 4.4, “Proxying an interface in hbm.xml” and Ejemplo 4.5, “Proxying an interface in annotations”.
Ejemplo 4.5. Proxying an interface in annotations
@Entity @Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
You should also avoid declaring public final
methods as this will again limit the ability to generate proxies from this class. If you want to use a class with public final
methods, you must explicitly disable proxying. Again, see Ejemplo 4.2, “Disabling proxies in hbm.xml” and Ejemplo 4.3, “Disabling proxies in annotations”.
Cat
declares accessor methods for all its persistent fields. Many other ORM tools directly persist instance variables. It is better to provide an indirection between the relational schema and internal data structures of the class. By default, Hibernate persists JavaBeans style properties and recognizes method names of the form getFoo
, isFoo
and setFoo
. If required, you can switch to direct field access for particular properties.
Properties need not be declared public. Hibernate can persist a property declared with package
, protected
or private
visibility as well.
Una subclase también tiene que cumplir con la primera y la segunda regla. Hereda su propiedad identificadora de la superclase Cat
. Por ejemplo:
package eg;
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
protected void setName(String name) {
this.name=name;
}
}
Tiene que sobrescribir los métodos equals()
y hashCode()
si:
piensa poner instancias de clases persistentes en un Set
(la forma recomendada de representar asociaciones multivaluadas); y
piensa utilizar reasociación de instancias separadas.
Hibernate garantiza la equivalencia de identidad persistente (fila de base de datos) y de identidad Java sólamente dentro del ámbito de una sesión en particular. De modo que en el momento en que mezcla instancias recuperadas en sesiones diferentes, tiene que implementar equals()
y hashCode()
si desea tener una semántica significativa para Set
s.
La forma más obvia es implementar equals()
/hashCode()
comparando el valor identificador de ambos objetos. Si el valor es el mismo, ambos deben ser la misma fila de la base de datos ya que son iguales. Si ambos son agregados a un Set
, sólo tendremos un elemento en el Set
). Desafortunadamente, no puede utilizar este enfoque con identificadores generados. Hibernate sólo asignará valores identificadores a objetos que son persistentes; una instancia recién creada no tendrá ningún valor identificador. Además, si una instancia no se encuentra guardada y está actualmente en un Set
, al guardarla se asignará un valor identificador al objeto. Si equals()
y hashCode()
están basados en el valor identificador, el código hash podría cambiar, rompiendo el contrato del Set
. Consulte el sitio web de Hibernate y allí encontrará una discusión completa sobre este problema. Este no es un problema de Hibernate, sino de la semántica normal de Java de identidad de objeto e igualdad.
Le recomendamos implementar equals()
y hashCode()
utilizando igualdad de clave empresarial (Business key equality). Igualdad de clave empresarial significa que el método equals()
sólamente compara las propiedades que forman la clave empresarial. Esta es una clave que podría identificar nuestra instancia en el mundo real (una clave candidata natural):
public class Cat {
...
public boolean equals(Object other) {
if (this == other) return true;
if ( !(other instanceof Cat) ) return false;
final Cat cat = (Cat) other;
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
if ( !cat.getMother().equals( getMother() ) ) return false;
return true;
}
public int hashCode() {
int result;
result = getMother().hashCode();
result = 29 * result + getLitterId();
return result;
}
}
A business key does not have to be as solid as a database primary key candidate (see Sección 13.1.3, “Consideración de la identidad del objeto”). Immutable or unique properties are usually good candidates for a business key.
The following features are currently considered experimental and may change in the near future.
Las entidades persistentes no necesariamente tienen que estar representadas como clases POJO o como objetos JavaBean en tiempo de ejecución. Hibernate también soporta modelos dinámicos (utilizando Map
eos de Map
eos en tiempo de ejecución) y la representación de entidades como árboles de DOM4J. No escriba clases persistentes con este enfoque, sólamente archivos de mapeo.
By default, Hibernate works in normal POJO mode. You can set a default entity representation mode for a particular SessionFactory
using the default_entity_mode
configuration option (see Tabla 3.3, “Propiedades de Configuración de Hibernate”).
Los siguientes ejemplos demuestran la representación utilizando Map
eos. Primero, en el archivo de mapeo tiene que declararse un entity-name
en lugar de, o además de un nombre de clase:
<hibernate-mapping>
<class entity-name="Customer">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name="name"
column="NAME"
type="string"/>
<property name="address"
column="ADDRESS"
type="string"/>
<many-to-one name="organization"
column="ORGANIZATION_ID"
class="Organization"/>
<bag name="orders"
inverse="true"
lazy="false"
cascade="all">
<key column="CUSTOMER_ID"/>
<one-to-many class="Order"/>
</bag>
</class>
</hibernate-mapping>
Aunque las asociaciones se declaran utilizando nombres de clase destino, el tipo destino de una asociación puede ser además una entidad dinámica en lugar de un POJO.
Después de establecer el modo de entidad predeterminado como dynamic-map
para la SessionFactory
, puede trabajar en tiempo de ejecución con Map
eos de Map
eos:
Session s = openSession();
Transaction tx = s.beginTransaction();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();
Una de las ventajas principales de un mapeo dinámico es el rápido tiempo de entrega del prototipado sin la necesidad de implementar clases de entidad. Sin embargo, pierde el chequeo de tipos en tiempo de compilación y muy probablemente tendrá que tratar con muchas excepciones en tiempo de ejecución. Gracias al mapeo de Hibernate, el esquema de base de datos se puede normalizar y volver sólido, permitiendo añadir una implementación apropiada del modelo de dominio más adelante.
Los modos de representación de entidad se pueden establecer por Session
:
Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession
Tenga en cuenta que la llamada a getSession()
utilizando un EntityMode
está en la API de Session
, no en la de SessionFactory
. De esta forma, la nueva Session
comparte la conexión JDBC, la transacción y otra información de contexto. Esto significa que no tiene que llamar a flush()
ni a close()
en la Session
secundaria, y también tiene que dejar el manejo de la transacción y de la conexión a la unidad de trabajo primaria.
More information about the XML representation capabilities can be found in Capítulo 20, Mapeo XML.
org.hibernate.tuple.Tuplizer
and its sub-interfaces are responsible for managing a particular representation of a piece of data given that representation's org.hibernate.EntityMode
. If a given piece of data is thought of as a data structure, then a tuplizer is the thing that knows how to create such a data structure, how to extract values from such a data structure and how to inject values into such a data structure. For example, for the POJO entity mode, the corresponding tuplizer knows how create the POJO through its constructor. It also knows how to access the POJO properties using the defined property accessors.
There are two (high-level) types of Tuplizers:
org.hibernate.tuple.entity.EntityTuplizer
which is responsible for managing the above mentioned contracts in regards to entities
org.hibernate.tuple.component.ComponentTuplizer
which does the same for components
Users can also plug in their own tuplizers. Perhaps you require that java.util.Map
implementation other than java.util.HashMap
be used while in the dynamic-map entity-mode. Or perhaps you need to define a different proxy generation strategy than the one used by default. Both would be achieved by defining a custom tuplizer implementation. Tuplizer definitions are attached to the entity or component mapping they are meant to manage. Going back to the example of our Customer
entity, Ejemplo 4.6, “Specify custom tuplizers in annotations” shows how to specify a custom org.hibernate.tuple.entity.EntityTuplizer
using annotations while Ejemplo 4.7, “Specify custom tuplizers in hbm.xml” shows how to do the same in hbm.xml
Ejemplo 4.6. Specify custom tuplizers in annotations
@Entity
@Tuplizer(impl = DynamicEntityTuplizer.class)
public interface Cuisine {
@Id
@GeneratedValue
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
@Tuplizer(impl = DynamicComponentTuplizer.class)
public Country getCountry();
public void setCountry(Country country);
}
Ejemplo 4.7. Specify custom tuplizers in hbm.xml
<hibernate-mapping>
<class entity-name="Customer">
<!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
-->
<tuplizer entity-mode="dynamic-map"
class="CustomMapTuplizerImpl"/>
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<!-- other properties -->
...
</class>
</hibernate-mapping>
org.hibernate.EntityNameResolver
is a contract for resolving the entity name of a given entity instance. The interface defines a single method resolveEntityName
which is passed the entity instance and is expected to return the appropriate entity name (null is allowed and would indicate that the resolver does not know how to resolve the entity name of the given entity instance). Generally speaking, an org.hibernate.EntityNameResolver
is going to be most useful in the case of dynamic models. One example might be using proxied interfaces as your domain model. The hibernate test suite has an example of this exact style of usage under the org.hibernate.test.dynamicentity.tuplizer2. Here is some of the code from that package for illustration.
/**
* A very trivial JDK Proxy InvocationHandler implementation where we proxy an
* interface as the domain model and simply store persistent state in an internal
* Map. This is an extremely trivial example meant only for illustration.
*/
public final class DataProxyHandler implements InvocationHandler {
private String entityName;
private HashMap data = new HashMap();
public DataProxyHandler(String entityName, Serializable id) {
this.entityName = entityName;
data.put( "Id", id );
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ( methodName.startsWith( "set" ) ) {
String propertyName = methodName.substring( 3 );
data.put( propertyName, args[0] );
}
else if ( methodName.startsWith( "get" ) ) {
String propertyName = methodName.substring( 3 );
return data.get( propertyName );
}
else if ( "toString".equals( methodName ) ) {
return entityName + "#" + data.get( "Id" );
}
else if ( "hashCode".equals( methodName ) ) {
return new Integer( this.hashCode() );
}
return null;
}
public String getEntityName() {
return entityName;
}
public HashMap getData() {
return data;
}
}
public class ProxyHelper {
public static String extractEntityName(Object object) {
// Our custom java.lang.reflect.Proxy instances actually bundle
// their appropriate entity name, so we simply extract it from there
// if this represents one of our proxies; otherwise, we return null
if ( Proxy.isProxyClass( object.getClass() ) ) {
InvocationHandler handler = Proxy.getInvocationHandler( object );
if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
DataProxyHandler myHandler = ( DataProxyHandler ) handler;
return myHandler.getEntityName();
}
}
return null;
}
// various other utility methods ....
}
/**
* The EntityNameResolver implementation.
*
* IMPL NOTE : An EntityNameResolver really defines a strategy for how entity names
* should be resolved. Since this particular impl can handle resolution for all of our
* entities we want to take advantage of the fact that SessionFactoryImpl keeps these
* in a Set so that we only ever have one instance registered. Why? Well, when it
* comes time to resolve an entity name, Hibernate must iterate over all the registered
* resolvers. So keeping that number down helps that process be as speedy as possible.
* Hence the equals and hashCode implementations as is
*/
public class MyEntityNameResolver implements EntityNameResolver {
public static final MyEntityNameResolver INSTANCE = new MyEntityNameResolver();
public String resolveEntityName(Object entity) {
return ProxyHelper.extractEntityName( entity );
}
public boolean equals(Object obj) {
return getClass().equals( obj.getClass() );
}
public int hashCode() {
return getClass().hashCode();
}
}
public class MyEntityTuplizer extends PojoEntityTuplizer {
public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super( entityMetamodel, mappedEntity );
}
public EntityNameResolver[] getEntityNameResolvers() {
return new EntityNameResolver[] { MyEntityNameResolver.INSTANCE };
}
public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
String entityName = ProxyHelper.extractEntityName( entityInstance );
if ( entityName == null ) {
entityName = super.determineConcreteSubclassEntityName( entityInstance, factory );
}
return entityName;
}
...
Con el fin de registrar un org.hibernate.EntityNameResolver
los usuarios deben:
Implement a custom tuplizer (see Sección 4.5, “Tuplizers”), implementing the getEntityNameResolvers
method
Registrarlo con el org.hibernate.impl.SessionFactoryImpl
(el cual es la clase de implementación para org.hibernate.SessionFactory
) usando el método registerEntityNameResolver
.
Copyright © 2004 Red Hat, Inc.