Esta es una pregunta anterior, pero estaba teniendo un problema similar y pensé que agregaría a este tema. Necesitaba agregar un registro a un escritor de registro de auditoría de StatelessSession existente. La implementación existente usaba una sesión sin estado porque el comportamiento de almacenamiento en caché de la implementación de la sesión estándar era una sobrecarga innecesaria y que no queríamos que nuestros oyentes de hibernación activaran para la escritura del registro de auditoría. Esta implementación se trataba de lograr el mayor rendimiento de escritura posible sin interacciones.
Sin embargo, el nuevo tipo de registro necesitaba utilizar un tipo de comportamiento insert-else-update, donde tenemos la intención de actualizar las entradas de registro existentes con un tiempo de transacción como un comportamiento de "marcado". En una sesión sin estado, saveOrUpdate() no se ofrece, por lo que necesitamos implementar manualmente la inserción-else-update.
A la luz de estos requisitos:
Usted puede utilizar el "inserto en la actualización ... duplicado tecla" mysql comportamiento a través de un sql-pieza de enganche específica para el objeto persistente de hibernación. Se puede definir la cláusula de encargo sql-inserto ya sea a través de anotación (como en la respuesta anterior) o a través de una entidad sql-inserto un mapeo XML de hibernación, por ejemplo .:
<class name="SearchAuditLog" table="search_audit_log" persister="com.marin.msdb.vo.SearchAuditLog$UpsertEntityPersister">
<composite-id name="LogKey" class="SearchAuditLog$LogKey">
<key-property
name="clientId"
column="client_id"
type="long"
/>
<key-property
name="objectType"
column="object_type"
type="int"
/>
<key-property
name="objectId"
column="object_id"
/>
</composite-id>
<property
name="transactionTime"
column="transaction_time"
type="timestamp"
not-null="true"
/>
<!-- the ordering of the properties is intentional and explicit in the upsert sql below -->
<sql-insert><![CDATA[
insert into search_audit_log (transaction_time, client_id, object_type, object_id)
values (?,?,?,?) ON DUPLICATE KEY UPDATE transaction_time=now()
]]>
</sql-insert>
El cartel original pregunta acerca de MySQL específicamente. Cuando implementé el comportamiento insert-else-update con mysql obtuve excepciones cuando se ejecutó la 'ruta de actualización' de sql.Específicamente, mysql informaba que se cambiaron 2 filas cuando solo se actualizó 1 fila (ostensiblemente porque la fila existente es eliminada y la nueva fila está insertada). Vea this issue para más detalles sobre esa característica en particular.
Así que cuando la actualización devolvió 2 veces el número de filas afectadas para hibernar, Hibernar lanzaba una excepción BatchedTooManyRowsAffectedException, retrotraía la transacción y propocionaba la excepción. Incluso si tuviera que atrapar la excepción y manejarla, la transacción ya se había retrotraído en ese punto.
Después de algunas búsquedas descubrí que esto era un problema con la entidad persister que hibernate estaba usando. En mi caso, hibernate usa SingleTableEntityPersister, que define una Expectativa de que el número de filas actualizadas debe coincidir con el número de filas definidas en la operación por lotes.
El ajuste final necesario para que este comportamiento funcione es definir un persister personalizado (como se muestra en la asignación xml anterior). En este caso, todo lo que teníamos que hacer era extender SingleTableEntityPersister y 'anular' la inserción de Expectativa. P.ej. Acabo de tachuelas esta clase estática sobre el objeto persistencia y la defino como el persister personalizado en el mapeo de hibernación:
public static class UpsertEntityPersister extends SingleTableEntityPersister {
public UpsertEntityPersister(PersistentClass arg0, EntityRegionAccessStrategy arg1, SessionFactoryImplementor arg2, Mapping arg3) throws HibernateException {
super(arg0, arg1, arg2, arg3);
this.insertResultCheckStyles[0] = ExecuteUpdateResultCheckStyle.NONE;
}
}
Se tomó un buen tiempo de cavar a través de código de hibernación para encontrar esto - yo no era capaz de encontrar cualquier temas en la red con una solución a esto.
¿Qué ocurre si desea utilizar un campo de identificación con incremento automático (por ejemplo, en mysql)? Estoy recibiendo errores extraños sobre la base de datos que no devuelve un ID –