2012-03-27 17 views
6

Quiero persistir una entidad de correo que tiene algunos recursos (en línea o adjuntos). Primero les relacionados como una relación bidireccional:Comportamiento diferente usando la relación unidireccional o bidireccional

@Entity 
public class Mail extends BaseEntity { 

    @OneToMany(mappedBy = "mail", cascade = CascadeType.ALL, orphanRemoval = true) 
    private List<MailResource> resource; 

    private String receiver; 
    private String subject; 
    private String body; 

    @Temporal(TemporalType.TIMESTAMP) 
    private Date queued; 

    @Temporal(TemporalType.TIMESTAMP) 
    private Date sent; 

    public Mail(String receiver, String subject, String body) { 
     this.receiver = receiver; 
     this.subject = subject; 
     this.body = body; 
     this.queued = new Date(); 
     this.resource = new ArrayList<>(); 
    } 

    public void addResource(String name, MailResourceType type, byte[] content) { 
     resource.add(new MailResource(this, name, type, content)); 
    } 

} 

@Entity 
public class MailResource extends BaseEntity { 

    @ManyToOne(optional = false) 
    private Mail mail; 

    private String name; 
    private MailResourceType type; 
    private byte[] content; 
} 

Y cuando los salvó fueron ejecutados

Mail mail = new Mail("[email protected]", "Hi!", "..."); 
mail.addResource("image", MailResourceType.INLINE, someBytes); 
mail.addResource("documentation.pdf", MailResourceType.ATTACHMENT, someOtherBytes); 
mailRepository.save(mail); 

tres inserciones:

INSERT INTO MAIL (ID, BODY, QUEUED, RECEIVER, SENT, SUBJECT) VALUES (?, ?, ?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE, MAIL_ID) VALUES (?, ?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE, MAIL_ID) VALUES (?, ?, ?, ?, ?) 

luego pensé que sería mejor utilizando sólo una relación OneToMany. No hay necesidad de guardar el cual Mail es en todos los MailResource:

@Entity 
public class Mail extends BaseEntity { 

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) 
    @JoinColumn(name = "mail_id") 
    private List<MailResource> resource; 

    ... 

    public void addResource(String name, MailResourceType type, byte[] content) { 
     resource.add(new MailResource(name, type, content)); 
    } 

} 

@Entity 
public class MailResource extends BaseEntity { 
    private String name; 
    private MailResourceType type; 
    private byte[] content; 
} 

tablas generadas son exactamente los mismos (MailResource tiene una FK a Correo). El problema es el SQL ejecutado:

INSERT INTO MAIL (ID, BODY, QUEUED, RECEIVER, SENT, SUBJECT) VALUES (?, ?, ?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE) VALUES (?, ?, ?, ?) 
INSERT INTO MAILRESOURCE (ID, CONTENT, NAME, TYPE) VALUES (?, ?, ?, ?) 
UPDATE MAILRESOURCE SET mail_id = ? WHERE (ID = ?) 
UPDATE MAILRESOURCE SET mail_id = ? WHERE (ID = ?) 

¿Por qué estas dos actualizaciones? Estoy usando EclipseLink, ¿este comportamiento será el mismo usando otro proveedor de JPA como Hibernate? ¿Qué solución es mejor?

ACTUALIZACIÓN: - Si yo no uso @JoinColumn EclipseLink crea tres tablas: CORREO, MAILRESOURCE y MAIL_MAILRESOURCE. Creo que esto es perfectamente lógico. Pero con @JoinColumn tiene suficiente información para crear solo dos tablas y, en mi opinión, solo inserta, sin actualizaciones.

Respuesta

0

Asignado por define el lado propietario de la relación así que para JPA proporciona una mejor manera de manejar asociaciones. Unir columna solo define la columna de relación. Como JPA es un framework completamente basado en la reflexión, podría pensar en la optimización realizada para Mapped by ya que es fácil encontrar el lado propietario de esta manera.

+0

¿Estás sugiriendo utilizar el atributo de mappedBy OneToMany en lugar de JoinColumn anotación? –

+0

Sí. Creo que siempre funcionará mejor de esa manera. –

2

Cuando utiliza @JoinColumn en OneToMany está definiendo uno "unidireccional" para muchos, que es un nuevo tipo de asignación agregado en JPA 2.0, esto no era compatible con JPA 1.0.

Normalmente esta no es la mejor manera de definir OneToMany, un OneToMany normal se define utilizando un mappedBy y teniendo ManyToOne en el objeto de destino. De lo contrario, el objeto de destino no tiene conocimiento de esta clave foránea y, por lo tanto, la actualización por separado.

También puede usar un JoinTable en lugar de JoinColumn (este es el valor predeterminado para OneToMany), y luego no hay ninguna clave externa en el destino de la que preocuparse.

También hay una cuarta opción. Puede marcar MailResource como Embeddable en lugar de Entity y usar ElementCollection.

Sede, http://en.wikibooks.org/wiki/Java_Persistence/OneToMany

+0

Pero solo quiero que se generen dos tablas (Mail y MailResource). Si utilizo ElementCollection of Embeddable, obtendría tres, ¿no? – sinuhepop

Cuestiones relacionadas