hibernate - onetomany - @PrimaryKeyJoinColumn con relación bidireccional @OneToOne
onetoone jpa (2)
Tengo dos objetos Mat y MatImage, padre e hijo resp. La clave principal de MatImage es la identificación de Mat y se unen por una relación uno a uno.
Si entiendo la relación bidireccional correctamente, el objeto hijo sabrá sobre el padre si hago algo como matImage.setMat (mat). Creo que la clave principal se llenaría en este punto, pero no es así. Lo sé porque sql arroja una excepción cuando intenta insertar MatImage con # 0 como matId.
Otra pregunta es con el problema n + 1. Me gustaría cargar de forma perezosa el objeto hijo porque no todo el tapete tiene matimage. Puedo intentar cambiar @OneToOne a @ManyToOne, pero no estoy seguro de cómo se puede hacer de forma bidireccional. Cualquier ayuda sería apreciada. Gracias.
Aquí están las entidades:
// Mat
@Entity
@Table(name="mat")
public class Mat implements Serializable {
@Id
@GeneratedValue(generator="SeqMat")
@SequenceGenerator(name="SeqMat", sequenceName="seq_mat_id")
int id
@OneToOne(mappedBy="mat", optional=true, fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn(name="id", referencedColumnName="matId")
@Cascade([ALL, DELETE_ORPHAN])
MatImage matImage
int matTemplateId
int number
...
}
// MatImage
@Entity
@Table(name="matimage")
public class MatImage implements Serializable {
@Id
int matId
@OneToOne(optional=true, fetch=FetchType.LAZY)
@JoinColumn(name="matId", referencedColumnName="id")
Mat mat
@Column(name="img_eventid")
int eventId
...
}
El problema es que JPA, de forma predeterminada, no permitirá las anotaciones adecuadas para las claves compartidas bidireccionales, las relaciones uno a uno. Hibernate tiene la construcción perfecta solo para eso, pero tenga en cuenta el orden correctamente, así como los parámetros específicos de las anotaciones (también puede leer la referencia de hibernación, pero su ejemplo no funcionará a menos que preestablezca primero el objeto dependiente, que puede se evitará con el método abajo, que básicamente salva ambos objetos automáticamente, de una vez). En este ejemplo, cada trabajo está procesando un mensaje. El trabajo es el "padre" y el mensaje es el objeto dependiente. La clave principal del mensaje será la identificación del trabajo (para eliminar una columna adicional en la tabla de mensajes. También puede crear una identificación en la tabla de mensajes, si necesita usar un mensaje fuera del alcance del trabajo, usando la misma configuración que la siguiente) . Cuando se crea un trabajo y se adjunta un mensaje, hibernación guardará automáticamente ambos objetos cuando se llame a jobDao.save.
@Entity
@Table
public class Job implements Serializable{
private Long id;
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@OneToOne(cascade = CascadeType.ALL, mappedBy = "job")
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
message.setJob(this);
}
}
Y ahora el mensaje dependiente:
@Entity
@Table
public abstract class Message implements Serializable {
private Job job;
@Id
@GeneratedValue(generator = "foreign")
@GenericGenerator(
name = "foreign",
strategy = "foreign",
parameters = {@org.hibernate.annotations.Parameter(name = "property", value = "job")})
@OneToOne(optional = false, cascade = CascadeType.ALL)
public Job getJob() {
return job;
}
public void setJob(Job job) {
this.job = job;
}
}
No estoy seguro de que realmente quieras. Pero puede usar algo como el siguiente para configurar su relación bidireccional.
@Entity
public class Mat implements Serializable {
private MutableInt id = new Mutable(-1);
private MatImage matImage;
@Id
@GeneratedValue(strategy=GenerationType.AUTO, generator="SeqMat")
@SequenceGenerator(name="SeqMat", sequenceName="seq_mat_id")
public Integer getId() {
return this.id.intValue;
}
public void setId(Integer id) {
this.id.setValue(id)
}
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@OneToOne(fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn
@Cascade({CascadeType.ALL, CascadeType.DELETE_ORPHAN})
public MatImage getMatImage() {
return this.matImage;
}
public void setMatImage(MatImage matImage) {
this.matImage = matImage;
this.matImage.setIdAsMutableInt(this.id);
}
}
Ahora nuestra MatImage
@Entity
public class MatImage implements Serializable {
private MutableInt id = new MutableInt(-1);
private Mat mat;
@Id
public Integer getId() {
return this.id.intValue();
}
public void setId(Integer id) {
this.id.setValue(id);
}
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@OneToOne(fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn
public Mat getMat() {
return mat;
}
public void setMat(Mat mat) {
this.mat = mat;
this.mat.setIdAsMutableInt(this.id);
}
}
Un par de consejos
La especificación JPA no incluye un método estandarizado para tratar el problema de la generación de clave primaria compartida
Explica por qué uso un org.apache.commons.lang.mutable.MutableInt
como una forma en que Mat
y MatImage
comparten el mismo objeto (id) en la memoria .
Ahora puedes usar algo como:
Mat mat = new Mat();
MatImage matImage = new MatImage();
/**
* Set up both sides
*
* Or use some kind of add convenience method To take car of it
*/
mat.setImage(matImage);
matImage.setMat(mat);
session.saveOrUpdate(mat);
Ambos compartirán el mismo ID generado.
Consejo: ponga la configuración de la anotación en el método getter en lugar del campo miembro. Hibernate hace uso de objetos proxy. Pero el proxy funciona bien cuando se usa la anotación en el método getter, no cuando se usa en el campo miembro.
Saludos,