miércoles, 21 de marzo de 2018

Hibernate Road Eclipse


- 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>

podemos tener estos valores

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.
Following are the important points worth noting:

  • 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:
userId int(11) PK
address varchar(255)
description varchar(255)
joinedDate datetime
userName varchar(255)

@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();
}

Se incrementa pool
 <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">10</property>

Primary Keys
    natural Primary keys -> email, login Id ...

@Id @GeneratedValue ( generator: String -GeneratedValue
                                             generator=[userId, userName, address, description, joinedDate..]
                                         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:
userId int(11) PK
city varchar(255)
pinCode varchar(255)
state varchar(255)
street varchar(255)
description longtext
joinedDate date
userName varchar(255)

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:
userId int(11) PK
description longtext
city varchar(255)
pinCode varchar(255)
state varchar(255)
street varchar(255)
joinedDate date
OFFICE_CITY_NAME varchar(255)
OFFICE_PIN_CODEE varchar(255)
OFFICE_STATE_NAME varchar(255)
OFFICE_STREET_NAME varchar(255)
userName varchar(255


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:
loginId bigint(20) PK
loginName varchar(255) PK
description longtext
city varchar(255)
pinCode varchar(255)
state varchar(255)
street varchar(255)
joinedDate date
OFFICE_CITY_NAME varchar(255)
OFFICE_PIN_CODEE varchar(255)
OFFICE_STATE_NAME varchar(255)
OFFICE_STREET_NAME varchar(255)
userName varchar(255)

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:
userId int(11) PK
description longtext
joinedDate date
userName varchar(255)

             Table: userdetails_listofaddresses

Columns:
UserDetails_userId int(11)
city varchar(255)
pinCode varchar(255)
state varchar(255)
street varchar(255)

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 hibernate
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;

             Table: user_address 

Columns:
USER_ID int(11)
city varchar(255)
pinCode varchar(255)
state varchar(255)
street varchar(255)

y me genera las siguientes tablas de secuencia

             Table: hibernate_sequence

Columns:
next_val bigint(20)

             Table: hibernate_sequences

Columns:
sequence_name varchar(255) PK
next_val bigint(20)

             Table: seq_address

Columns:
next_val bigint(20)

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:
userId int(11) PK
userName varchar(255)
VEHICLE_ID int(11)

             Table: vehicle

Columns:
vehicleId int(11) PK
vehicleName varchar(255)

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  @GenericGenerator

@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:
userId int(11) PK
userName varchar(255)
            Table: user_vehicle

Columns:
USER_ID int(11)
VEHICLE_ID int(11) PK
            
            Table: vehicle

Columns:
vehicleId int(11) PK
vehicleName varchar(255)

ManyToOne 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
@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:
userId int(11) PK
userName varchar(255)

             Table: user_vehicle

Columns:
USER_ID int(11)
VEHICLE_ID int(11) PK

            Table: user_vehicle

Columns:
USER_ID int(11)
VEHICLE_ID int(11) PK

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:
userId int(11) PK
userName varchar(255)
             Table: vehicle

Columns:
vehicleId int(11) PK
vehicleName varchar(255)
USER_ID int(11)

ManyToMany 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;
@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:
userId int(11) PK
userName varchar(255)

             Table: vehicle

Columns:
vehicleId int(11) PK
vehicleName varchar(255

Me genera dos tablas de relación 

             Table: user_details_vehicle

Columns:
UserDetails_userId int(11)
vehicle_vehicleId int(11)


            Table: vehicle_user_details

Columns:
Vehicle_vehicleId int(11)
userList_userId int(11)

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