java - requestmapping - spring mvc formulario ejemplo
Problemas para usar Stream respaldado por ScrollableResults como tipo de retorno en Spring MVC (1)
Nota importante: esto ha sido aceptado como un problema de primavera con una versión de corrección de destino de 4.1.2.
Mi objetivo es lograr O (1) complejidad del espacio cuando ScrollableResults
una respuesta HTTP de Hibernate''s ScrollableResults
. Quiero mantener el mecanismo estándar donde se envía un MessageConverter
para manejar un objeto devuelto por @Controller
. He configurado lo siguiente:
-
MappingJackson2HttpMessageConverter
enriquecido con unJsonSerializer
que maneja un Java 8Stream
; - un
ScrollableResultSpliterator
personalizado necesario para ajustarScrollableResults
en unStream
; -
OpenSessionInViewInterceptor
necesitaba mantener la sesión de Hibernate abierta dentro delMessageConverter
; - establecer
hibernate.connection.release_mode
enON_CLOSE
; - asegúrese de que la conexión JDBC tenga la necesaria capacidad de almacenamiento del ResultSet:
con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)
.
Además, necesito una base de datos que admita ese tipo de capacidad de retención. PostgreSQL es una base de datos así y no tengo problemas con esto.
El último obstáculo que he encontrado es la política utilizada por HibernateTransactionManager
en el compromiso de transacción: a menos que la sesión subyacente sea "Hibernate-managed", la disconnect()
, cerrando mi cursor junto con todo lo demás. Dicha política es útil en algunos escenarios especiales, específicamente "sesiones de ámbito de conversación", que están muy lejos de mis requisitos.
Me las he arreglado para solucionar esto con un truco malo: tuve que anular el método ofensivo con un método que es efectivamente un copiar y pegar del original, excepto la llamada eliminada de disconnect()
, pero debe recurrir a la reflexión para acceder a privado API.
public class NoDisconnectHibernateTransactionManager extends HibernateTransactionManager
{
private static final Logger logger = LoggerFactory.getLogger(NoDisconnectHibernateTransactionManager.class);
public NoDisconnectHibernateTransactionManager(SessionFactory sf) { super(sf); }
@Override
protected void doCleanupAfterCompletion(Object transaction) {
final JdbcTransactionObjectSupport txObject = (JdbcTransactionObjectSupport) transaction;
final Class<?> c = txObject.getClass();
try {
// Remove the session holder from the thread.
if ((Boolean)jailBreak(c.getMethod("isNewSessionHolder")).invoke(txObject))
TransactionSynchronizationManager.unbindResource(getSessionFactory());
// Remove the JDBC connection holder from the thread, if exposed.
if (getDataSource() != null)
TransactionSynchronizationManager.unbindResource(getDataSource());
final SessionHolder sessionHolder = (SessionHolder)jailBreak(c.getMethod("getSessionHolder")).invoke(txObject);
final Session session = sessionHolder.getSession();
if ((Boolean)jailBreak(HibernateTransactionManager.class.getDeclaredField("prepareConnection")).get(this)
&& session.isConnected() && isSameConnectionForEntireSession(session))
{
// We''re running with connection release mode "on_close": We''re able to reset
// the isolation level and/or read-only flag of the JDBC Connection here.
// Else, we need to rely on the connection pool to perform proper cleanup.
try {
final Connection con = ((SessionImplementor) session).connection();
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (HibernateException ex) {
logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
}
}
if ((Boolean)jailBreak(c.getMethod("isNewSession")).invoke(txObject)) {
logger.debug("Closing Hibernate Session [{}] after transaction", session);
SessionFactoryUtils.closeSession(session);
}
else {
logger.debug("Not closing pre-bound Hibernate Session [{}] after transaction", session);
if (sessionHolder.getPreviousFlushMode() != null)
session.setFlushMode(sessionHolder.getPreviousFlushMode());
}
sessionHolder.clear();
}
catch (ReflectiveOperationException e) { throw new RuntimeException(e); }
}
static <T extends AccessibleObject> T jailBreak(T o) { o.setAccessible(true); return o; }
}
Dado que considero mi enfoque como la "forma correcta" de generar respuestas respaldadas por ResultSet, y dado que la API de Streams hace que este enfoque sea muy conveniente, me gustaría resolverlo de una manera compatible.
¿Hay alguna manera de obtener el mismo comportamiento sin mi hack? Si no, ¿sería bueno solicitarlo a través de Spring''s Jira?
Limpiar. Como Marko Topolnik había dicho aquí
Sí, me olvidé de esta parte de que la configuración de holdability solo se aplica al encontrar una sesión preexistente. Esto significa que mi "idea" de cómo se podría hacer ya es la forma en que se hace. También significa que mi comentario sobre las fallas no se aplica: o desea la capacidad de retención y se salta la desconexión de la sesión, o usted tampoco lo necesita. Por lo tanto, si no puede obtener la capacidad de retención, no hay ninguna razón para no desconectar la sesión en la confirmación, por lo tanto, no hay ninguna razón para activar "allowResultSetAccessAfterCompletion" en ese caso.