2012-06-06 12 views
6

Tengo una entidad Hibernate llamada IssueParticipant. Básicamente, describe la relación entre un usuario y un problema (que es como un problema JIRA o Bugzilla). Representa un tipo de tabla de vinculación de muchos a muchos en la base de datos, que vincula un ID de usuario con un ID de problema, pero también incluye otra información relacionada con la configuración de notificación, por lo que se trata como su propia entidad.Hibernar saveOrUpdate() intenta guardar cuando debería actualizar

Estaba teniendo grandes problemas con el uso de userId y issueId como una clave compuesta, así que creé una clave sintética que es una cadena (y varchar en la base de datos postgres), que se forma como: _.

Ahora, tengo una pantalla donde un usuario puede editar todos los usuarios asociados con un problema, al mismo tiempo que edita la configuración de notificación. En una clase de controlador de E Crear una lista de IssueParticipants así:

IssueParticipant participant = new IssueParticipant(); 
participant.setUser(accountUser); 
participant.setIssue(issue); 

lo que estos son, por supuesto, no se maneja por Hibernate en este punto.

Luego en mi DAO, repito a través de ellos y llamo a saveOrUpdate(), esperando que si existe un IssueParticipant con la misma clave sintética en la base de datos, se actualizará; de lo contrario se inserta:

for (IssueParticipant participant : participants) { 
     getCurrentSession().saveOrUpdate(participant); 
     savedIds.add(participant.getIssueUserKey()); 
    } 

(savedIds es una lista estoy manteniendo por lo que más adelante voy a saber qué IssueParticipants debería eliminar de la base de datos).

En lugar de lo que cabe esperar, sin embargo, me sale una excepción:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "issue_participant_pkey" 

Aquí está mi clase de entidad, abreviado:

public class IssueParticipant extends Entity { 

    private String issueUserKey; 
    private Long issueId; 
    private Long userId; 

    // Edit: adding 'dateAdded' definition 
    private Date dateAdded; 
// ... 

    // below may be null 
    private SPUser user; 
    private Issue issue; 

    public static IssueParticipant nulledIssueParticipant() { 
     IssueParticipant ip = new IssueParticipant(); 
     return ip; 
    } 
    public String getIssueUserKey() { 
     return issueUserKey; 
    } 

    public void setIssueUserKey(String issueUserKey) { 
     this.issueUserKey = issueUserKey; 
    } 

    public Long getId() { 
     // currently meaningless 
     return 0L; 
    } 

    public Long getIssueId() { 
     return this.issueId; 
    } 

    public void setIssueId(Long issueId) { 
     this.issueId = issueId; 
     updateKey(); 
    } 

    public Long getUserId() { 
     return this.userId; 
    } 

    public void setUserId(Long userId) { 
     this.userId = userId; 
     updateKey(); 
    } 

    private void updateKey() { 
     issueUserKey = getIssueId() + KEY_SEP + getUserId(); 
    } 

    public SPUser getUser() { 
     return user; 
    } 

    public void setUser(SPUser user) { 
     this.user = user; 
     setUserId(user.getId()); 
    } 

    public Issue getIssue() { 
     return issue; 
    } 

    public void setIssue(Issue issue) { 
     this.issue = issue; 
     setIssueId(issue.getId()); 
    } 

// edit: adding 'dateAdded' methods 
public Date getDateAdded() { 
    return dateAdded; 
} 

public void setDateAdded(Date dateAdded) { 
    this.dateAdded = dateAdded; 
} 

... 

} 

Aquí está su archivo de HBM:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
     "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping default-lazy="false"> 
    <class name="com.xxx.yyy.IssueParticipant" table="issue_participant"> 
     <id name="issueUserKey" column="issue_user_key" type="string"> 
      <generator class="assigned"/> 
     </id> 
     <version name="dateAdded" column="date_added" type="timestamp" unsaved-value="null" /> 
     <property name="issueId" column="issue_id" /> 
     <many-to-one name="user" column="user_id" class="com.xxx.yyy.SPUser" not-null="true" cascade="none" /> 
     <property name="alertRss" column="alert_rss" type="boolean" /> 
     <property name="alertEmail" column="alert_email" type="boolean" /> 
     <property name="alertWeb" column="alert_web" type="boolean" /> 
     <property name="alertClient" column="alert_client" type="boolean" /> 

    </class> 
</hibernate-mapping> 

Y de hecho user_issue_key es la clave principal en la tabla de la base de datos correspondiente.

Creo que la solución correcta podría ser utilizar SpringJDBC en este caso, pero me encantaría descubrir qué está pasando aquí. Alguien tiene alguna idea? Gracias por adelantado.

Respuesta

10

saveOrUpdate() no consulta la base de datos para decidir si debe guardar o actualizar la entidad dada.Esto hace que la decisión basada en el estado de la entidad, de la siguiente manera:

  • si el objeto ya es persistente en esta sesión, no hacer nada
  • si otro objeto asociado a la sesión tiene el mismo identificador, lanzar una excepción
  • si el objeto no tiene ninguna propiedad identificadora, save() que
  • si el identificador del objeto tiene el valor asignado a un objeto recién instanciado, save() que
  • si el objeto está versionada por un < versión> o < marca de tiempo> y el valor de la propiedad de versiones es el mismo valor asignado a un objeto recién instanciado, save() que
  • de otra manera actualizar() del objeto

Así, en lo que Entiendo que en su caso la decisión se basa en el valor del campo dateAdded, por lo tanto, debe mantenerlo para distinguir entre instancias nuevas y separadas.

Consulte también:

+0

Érase una vez que debo haber sabido esto, ya que creé el archivo HBM (debe estar cerca de hace dos años en este punto). En cualquier caso, lo que necesitaba era indicar el rol de valor sin guardar en dateAdded; ¡Gracias! Una vez que obtengo más puntos de reputación o puedo encontrar mis credenciales de inicio de sesión anteriores, le daré su respuesta. –

2

lo puede conseguir para consultar la base de datos para hacer esta determinación si realmente quiere. Cambie su asignación de identificador a:

<id name="issueUserKey" column="issue_user_key" type="string" unsaved-value="undefined"> 
    <generator class="assigned"/> 
</id> 

Por lo general, esa no es la mejor manera. De hecho, tiene un mapeo <version/> que suele ser una mejor alternativa del identificador para decidir entre guardar/actualizar sin la sobrecarga de consultar la base de datos. Intentó configurarlo en unsaved-value="null". Pero no mostró la asignación de propiedad para su propiedad IssueParticipant.dateAdded (que es lo que intenta asignar como <version/>; ¿se da cuenta de que la versión se incrementa cada actualización ?; también, usted sabe acerca de <timestamp/> en lugar de ¿verdad?). De todos modos, ahí es donde está tu problema. ¿Podría mostrar la definición de su propiedad IssueParticipant.dateAdded?

+0

Sure; Edité el código en el OP para agregar la fechaAdded. Es solo un java.util.Date. En cuanto al usuario de , siempre lo consideré más como un bloqueo optimista; pero sí, según el enlace de axtavt, supongo que puede desempeñar un papel en la detección del estado. –

Cuestiones relacionadas