- Download Hibernate
- Ubicar el subdirectorio para agregar la libreria al projecto en eclipse.
- New -> java project -> proyecto en eclipse, nombre: HibernateRoad
- Project Properties -> Java Build Path -> Libraries -> Add Library... ->
User Library -> new User Library -> nombre: Hibernate5.2
- se agregan jars el principal si viene, Lib/required, todos., Lib/jpa, Lib/bytecode/javaassist
Using Hibernate
Saving without Hibernate
- JDBC database configuration
- The Model Object
- Service Method to create the model object
- Database Design
-DAO method to save the object usign SQL queries
The Hibernate way
- JDBC database configuration - Hibernate configuration
- The Model Object - Annotations
- Service Method to create the model object - Use the Hibernate API
- Database design not needed!
-DAO method to save the object using SQL not needed!
- En el proyecto se crea el archivo src/hibernate.cfg.xml
-del arhivo descargado de hibernate se busca archivo hibernate.cfg.xml para copiar configuracion
debe tener Database connection settings
JDBC connection Pool
SQL dialect
Disable the second level cache
etc...
algo como esto
<?xml version='1.0' encoding='utf-8'?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!-- Names the annotated entity class -->
<mapping class="org.hibernate.tutorial.annotations.Event"/>
</session-factory>
</hibernate-configuration>
Se agrega al path la libreria de mysql connector, ya que usamos mysql
- Se crea una clase org.bext.dto.UserDetails se ponen anotaciones @Entity, @Id
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class UserDetails {
@Id
private int userId;
private String userName;
setters y getters...
- Se crea una clase de prueba org.bext.hibernate.HibernateTest
Using de Hibernate API
- Create a session factory
- Create a session from de session factory
- Use the session to save model objects - en este punto la aplicación continua abierta.
- Cerrar la session
- Cerrar la sessionFactory, en este punto la aplicación termina
En hibernate.cfg.xml
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
As per the documentation it can have four valid values:
create | update | validate | create-drop
Following is the explanation of the behaviour shown by these value:
- create :- create the schema, the data previously present (if there) in the schema is lost
- update:- update the schema with the given values.
- validate:- validate the schema. It makes no change in the DB.
- create-drop:- create the schema with destroying the data previously present(if there). It also drop the database schema when the SessionFactory is closed.
- In case of update, if schema is not present in the DB then the schema is created.
- In case of validate, if schema does not exists in DB, it is not created. Instead, it will throw an error:-
Table not found:<table name>
- In case of create-drop, schema is not dropped on closing the session. It drops only on closing the SessionFactory.
- In case if i give any value to this property(say abc, instead of
above four values discussed above) or it is just left blank. It shows
following behaviour:
-If schema is not present in the DB:- It creates the schema
-If schema is present in the DB:- update the schema.
public class HibernateTest {
public static void main(String[] args) {
UserDetails userDetails = new UserDetails();
UserDetails userDetails2 = new UserDetails();
userDetails.setUserId(1);
userDetails.setUserName("Primer Usuario");
userDetails.setJoinedDate(new Date());
userDetails.setAddress("address Primer Usuario");
userDetails.setDescription("Primer Usuario Descripcion");
userDetails2.setUserId(2);
userDetails2.setUserName("Segundo Usuario");
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDetails);
session.save(userDetails2);
session.getTransaction().commit();
//session.close();
//sessionFactory.close();
}
User Class ---> ID Name Address Phone DateOfBirth
Id
Name
Address
Phone
Date of Birth
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id
private int userId;
private String userName;
private Date joinedDate;
private String address;
private String description;
Es creada en mySQL como:
Table: user_details
Columns:
@Basic -> fetch, optional=boolean
@Transient -> el campo no se toma en cuenta para persistencia, ni lo crea.
@Temporal -> DATE, TIME, TIMESTAMP formateo para TemporalType.DATE field
@Lob -> para large binary objects ya sea BLOB, CLOB -> Longtext para mysql
Recuperando objetos con session.get(Class<T>,Serializable llave_primaria)
public class HibernateTest {
public static void main(String[] args) {
UserDetails userDetails = new UserDetails();
UserDetails userDetails2 = new UserDetails();
userDetails.setUserId(1);
userDetails.setUserName("Primer Usuario");
userDetails.setJoinedDate(new Date());
userDetails.setAddress("address Primer Usuario");
userDetails.setDescription("Primer Usuario Descripcion");
userDetails2.setUserId(2);
userDetails2.setUserName("Segundo Usuario");
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDetails);
session.save(userDetails2);
session.getTransaction().commit();
session.close();
userDetails = null;
session = sessionFactory.openSession();
session.beginTransaction();
userDetails = session.get(UserDetails.class, 2);
System.out.println("El usuario obtenido es:" + userDetails.getUserName());
//sessionFactory.close();
}
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">10</property>
natural Primary keys -> email, login Id ...
@Id @GeneratedValue ( generator: String -GeneratedValue
generator=[
strategy: GenerationType
strategy=[AUTO,IDENTITY,TABLE,SEQUENCE]
(strategy=GenerationType.AUTO) es la default
Embedded Objects
Dentro de la Clase UserDetails vamos a incorporar el objeto Address que tiene sus propios campos,
esto se hace con @Embeddable y @Embedded
User Class ID Name Street City State PinCode Phone DateOfBirth
Id
Name
Address Street
City
State
PinCode
Phone
Date of Birth
Se necesita hacer un Class Address con @Embedable y un campo Address en UserDetails con @Embeded
import javax.persistence.Embeddable;
@Embeddable
public class Address {
private String street;
private String city;
private String state;
private String pinCode;
getters y setters...
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int userId;
private String userName;
@Temporal (TemporalType.DATE)
private Date joinedDate;
@Embedded
private Address address;
@Lob
private String description;
public class HibernateTest {
public static void main(String[] args) {
Address addr1 = new Address();
addr1.setStreet("calle 10");
addr1.setCity("Mexico");
addr1.setState("DF");
addr1.setPinCode("32123");
Address addr2 = new Address();
addr2.setStreet("Miguel Hidalgo");
addr2.setCity("San Miguel de Allende");
addr2.setState("Guanajuato");
addr2.setPinCode("00011");
UserDetails userDetails = new UserDetails();
UserDetails userDetails2 = new UserDetails();
userDetails.setUserName("Primer Usuario");
userDetails.setJoinedDate(new Date());
userDetails.setDescription("Primer Usuario Descripcion");
userDetails.setAddress(addr1);
userDetails2.setUserName("Segundo Usuario");
userDetails2.setAddress(addr2);
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDetails);
session.save(userDetails2);
session.getTransaction().commit();
session.close();
//sessionFactory.close();
}
Table:
user_details
Columns:
Columns:
Los datos en tabla User_Details
userId, city, pinCode, state, street, description, joinedDate, userName
1, Mexico, 32123, DF, "calle 10", "Primer Usuario Descripcion",2018-03-19,"Primer Usuario"
2, "San Miguel de Allende",00011,Guanajuato,"Miguel Hidalgo",NULL,NULL,"Segundo Usuario"
Para cambiar los nombres de las columnas de Address, en las propiedades de Address se usa la anotación
@Column (name="ElNombre")
Incorporando más de un Objeto
En el caso de incorporar más de un Objeto Address, ya que necesitamos HomeAddress y OfficeAddress, sus nombres de campos al transferirlos a la tabla colicionarán, por lo que tenemos que renombrarlos con @AttributeOverrides
En la clase UserDetails tenemos
...
@Embedded
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="street",column=@Column(name="OFFICE_STREET_NAME")),
@AttributeOverride(name="city",column=@Column(name="OFFICE_CITY_NAME")),
@AttributeOverride(name="state",column=@Column(name="OFFICE_STATE_NAME")),
@AttributeOverride(name="pinCode",column=@Column(name="OFFICE_PIN_CODEE"))
})
private Address officeAddress;
...
La tabla generada queda de la siguiente manera
Table: user_details
Columns:
Primary Key Compuesta
con @EmbeddedId
Clase LoginName
@Embeddable
public class LoginName implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4755541708740964848L;
private Long loginId;
private String loginName;
public LoginName() {
super();
}
public LoginName(Long loginId, String loginName) {
super();
this.loginId = loginId;
this.loginName = loginName;
}
public Long getLoginId() {
return loginId;
}
public void setLoginId(Long loginId) {
this.loginId = loginId;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((loginId == null) ? 0 : loginId.hashCode());
result = prime * result + ((loginName == null) ? 0 : loginName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LoginName other = (LoginName) obj;
if (loginId == null) {
if (other.loginId != null)
return false;
} else if (!loginId.equals(other.loginId))
return false;
if (loginName == null) {
if (other.loginName != null)
return false;
} else if (!loginName.equals(other.loginName))
return false;
return true;
}
Clase UserDetails
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@EmbeddedId
private LoginName loginName;
private String userName;
@Temporal (TemporalType.DATE)
private Date joinedDate;
@Embedded
private Address homeaddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="street",column=@Column(name="OFFICE_STREET_NAME")),
@AttributeOverride(name="city",column=@Column(name="OFFICE_CITY_NAME")),
@AttributeOverride(name="state",column=@Column(name="OFFICE_STATE_NAME")),
@AttributeOverride(name="pinCode",column=@Column(name="OFFICE_PIN_CODEE"))
})
private Address officeaddress;
@Lob
private String description;
public LoginName getLoginName() {
return loginName;
}
public void setLoginName(LoginName loginName) {
this.loginName = loginName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getJoinedDate() {
return joinedDate;
}
public void setJoinedDate(Date joinedDate) {
this.joinedDate = joinedDate;
}
public Address getHomeaddress() {
return homeaddress;
}
public void setHomeaddress(Address homeaddress) {
this.homeaddress = homeaddress;
}
public Address getOfficeaddress() {
return officeaddress;
}
public void setOfficeaddress(Address officeaddress) {
this.officeaddress = officeaddress;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
En HibernateTest.java, creamos el Objeto llavePrimaria LoginName
...
LoginName loginName1 = new LoginName(1L,"unoName");
LoginName loginName2 = new LoginName(2L,"dosName");
UserDetails userDetails = new UserDetails();
userDetails.setLoginName(loginName1);
UserDetails userDetails2 = new UserDetails();
userDetails2.setLoginName(loginName2);
...
Table:
user_details
Columns:
Columns:
Guardando Colecciones @ElementCollection
En la clase UserDetails creamos la Collection con las siguientes anotaciones
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int userId;
private String userName;
@Temporal (TemporalType.DATE)
private Date joinedDate;
@ElementCollection
private Set<Address> listOfAddresses = new HashSet();
@Lob
private String description;
...
En HibernateTest.java asignamos datos
userDetails.setUserName("Primer Usuario");
userDetails.setJoinedDate(new Date());
userDetails.setDescription("Primer Usuario Descripcion");
userDetails.getListOfAddresses().add(addr1);
userDetails2.setUserName("Segundo Usuario");
userDetails2.getListOfAddresses().add(addr2);
Las tablas generadas son
Table: user_details
Columns:
Table: userdetails_listofaddresses
Columns:
Configurando las Colecciones
cambiando el nombre de la tabla de la coleccion y el campo userId
En la clase UserDetails
...
@ElementCollection
@JoinTable(name="USER_ADDRESS",
joinColumns=@JoinColumn(name="USER_ID"))
@GenericGenerator(name="addressGenerator",strategy="sequence",
parameters = {
@Parameter(name="sequence_name", value="seq_address") }
)
@CollectionId(columns = { @Column(name="ADDRESS_ID") }, generator = "addressGenerator", type = @Type(type="long"))
private Collection<Address> listOfAddresses = new ArrayList<Address>();
...
La estrategia "hilo" ha sido eliminada de hibernate 5.+
De las anotaciones anteriores, no todas son JPA standard, pertenecen a hibernateimport org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
Columns:
y me genera las siguientes tablas de secuencia
Table: hibernate_sequence
Columns:
Table: hibernate_sequences
Columns:
Table: seq_address
Columns:
Proxy Objects y Eager, Lazy Fetch
Hibernate Proxy
User Class Proxy User Class
getId() getId()
getName() <---> getName() <------> DATABASE
getListOfAddresses() getListOfAddresses()
Para probar el concepto, en HibernateTest.java tenemos
...
session = sessionFactory.openSession();
userDetails = (UserDetails) session.get(UserDetails.class, 1);
session.close();
System.out.println(userDetails.getListOfAddresses().size());
...
Al cerrar la session e intentar obtener la lista de direcciones, mandará una exception, puesto que solo cargo a un primer nivel las variables del objeto UserDetails, la ListOfAddress, está en segundo nivel, y el fetch esta en LAZY por default. Si cambiamos a EAGER, no nos mandará la exception.
...
@ElementCollection(fetch=FetchType.EAGER)
@JoinTable(name="USER_ADDRESS",
joinColumns=@JoinColumn(name="USER_ID"))
private Collection<Address> listOfAddresses = new ArrayList<Address>();
...
OneToOne Mapping
Se crea otro objeto Vehicle, el cual se asociará uno a uno a UserDetails
@Entity
public class Vehicle {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters = {
@Parameter(name="sequence_name", value="seq_vehicle") }
)
private int vehicleId;
private String vehicleName;
setters y getters...
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int userId;
private String userName;
@OneToOne
@JoinColumn(name="VEHICLE_ID")
private Vehicle vehicle;
...
En HibernateTest, inicializamos los datos
public static void main(String[] args) {
UserDetails userDetails = new UserDetails();
userDetails.setUserName("Primer Usuario");
Vehicle vehicle = new Vehicle();
vehicle.setVehicleName("Carrito");
userDetails.setVehicle(vehicle);
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDetails);
session.save(vehicle);
session.getTransaction().commit();
session.close();
Table:
user_details
Columns:
Table: vehicle
Columns:
OneToMany Mapping @JoinColumn
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters={@Parameter(name="sequence_name",value="seq_vehicle")}
)
private int userId;
private String userName;
@OneToMany
@JoinColumn(name="VEHICLE_ID")
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
Para que no se tomen independientes por tabla los consecutivos de las llaves, se agrega @GenericGeneratorColumns:
Table: vehicle
Columns:
OneToMany Mapping @JoinColumn
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters={@Parameter(name="sequence_name",value="seq_vehicle")}
)
private int userId;
private String userName;
@OneToMany
@JoinColumn(name="VEHICLE_ID")
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
...
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
...
OneToMany Mapping @JoinTable
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters={@Parameter(name="sequence_name",value="seq_vehicle")}
)
private int userId;
private String userName;
@OneToMany
@JoinTable(name="USER_VEHICLE",joinColumns=@JoinColumn(name="USER_ID"),
inverseJoinColumns=@JoinColumn(name="VEHICLE_ID"))
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
...
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
...
Table:
user_details
Columns:
Columns:
Table:
user_vehicle
Columns:
Columns:
Table: vehicle
Columns:
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters={@Parameter(name="sequence_name",value="seq_vehicle")}
)
private int userId;
private String userName;
@OneToMany
@JoinTable(name="USER_VEHICLE",joinColumns=@JoinColumn(name="USER_ID"),
inverseJoinColumns=@JoinColumn(name="VEHICLE_ID"))
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
...
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
@ManyToOne
private UserDetails user;
...
Table: user_details
Columns:
Table: user_vehicle
Columns:
Table: user_vehicle
Columns:
OneToMany Mapping
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters={@Parameter(name="sequence_name",value="seq_vehicle")}
)
private int userId;
private String userName;
@OneToMany(mappedBy="user")
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
...
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
@ManyToOne
@JoinColumn(name="USER_ID")
private UserDetails user;
...
Table:
user_details
Columns:
Columns:
Table:
vehicle
Columns:
Columns:
@Entity
@Table (name="USER_DETAILS")
public class UserDetails {
@Id @GeneratedValue(generator="vehicle-gen",strategy=GenerationType.AUTO)
@GenericGenerator(name="vehicle-gen",strategy="sequence",
parameters={@Parameter(name="sequence_name",value="seq_vehicle")}
)
private int userId;
private String userName;
@ManyToMany
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
...
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
@ManyToMany
private Collection<UserDetails> userList = new ArrayList<UserDetails>();
...
Table: user_details
Columns:
Table: vehicle
Columns:
Me genera dos tablas de relación
Table: user_details_vehicle
Columns:
Table: vehicle_user_details
Columns:
Para evitar esto se modifica vehicle con @ManyToMany(mappedBy="vehicle")
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
@ManyToMany(mappedBy="vehicle")
private Collection<UserDetails> userList = new ArrayList<UserDetails>();
...
Así solo me genera la tabla Table: user_details_vehicle
fdt