Issue
My situation is: entity parent MainSpeech with child Messages. Messages in turn is parent of entity Attachment.
I try to persist/merge an existing MainSpeech by adding a new Massage which have two new Attachments. Persist/merge fails because ID_MESSAGE is null. ID_MESSAGE is the foreing key that join Messages with Attachment.
//Entity MainSpeech
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="mainSpeech")
public List<Messages> getMessages() {
return messages;
}
public void setMessages(List<Messages> messages) {
this.messages = messages;
}
//Entity Messages
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="ID_MAINSPEECH",nullable=false)
public MainSpeech getMainSpeech() {
return mainSpeech;
}
public void setMainSpeech(MainSpeech mainSpeech) {
this.mainSpeech = mainSpeech;
}
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="messages")
public List<Attachments> getAttachments() {
return attachments;
}
public void setAttachments(List<Attachments> attachments) {
this.attachments = attachments;
}
//Entity Attachments
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="ID_MESSAGE",nullable=false)
public Messages getMessages() {
return messages;
}
public void setMessages(Messages messages) {
this.messages = messages;
}
My code:
public String inviaRisposta() {
//conversazione is MainSpeech and it is an instance of MainSpeech
Messaggi messaggio = new Messaggi(autore,
EnumStatoMessaggio.INVIATO.getValue(),
testoMsg,
userWorkContext.getProfiloMessaggistica(),
identity.getCredentials().getUsername(),
conversazione);
//aggiornamento conversazione e inserimento messaggio
conversazione.getMessaggi().add(messaggio);
List<Allegati> allegati = new ArrayList<Allegati>();
try {
if (StringUtils.isNotBlank(fileNameAllegatoUno)) {
Allegati allegato = new Allegati();
allegato.setFile(fileAllegatoUno);
allegato.setDataUpload(new Date());
allegato.setNomeFile(fileNameAllegatoUno);
allegati.add(allegato);
}
if (StringUtils.isNotBlank(fileNameAllegatoDue)) {
Allegati allegato = new Allegati();
allegato.setFile(fileAllegatoDue);
allegato.setDataUpload(new Date());
allegato.setNomeFile(fileNameAllegatoDue);
allegati.add(allegato);
}
if (StringUtils.isNotBlank(fileNameAllegatoTre)) {
Allegati allegato = new Allegati();
allegato.setFile(fileAllegatoTre);
allegato.setDataUpload(new Date());
allegato.setNomeFile(fileNameAllegatoTre);
allegati.add(allegato);
}
} catch (NoSuchAlgorithmException e) {
logger.error("Errore nella lettura dell'allegato", e);
} catch (IOException ioe) {
logger.error("Errore nella lettura dell'allegato", ioe);
}
if (allegati.size() > 0) {
messaggio.setAllegati(allegati);
entityManager.merge(conversazione);
entityManager.persist(messaggio);
/* ERROR DISPLAY IN CONSOLE
10:33:30,942 INFO [STDOUT] Hibernate:
insert
into
gm_messaggi
(autore, id_conversazione, ts_creazione, ts_invio, ts_ultimo_aggiornamento, destinatario, letto_da_destinatario, mittente, stato, testo, utente_mittente)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10:33:30,957 INFO [STDOUT] Hibernate:
insert
into
gm_allegati
(ts_upload, file, id_messaggio, nome_file)
values
(?, ?, ?, ?)
10:33:30,965 INFO [STDOUT] 2019-03-05 10:33:30 WARN JDBCExceptionReporter:233 - SQL Error: 1048, SQLState: 23000
10:33:30,966 INFO [STDOUT] 2019-03-05 10:33:30 ERROR JDBCExceptionReporter:234 - Column 'ID_MESSAGGIO' cannot be null
10:33:50,260 SEVERE [application] javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [it.racomputer.teso.poste.twsign.conversazioni.entity.Allegati]
javax.faces.el.EvaluationException: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [it.racomputer.teso.poste.twsign.conversazioni.entity.Allegati]
*/
} else {
entityManager.merge(conversazione);
}
return "ok";
}
Messages (Messaggi) entity:
package it.entity;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import it.racomputer.seam4ra.auth.entity.AbstractSurrogateKeyEntity;
import it.racomputer.teso.poste.twsign.enums.EnumProfiloMessaggistica;
@Entity
@Table(name = "GM_MESSAGGI")
public class Messaggi extends AbstractSurrogateKeyEntity implements Comparable<Messaggi> {
private static final long serialVersionUID = 7749659591617999027L;
private String autore;
private Date dataCreazione;
private Date dataUltimoAggiornamento;
private Date dataInvio;
private String stato;
private String testo;
private char mittente;
private char destinatario;
private String utenteMittente;
private Conversazioni conversazioni;
private boolean lettoDaDestinatario;
private List<Allegati> allegati = new ArrayList<Allegati>();
public Messaggi() {
super();
}
public Messaggi(String autore, String stato, String testo, char mittente, String utenteMittente, Conversazioni conversazioni) {
super();
this.autore = autore;
this.dataCreazione = new Date();
this.dataUltimoAggiornamento = new Date();
this.dataInvio = new Date();
this.stato = stato;
this.testo = testo;
this.mittente = mittente;
this.destinatario = EnumProfiloMessaggistica.not(mittente).getValue();
this.utenteMittente = utenteMittente;
this.conversazioni = conversazioni;
this.lettoDaDestinatario = false;
}
public Messaggi(Messaggi messaggio) {
super();
this.autore = messaggio.getAutore();
this.dataCreazione = messaggio.getDataCreazione();
this.dataUltimoAggiornamento = messaggio.getDataUltimoAggiornamento();
this.dataInvio = messaggio.getDataInvio();
this.stato = messaggio.getStato();
this.testo = messaggio.getTesto();
this.mittente = messaggio.getMittente();
this.destinatario = messaggio.getDestinatario();
this.utenteMittente = messaggio.getUtenteMittente();
this.conversazioni = messaggio.getConversazioni();
this.lettoDaDestinatario = messaggio.isLettoDaDestinatario();
}
@Column(name="AUTORE", nullable=false, length=45)
public String getAutore() {
return autore;
}
public void setAutore(String autore) {
this.autore = autore;
}
@Column(name="TS_CREAZIONE", nullable=false)
public Date getDataCreazione() {
return dataCreazione;
}
public void setDataCreazione(Date dataCreazione) {
this.dataCreazione = dataCreazione;
}
@Column(name="TS_ULTIMO_AGGIORNAMENTO", nullable=false)
public Date getDataUltimoAggiornamento() {
return dataUltimoAggiornamento;
}
public void setDataUltimoAggiornamento(Date dataUltimoAggiornamento) {
this.dataUltimoAggiornamento = dataUltimoAggiornamento;
}
@Column(name="TS_INVIO", nullable=false)
public Date getDataInvio() {
return dataInvio;
}
public void setDataInvio(Date dataInvio) {
this.dataInvio = dataInvio;
}
@Override
public int compareTo(Messaggi m) {
if (getDataInvio() == null || m.getDataInvio() == null) {
return 0;
}
return getDataInvio().compareTo(m.getDataInvio());
}
@Column(name="STATO", nullable=false, length=30)
public String getStato() {
return stato;
}
public void setStato(String stato) {
this.stato = stato;
}
@Column(name="TESTO", nullable=false, length=1000)
public String getTesto() {
return testo;
}
public void setTesto(String testo) {
this.testo = testo;
}
@Column(name="MITTENTE", nullable=false, length=1)
public char getMittente() {
return mittente;
}
public void setMittente(char mittente) {
this.mittente = mittente;
}
@Column(name="DESTINATARIO", nullable=false, length=1)
public char getDestinatario() {
return destinatario;
}
public void setDestinatario(char destinatario) {
this.destinatario = destinatario;
}
@Column(name="UTENTE_MITTENTE", nullable=false, length=40)
public String getUtenteMittente() {
return utenteMittente;
}
public void setUtenteMittente(String utenteMittente) {
this.utenteMittente = utenteMittente;
}
@ManyToOne(fetch=FetchType.LAZY,targetEntity=Conversazioni.class)
@JoinColumn(name="ID_CONVERSAZIONE",nullable=false)
public Conversazioni getConversazioni() {
return conversazioni;
}
public void setConversazioni(Conversazioni conversazioni) {
this.conversazioni = conversazioni;
}
@Type(type = "yes_no")
@Column(name="LETTO_DA_DESTINATARIO", nullable=false, length=1)
public boolean isLettoDaDestinatario() {
return lettoDaDestinatario;
}
public void setLettoDaDestinatario(boolean lettoDaDestinatario) {
this.lettoDaDestinatario = lettoDaDestinatario;
}
public boolean letto(char profiloMessaggistica) {
if (profiloMessaggistica == getDestinatario() && !isLettoDaDestinatario()) {
return false;
} else {
return true;
}
}
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,targetEntity=Allegati.class,mappedBy="messaggi")
public List<Allegati> getAllegati() {
return allegati;
}
public void setAllegati(List<Allegati> allegati) {
this.allegati = allegati;
}
public boolean isMsgMine (char profiloMessaggistica) {
if (profiloMessaggistica == this.mittente) {
return true;
} else {
return false;
}
}
}
SuperClass to define primary key generation:
package it.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public abstract class AbstractSurrogateKeyEntity implements Serializable
{
private Long id;
@Id
@GeneratedValue
@Column(name="OBJ_ID")
public Long getId()
{
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Solution
You don't eve need this line:
entityManager.persist(messaggio);
It's because you're already adding the association here:
conversazione.getMessaggi().add(messaggio);
and then save it with entityManager.merge(conversazione);
. Also in every one-to-many relationship you have cascade=CascadeType.ALL
, so the child is persisted.
What you did was inserting an object, that already was in db.
Answered By - Andronicus