Sugerencia de consulta de solo lectura de Hibernate para operaciones de lectura más rápidas
De forma predeterminada, Hibernate carga todos los objetos de entidad en modo lectura-escritura. Realiza comprobaciones sucias para detectar los cambios que necesita para persistir en su base de datos para cada uno de ellos. Eso hace que las entidades sean muy fáciles de usar, pero también crea una sobrecarga si no desea cambiar nada. Puede evitar esto configurando la sugerencia de solo lectura de Hibernate en su consulta.
Al declarar una consulta como de solo lectura, habilita Hibernate para realizar algunas optimizaciones internas. Si Hibernate sabe que no cambia los objetos de entidad recuperados, no tiene que realizar ninguna verificación sucia en ellos. Y si no lo hace, tampoco necesita mantener copias deshidratadas de los objetos recuperados para detectar estos cambios. Esto reduce la huella de memoria de su sesión y el esfuerzo de todas las operaciones de descarga.
Configuración de la sugerencia de consulta de solo lectura
Puede configurarlas de manera similar en todo tipo de consultas. La opción más sencilla es llamar al método setHint en la interfaz Query y TypedQuery . Este método espera el nombre de la sugerencia como una cadena y su valor como un objeto .
ChessPlayer chessPlayer = em.createQuery("SELECT p FROM ChessPlayer p WHERE p.firstName = :firstName", ChessPlayer.class).setParameter("firstName", "Paul") .setHint(QueryHints.READ_ONLY, true) // .setHint("org.hibernate.readOnly", true) .getSingleResult(); |
El nombre de la sugerencia de solo lectura es org.hibernate.readyOnly . Como puede ver en el fragmento de código, puede proporcionarlo como una cadena o usar la constante READ_ONLY de la clase QueryHints de Hibernate . Los valores admitidos de esta sugerencia son verdadero y falso (valor predeterminado).
Si está utilizando una consulta con nombre, también puede establecer la sugerencia como parte de la definición de la consulta. Hibernate lo aplicará automáticamente a su consulta cuando cree una instancia.
1234 |
@NamedQuery (name = "findByFirstName" , query = "SELECT p FROM ChessPlayer p WHERE p.firstName = :firstName" , hints = @QueryHint (name = QueryHints.READ_ONLY, value = "true" )) public class ChessPlayer { ... } |
Y si está utilizando el método de búsqueda en la interfaz EntityManager , puede proporcionar un mapa <String, Object> con sus sugerencias como tercer parámetro.
123 |
Map<String, Object> hints = new HashMap<>(); hints.put(QueryHints.READ_ONLY, true ); ChessPlayer chessPlayer = em.find(ChessPlayer. class , 1L, hints); |
En los 3 casos, el resultado es el mismo. La sugerencia de consulta no afecta la creación y ejecución de la consulta. Tampoco ve ninguna diferencia en el registro de su aplicación o en la base de datos. Hibernate solo excluye los objetos de entidad recuperados de todas las comprobaciones sucias y no almacena ninguna copia interna de ellos.
No cambie los objetos de solo lectura
Probablemente no se sorprenda si le digo que nunca debe cambiar un objeto de entidad que haya obtenido en modo de solo lectura.
Como se explicó anteriormente, Hibernate excluye estos objetos de todas las comprobaciones sucias para reducir la huella de memoria y acelerar las operaciones de vaciado. Debido a esta optimización, Hibernate no detectará que cambió uno de los objetos de solo lectura. No activará ningún cambio de estado del ciclo de vida o declaración SQL UPDATE para él.
Desafortunadamente, Hibernate tampoco le impide cambiar un objeto de solo lectura. Por lo tanto, depende de usted asegurarse de que el resultado de una consulta de solo lectura nunca se use en un contexto que intente cambiar los objetos devueltos.
Puede ver todo eso cuando ejecutamos el siguiente caso de prueba. Primero ejecuto una consulta con una pista de solo lectura para obtener un objeto ChessPlayer . En el siguiente paso, se cambia el apellido del jugador y confirmar la transacción.
EntityManager em = emf.createEntityManager();em.getTransaction().begin(); ChessPlayer chessPlayer = em.createQuery("select p from ChessPlayer p " +"where p.firstName = :firstName ", ChessPlayer.class).setParameter("firstName", "Paul").setHint(QueryHints.READ_ONLY, true)// .setHint("org.hibernate.readOnly", true).getSingleResult(); chessPlayer.setFirstName("changed first name"); em.getTransaction().commit();em.close(); |
Sin la sugerencia de solo lectura, Hibernate detectaría el cambio durante una operación de descarga antes de confirmar la transacción. Pero debido a que la sugerencia de solo lectura excluyó el objeto ChessPlayer de todas las comprobaciones sucias, Hibernate no detecta el cambio y no realiza una instrucción SQL UPDATE.
16:54:52,932 DEBUG SQL:144 - select chessplaye0_.id as id1_1_, chessplaye0_.birthDate as birthdat2_1_, chessplaye0_.firstName as firstnam3_1_, chessplaye0_.lastName as lastname4_1_, chessplaye0_.version as version5_1_ from ChessPlayer chessplaye0_ where chessplaye0_.firstName=? 16:54:52,950 DEBUG StatisticsImpl:729 - HHH000117: HQL: select p from ChessPlayer p where p.firstName = :firstName , time : 19ms, rows : 1 16:54:53,000 INFO StatisticalLoggingSessionEventListener:258 - Session Metrics { 23800 nanoseconds spent acquiring 1 JDBC connections; 19500 nanoseconds spent releasing 1 JDBC connections; 78200 nanoseconds spent preparing 1 JDBC statements; 2558700 nanoseconds spent executing 1 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 nanoseconds spent performing 0 L2C puts; 0 nanoseconds spent performing 0 L2C hits; 0 nanoseconds spent performing 0 L2C misses; 9649700 nanoseconds spent executing 1 flushes (flushing a total of 1 entities and 2 collections); 23400 nanoseconds spent executing 1 partial -flushes (flushing a total of 0 entities and 0 collections) } |
Limitaciones antes de Hibernate 5.4.11
Si está utilizando una versión de Hibernate anterior a la 5.4.11, debe tener en cuenta el error HHH-11958 . En estas versiones anteriores, la sugerencia de consulta de solo lectura no tenía ningún efecto si la configuraba para el método de búsqueda de EntityManager . Hibernate aún incluyó el objeto de la entidad en los cheques sucios y mantuvo una copia deshidratada del objeto recuperado.
Desde Hibernate 5.4.11, este error se corrige, y la optimización de sólo lectura también funciona cuando se está utilizando el EntityManager ‘s hallazgo método.
Fuente: https://thorben-janssen.com/read-only-query-hint/