@ManyToMany UserDetails a Vehicle y @ManyToMany Vehicle a UserDetails
Establezcamos una relación de muchos a muchos de UserDetails
a Vehicle y viceversa, en otras palabras usuario puede tener varios vehículos,
y también un vehículo puede tener varios usuarios.
UserDetails.java
@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>();
setters y
getters…
en Vehicle.java
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
@ManyToMany
private Collection<UserDetails>
userList = new ArrayList<UserDetails>();
En HibernateTest.java cargaremos datos a user y a vehicle.
public static void main(String[] args) {
UserDetails
userDetails = new UserDetails();
Vehicle
vehicle1 = new Vehicle();
vehicle1.setVehicleName("carrito");
Vehicle
vehicle2 = new Vehicle();
vehicle2.setVehicleName("Audi");
userDetails.setUserName("Primer Usuario");
userDetails.getVehicle().add(vehicle1);
userDetails.getVehicle().add(vehicle2);
vehicle1.getUserList().add(userDetails);
vehicle2.getUserList().add(userDetails);
SessionFactory
sessionFactory = new
Configuration().configure().buildSessionFactory();
Session
session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDetails);
session.save(vehicle1);
session.save(vehicle2);
session.getTransaction().commit();
session.close();
//sessionFactory.close();
}
Al
ejecutar el programa. Hibernate reporta en la consola, la creación de dos
tablas de relación.
Hibernate: drop table if exists
seq_vehicle
Hibernate: drop table if exists
USER_DETAILS
Hibernate: drop table if exists
USER_DETAILS_Vehicle
Hibernate: drop table if exists Vehicle
Hibernate: drop table if exists
Vehicle_USER_DETAILS
Hibernate: create table hibernate_sequence
(next_val bigint) engine=MyISAM
…
Hibernate: insert into hibernate_sequence
values ( 1 )
Hibernate: create table seq_vehicle
(next_val bigint) engine=MyISAM
Hibernate: insert into seq_vehicle values
( 1 )
Hibernate: create table USER_DETAILS
(userId integer not null, userName varchar(255), primary key (userId))
engine=MyISAM
Hibernate: create table
USER_DETAILS_Vehicle (UserDetails_userId integer not null, vehicle_vehicleId
integer not null) engine=MyISAM
Hibernate: create table Vehicle (vehicleId
integer not null, vehicleName varchar(255), primary key (vehicleId))
engine=MyISAM
Hibernate:
create table Vehicle_USER_DETAILS (Vehicle_vehicleId integer not null,
userList_userId integer not null) engine=MyISAM
Hibernate:
alter table USER_DETAILS_Vehicle add constraint FKd6kp2c4oltmpsg6g9d8u15ymw
foreign key (vehicle_vehicleId) references Vehicle (vehicleId)
Hibernate: alter table
USER_DETAILS_Vehicle add constraint FKbdrxqw3j8dovf9npdkrfkpc3u foreign key
(UserDetails_userId) references USER_DETAILS (userId)
Hibernate: alter table
Vehicle_USER_DETAILS add constraint FK8t4fr6s5yx5sg3qquujrjn2w2 foreign key
(userList_userId) references USER_DETAILS (userId)
Hibernate: alter table
Vehicle_USER_DETAILS add constraint FK8mnn7v8pd5lo5asfwtvuwbfp3 foreign key
(Vehicle_vehicleId) references Vehicle (vehicleId)
…
Hibernate: select next_val as id_val from
seq_vehicle for update
Hibernate: update seq_vehicle set
next_val= ? where next_val=?
Hibernate: select next_val as id_val from
hibernate_sequence for update
Hibernate: update hibernate_sequence set
next_val= ? where next_val=?
Hibernate: select next_val as id_val from
hibernate_sequence for update
Hibernate: update hibernate_sequence set
next_val= ? where next_val=?
Hibernate: insert into USER_DETAILS
(userName, userId) values (?, ?)
Hibernate: insert into Vehicle
(vehicleName, vehicleId) values (?, ?)
Hibernate: insert into Vehicle
(vehicleName, vehicleId) values (?, ?)
Hibernate: insert into
USER_DETAILS_Vehicle (UserDetails_userId, vehicle_vehicleId) values (?, ?)
Hibernate: insert into
USER_DETAILS_Vehicle (UserDetails_userId, vehicle_vehicleId) values (?, ?)
Hibernate: insert into
Vehicle_USER_DETAILS (Vehicle_vehicleId, userList_userId) values (?, ?)
Hibernate: insert into
Vehicle_USER_DETAILS (Vehicle_vehicleId, userList_userId) values (?, ?)
userId | userName |
1 | Primer Usuario |
Tabla Vehicle
vehicleId | vehicleName |
1 | carrito |
2 | Audi |
Tabla Vehicle_USER_DETAILS
Vehicle_vehicleId | userList_userId |
1 | 1 |
2 | 1 |
Tabla USER_DETAILS_Vehicle
UserDetails_userId | vehicle_vehicleId |
1 | 1 |
1 | 2 |
Se puede observar que es redundante una de las dos tablas de relación que creo hibernate, con una sola sería suficiente, pare ello hay que indicar en la entidad que no queremos que genere la tabla de relación redundante Vehicle con @ManyToMany(mappedBy="vehicle").
@ManyToMany(mappedBy=”…”)
En Vehicle.java, indicamos con mappedBy=”vehicle” que use la
propiedad “vehicle” de UserDetails.
@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>();
…
Al ejecutar el programa de carga de prueba
HibernateTest.java, observamos que ahora solo que crea una sola table de relación
USER_DETAILS_Vehicle
Hibernate: drop table if exists
hibernate_sequence
…
Hibernate: drop table if exists
seq_vehicle
Hibernate: drop table if exists
USER_DETAILS
Hibernate: drop table if exists
USER_DETAILS_Vehicle
Hibernate: drop table if exists Vehicle
Hibernate: create table hibernate_sequence
(next_val bigint) engine=MyISAM
…
Hibernate: insert into hibernate_sequence
values ( 1 )
Hibernate: create table seq_vehicle
(next_val bigint) engine=MyISAM
Hibernate: insert into seq_vehicle values
( 1 )
Hibernate: create table USER_DETAILS
(userId integer not null, userName varchar(255), primary key (userId))
engine=MyISAM
Hibernate:
create table USER_DETAILS_Vehicle (userList_userId integer not null,
vehicle_vehicleId integer not null) engine=MyISAM
Hibernate: create table Vehicle (vehicleId
integer not null, vehicleName varchar(255), primary key (vehicleId))
engine=MyISAM
Hibernate: alter table
USER_DETAILS_Vehicle add constraint FKd6kp2c4oltmpsg6g9d8u15ymw foreign key
(vehicle_vehicleId) references Vehicle (vehicleId)
Hibernate: alter table
USER_DETAILS_Vehicle add constraint FK8a2f131hek5akrmni3vff2ivy foreign key
(userList_userId) references USER_DETAILS (userId)
…
Hibernate: select next_val as id_val from
seq_vehicle for update
Hibernate: update seq_vehicle set
next_val= ? where next_val=?
Hibernate: select next_val as id_val from
hibernate_sequence for update
Hibernate: update hibernate_sequence set
next_val= ? where next_val=?
Hibernate: select next_val as id_val from
hibernate_sequence for update
Hibernate: update hibernate_sequence set
next_val= ? where next_val=?
Hibernate: insert into USER_DETAILS
(userName, userId) values (?, ?)
Hibernate: insert into Vehicle
(vehicleName, vehicleId) values (?, ?)
Hibernate: insert into Vehicle
(vehicleName, vehicleId) values (?, ?)
Hibernate: insert into
USER_DETAILS_Vehicle (userList_userId, vehicle_vehicleId) values (?, ?)
Hibernate: insert into
USER_DETAILS_Vehicle (userList_userId, vehicle_vehicleId) values (?, ?)
@ManyToOne @NotFound(NotFoundAction.IGNORE)
Utilizado para cuando se quiere evitar una exception cuando no se encuentra la entidad.
CascadeTypes
En acciones como guardar, actualizar sobre una entidad con entidades hijas como propiedades sobre las cuales se desea que dicha acción se aplique también a estas entidades hijas. en este ejemplo guardamos userDetails pero no vehicle, es entonces cuando se genera un error
object references an unsaved transient instance...
@OneToMany
object references an unsaved transient instance...
@OneToMany
@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;
public static void main(String[] args) {
UserDetails
userDetails = new UserDetails();
Vehicle
vehicle1 = new Vehicle();
vehicle1.setVehicleName("carrito");
Vehicle
vehicle2 = new Vehicle();
vehicle2.setVehicleName("Audi");
userDetails.setUserName("Primer Usuario");
userDetails.getVehicle().add(vehicle1);
userDetails.getVehicle().add(vehicle2);
SessionFactory
sessionFactory = new
Configuration().configure().buildSessionFactory();
Session
session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDetails);
//session.save(vehicle1);
//session.save(vehicle2);
session.getTransaction().commit();
session.close();
//sessionFactory.close();
}
may 31, 2018 2:30:30 PM
org.hibernate.internal.ExceptionMapperStandardImpl mapManagedFlushFailure
ERROR: HHH000346: Error during managed flush
[org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient
instance beforeQuery flushing: org.bext.dto.Vehicle]
Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved
transient instance - save the transient instance beforeQuery flushing: org.bext.dto.Vehicle
…
Agregando @OneToMany(cascade=CascadeType.PERSIST)
Al calificar con cascade=CascadeType.PERSIST, no es necesario indicar en el código que guarde las entidades vehicle, pero si hay que indicar que se hace un persist() en lugar de un save().
Al calificar con cascade=CascadeType.PERSIST, no es necesario indicar en el código que guarde las entidades vehicle, pero si hay que indicar que se hace un persist() en lugar de un save().
@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(cascade=CascadeType.PERSIST)
@JoinTable(name="USER_VEHICLE",joinColumns=@JoinColumn(name="USER_ID"),
inverseJoinColumns=@JoinColumn(name="VEHICLE_ID"))
private Collection<Vehicle> vehicle = new ArrayList<Vehicle>();
En HibernateTest.java, cambiamos save a persist.
SessionFactory sessionFactory = new
Configuration().configure().buildSessionFactory();
Session
session = sessionFactory.openSession();
session.beginTransaction();
session.persist(userDetails);
//session.save(vehicle1);
//session.save(vehicle2);
session.getTransaction().commit();
session.close();
Implementando Herencia
Herencia es una característica común en lenguajes OOP, pero
para modelos entidad relación no es materia común. Hibernate da algunas
opciones para implementar herencia.
@Entity
public class DosRuedas extends Vehicle {
private String Manubrio;
…setters y getters…
@Entity
public class CuatroRuedas extends Vehicle {
private String volante;
…setters y getters…
HibernateTest.java
public static void main(String[] args) {
Vehicle
vehicle = new Vehicle();
vehicle.setVehicleName("transporte");
DosRuedas bicicleta = new DosRuedas();
bicicleta.setVehicleName("birula");
bicicleta.setManubrio("Manubrio de
bicicleta");
CuatroRuedas automovil = new CuatroRuedas();
automovil.setVehicleName("Audi");
automovil.setVolante("volante de automovil");
SessionFactory sessionFactory = new
Configuration().configure().buildSessionFactory();
Session
session = sessionFactory.openSession();
session.beginTransaction();
session.save(vehicle);
session.save(bicicleta);
session.save(automovil);
session.getTransaction().commit();
session.close();
//sessionFactory.close();
}
Hibernate crea una única table con columnas extras
referentes a las propiedades de las clases hijas.
Tabla Vehicle
DTYPE | vehicleId | vehicleName | Manubrio | volante |
Vehicle | 1 | transporte | NULL | NULL |
DosRuedas | 2 | birula | Manubrio de bicicleta | NULL |
CuatroRuedas | 3 | Audi | NULL | volante de automovil |
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
Configurando valores y nombres en SINGLE_TABLE
Vehicle.java
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="Tipo_Vehiculo",
discriminatorType=DiscriminatorType.STRING)
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
…
DosRuedas.java
@Entity
@DiscriminatorValue("Bicicleta")
public class DosRuedas extends Vehicle {
private String Manubrio;
…
CuatroRuedas.java
@Entity
@DiscriminatorValue("Carro")
public class CuatroRuedas extends Vehicle {
private String volante;
…
Con estas configuraciones, obtenemos la table Vehicle con
nombre de columna de discriminación con nombre Tipo_Vehiculo y valores como “Bicicleta”
y “Carro”
Tabla Vehiculo customizada
Tipo_Vehiculo | vehicleId | vehicleName | Manubrio | volante |
Vehicle | 1 | transporte | NULL | NULL |
Bicicleta | 2 | birula | Manubrio de bicicleta | NULL |
Carro | 3 | Audi | NULL | volante de automovil |
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
La estratégia que crea una tabla por clase, es configurada
con la anotación mostrada en el titulo de este párrafo. Con esto no es necesaria
la anotación @DiscriminationColumn en la entidad padre y @DiscriminationValue
en las entidades hijas.
Vehicle.java
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Vehicle {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int vehicleId;
private String vehicleName;
…
DosRuedas.java
@Entity
public class DosRuedas extends Vehicle {
private String Manubrio;
…
CuatroRuedas.java
@Entity
public class CuatroRuedas extends Vehicle {
private String volante;
…
Tabla Vehicle
vehicleId | vehicleName |
1 | transporte |
Tabla DosRuedas
vehicleId | vehicleName | Manubrio |
2 | birula | Manubrio de bicicleta |
Tabla CuatroRuedas
vehicleId | vehicleName | volante |
3 | Audi | volante de automovil |
@Inheritance(strategy=InheritanceType.JOINED)
Con la estratégia JOINED se crean tablas separadas por clase
o entidad, y se relacionan con una columna clave, de tal forma que recuperar la
información se hace por medio de un query join.
Vehicle.java
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Vehiculo {
@Id
@GeneratedValue
private int Id;
private String Name;
setters y getters
@Inheritance(strategy = InheritanceType.JOINED)
public class Vehiculo {
@Id
@GeneratedValue
private int Id;
private String Name;
setters y getters
…
DosRuedas.java
@Entity public class DosRuedas extends Vehiculo { private String Manubrio;
setters y getters
…
CuatroRuedas.java
@Entity public class CuatroRuedas extends Vehiculo{ private String volante;
setters y getters
…
Programa de Prueba
public class HibernateTest { public static void main(String[] args){ EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("org.hibernate.tutorial.jpa"); Vehiculo vehiculo = new Vehiculo(); vehiculo.setName("Transporte"); DosRuedas dosRuedas = new DosRuedas(); dosRuedas.setName("bicicleta"); dosRuedas.setManubrio("Manubrio de Bicicleta"); CuatroRuedas cuatroRuedas = new CuatroRuedas(); cuatroRuedas.setName("Audi"); cuatroRuedas.setVolante("Volante de Audi"); EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); entityManager.persist(vehiculo); entityManager.persist(dosRuedas); entityManager.persist(cuatroRuedas); entityManager.getTransaction().commit(); entityManagerFactory.close(); } }
Hibernate
muestra en la consola
Hibernate:
create table CuatroRuedas (volante varchar(255), Id integer not null, primary
key (Id)) engine=MyISAM
Hibernate:
create table DosRuedas (Manubrio varchar(255), Id integer not null, primary key
(Id)) engine=MyISAM
Hibernate:
create table Vehiculo (Id integer not null, Name varchar(255), primary key
(Id)) engine=MyISAM
Hibernate:
alter table CuatroRuedas add constraint FKhaimfgg1r4h57bbo2js59hqq2 foreign key
(Id) references Vehiculo (Id)
Hibernate:
alter table DosRuedas add constraint FKmdtgstld5lhffvuc9lx69xmhk foreign key
(Id) references Vehiculo (Id)
Tue Jun 05
19:10:20 CDT 2018 WARN: Establishing SSL connection without server's identity
verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+
requirements SSL connection must be established by default if explicit option
isn't set. For compliance with existing applications not using SSL the
verifyServerCertificate property is set to 'false'. You need either to
explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide
truststore for server certificate verification.
Hibernate:
select next_val as id_val from hibernate_sequence for update
Hibernate:
update hibernate_sequence set next_val= ? where next_val=?
Hibernate:
insert into Vehiculo (Name, Id) values (?, ?)
Hibernate:
insert into Vehiculo (Name, Id) values (?, ?)
Hibernate:
insert into DosRuedas (Manubrio, Id) values (?, ?)
Hibernate:
insert into Vehiculo (Name, Id) values (?, ?)
Hibernate:
insert into CuatroRuedas (volante, Id) values (?, ?)
jun 05,
2018 7:10:20 PM
org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl
stop
INFO:
HHH10001008: Cleaning up connection pool
[jdbc:mysql://localhost:3306/wsp?zeroDateTimeBehavior=convertToNull]
tabla Vehiculo
Id | Name |
1 | Transporte |
2 | bicicleta |
3 | Audi |
Tabla DosRuedas
Manubrio | Id |
Manubrio de Bicicleta | 2 |
Tabla CuatroRuedas
Manubrio | Id |
Manubrio de Bicicleta | 2 |
La integración de los datos se hace con un join de las tablas
SELECT * FROM vehiculo
JOIN cuatroruedas ON vehiculo.id = cuatroruedas.id;
Id | Name | volante | Id |
3 | Audi | Volante de Audi | 3 |
SELECT * FROM vehiculo
JOIN dosruedas ON vehiculo.id = dosruedas.id;
Id | Name | Manubrio | Id |
2 | bicicleta | Manubrio de Bicicleta | 2 |
FinTexto
No hay comentarios:
Publicar un comentario