{"id":82017,"date":"2026-09-08T12:03:39","date_gmt":"2026-09-08T06:33:39","guid":{"rendered":"https:\/\/tothenewone.online\/blog\/?p=82017"},"modified":"2026-10-27T23:14:59","modified_gmt":"2026-10-27T17:44:59","slug":"how-hibernate-turned-a-preview-into-a-write","status":"publish","type":"post","link":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/","title":{"rendered":"How Hibernate Turned a Preview into a Write"},"content":{"rendered":"<h1>The bug that shouldn&#8217;t have existed<\/h1>\n<p>The report read like a riddle: <em>edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error.<\/em><\/p>\n<p>Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved anyway.<\/p>\n<p>The app in question manages <strong>versioned plans<\/strong>: before a user approves a new plan version, we show a preview \u2014 &#8220;<em>if you approve this, here&#8217;s what it will affect.<\/em>&#8221;\u00a0 That preview is where the trouble hid.<\/p>\n<p><em>\u00a0(The scenario below is anonymised, but the bug, cause and fix are exactly as they happened.)<\/em><\/p>\n<h2>The Reproduction<\/h2>\n<p>Change a version&#8217;s dates, open the preview, hit <strong>Cancel<\/strong>, refresh \u2014 and the version shows the new dates and an APPROVED status, as if I&#8217;d saved.<\/p>\n<p>Here&#8217;s the twist that took me longest to see: the write never came from Cancel. Cancel is pure UI \u2014 it discards the form and never calls the server. The change\u00a0 was persisted one step earlier, when the preview asked the backend &#8220;what would this affect?&#8221; By the time I clicked Cancel, it was already committed, so Cancel had nothing left to undo.<\/p>\n<p>(And a preview should call the backend \u2014 only the database knows what&#8217;s related. The sin wasn&#8217;t the call; it was that a read had a write hiding inside it.)<\/p>\n<h2>The SQL gave it away<\/h2>\n<p>No exception, no error page \u2014 so I stopped guessing and turned on the one witness that never lies:<br \/>\n<code>\u00a0 logging.level.org.hibernate.SQL=DEBUG<\/code><br \/>\nAnd there it sat, in the middle of a <strong>read<\/strong>:<br \/>\n<code>\u00a0 update plan_version set start_date=?, status=? where id=?<\/code><br \/>\nAn UPDATE I never wrote, fired during a preview. The reads were writing.<\/p>\n<h2>What Hibernate was doing under the hood<\/h2>\n<p>When you load an entity inside a transaction, Hibernate keeps a snapshot of it and watches for changes \u2014 this is <strong>dirty checking<\/strong>. Change a field and the entity is\u00a0 <em>dirty<\/em>. With FlushMode.AUTO (the default), Hibernate may flush pending changes before executing a query when it determines that the query could be affected by\u00a0 those changes \u2014 and it always flushes at commit. The intent is friendly: keep your query results consistent with what you&#8217;ve already changed in memory.<\/p>\n<p>So the sequence that bit me was:<\/p>\n<p>1.\u00a0\u00a0\u00a0\u00a0 The preview mutates managed entities in memory (APPROVED status, new dates, etc).<\/p>\n<p>2.\u00a0\u00a0\u00a0\u00a0 It runs a look-up to build the impact list.<\/p>\n<p>3.\u00a0\u00a0\u00a0\u00a0 Hibernate decides that query could be affected by the dirty entities, so it flushes them \u2014 writing the pretend changes for real.<\/p>\n<p>4.\u00a0\u00a0\u00a0\u00a0 The read-write transaction commits at the end of the request, sealing them.<\/p>\n<p>And this isn&#8217;t a Grails quirk (could be assumed from the given example in Grails). The persistence context, dirty checking and FlushMode.AUTO all live in\u00a0 Hibernate\u00a0 \u2014 Grails and Spring merely delegate to the same session. The identical bug reproduces in plain Spring + Hibernate or Spring Data JPA: a read-write\u00a0 @Transactional method that loads an entity, mutates it, then runs a query will flush that mutation just the same. (The one Grails-specific footnote is that its services are transactional by default, so you can be sitting in a read-write transaction without ever having asked for one.)<\/p>\n<h2>Why save() wasn&#8217;t required<\/h2>\n<p>Here&#8217;s the offending method, boiled down. It&#8217;s Groovy, from a Grails service \u2014 and remember, Grails services run in a read-write transaction by default. Its job is display-only: take the plan the user is editing, simulate the proposed approval in memory, then query what that change would affect so the UI can render a preview.\u00a0 Notice there is no save(), no persist(), no flush() anywhere \u2014 which is exactly why the write is so surprising.<\/p>\n<pre>\/\/ The snippets are Groovy, from a Grails service using GORM (Grails' data-access layer, which \r\n\/\/ runs on Hibernate underneath) \u2014 and Grails services run in a read-write transaction by default. \r\n\/\/ The behaviour is identical on any Grails\/Hibernate or Spring\/Hibernate stack.\r\n\r\n@Transactional                           \/\/ read-write \u2192 Hibernate flush mode AUTO\r\nclass PlanVersionService {\r\n\r\n    Preview buildPreview(Command command) {\r\n        \/\/ 1. Load managed entities \u2014 Hibernate's session now tracks them for changes.\r\n        def plan = planRepository.findById(command.planId)\r\n\r\n        \/\/ 2. \"Simulate\" the proposed change by mutating those managed entities in memory.\r\n        \/\/ We never call save() \u2014 in our heads this is just a harmless what-if.\r\n        def simulated = plan.versions.collect { version -&gt;\r\n            if (version.id == command.versionId) {\r\n                version.startDate = command.startDate\r\n                version.status    = APPROVED       \/\/ \u2190 entity is now DIRTY (in memory only\u2026 we hope)\r\n            }\r\n            version\r\n        }.toSet()\r\n\r\n        \/\/ 3. Run a query to work out the impact.\r\n        \/\/ Because the transaction is read-write, AUTO flush fires BEFORE this SELECT \u2014\r\n        \/\/ Hibernate pushes the dirty entities from step 2 to the DB as a real UPDATE.\r\n        def affected = customRepository.findAffected(simulated, command)\r\n\r\n        \/\/ 4. Method returns \u2192 the read-write transaction commits \u2192 that UPDATE is now permanent.\r\n        return new Preview(affected)               \/\/ user hasn't confirmed anything yet\r\n    }\r\n}\r\n<\/pre>\n<p>The entities mutated in step 2 are <strong>managed<\/strong> \u2014 loaded inside the transaction and tracked by Hibernate&#8217;s persistence context. When step 3 runs a query, Hibernate\u00a0 may flush those pending changes first so the query sees a consistent picture, and &#8220;flush&#8221; means a real UPDATE. The transaction then commits it. save() was\u00a0 never required because the query did the writing for us.<\/p>\n<h2>The one-line fix<\/h2>\n<p>The preview is display-only, so it should never have been in a read-write transaction in the first place. One annotation says so:<\/p>\n<pre>@Transactional(readOnly = true)\r\nMap buildApprovalImpactPreview(...) {\r\n    \/\/ simulate, query, preview \u2014 but never persist\r\n}\r\n<\/pre>\n<p>With the Spring\/Hibernate configuration used here, @Transactional(readOnly = true) switches the session to a read-only transaction mode that prevents the dirty\u00a0 changes from being automatically flushed. The mutations from the simulation remain in the persistence context, are never written, and are discarded when the\u00a0 persistence context ends. (The class had a bare, read-write @Transactional at the top, which is why every method defaulted to auto-flush until told otherwise.)<\/p>\n<h2>The deeper design smell<\/h2>\n<p>Because the real smell isn&#8217;t the missing annotation \u2014 it&#8217;s <strong>using live, managed entities as a scratchpad<\/strong>. Safer options:<\/p>\n<ul>\n<li><strong>Project into DTOs \/ read models \u2014<\/strong> Hibernate can&#8217;t flush what it isn&#8217;t tracking.<\/li>\n<li><strong>Detach or clone before mutating \u2014<\/strong> session.evict(entity) (or a defensive copy) lets you change a throwaway, not the tracked entity.<\/li>\n<li><strong>Keep genuinely read-only work in a read-only transaction \u2014<\/strong> belt and braces on top of the design fix.<\/li>\n<\/ul>\n<h2>What I learned<\/h2>\n<p>If I could hand my past self a checklist, it would be this.<\/p>\n<p>For preview \/ simulation code<\/p>\n<ul>\n<li>Don&#8217;t mutate managed entities.<\/li>\n<li>Prefer DTOs\/projections for simulations.<\/li>\n<li>Use @Transactional(readOnly = true) for genuinely read-only operations.<\/li>\n<li>Don&#8217;t assume &#8220;no save() = no database write.&#8221;<\/li>\n<li>Enable Hibernate SQL logging when investigating unexpected persistence.<\/li>\n<li>Understand the persistence context and dirty checking before debugging the service layer.<\/li>\n<\/ul>\n<p>The fix was a single annotation but it made me stop treating the ORM as a black box and start understanding the machinery underneath: the persistence context, dirty checking, flush modes, and the well-meaning reflex that turns a preview into a commit.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The bug that shouldn&#8217;t have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved [&hellip;]<\/p>\n","protected":false},"author":2278,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":0,"footnotes":""},"categories":[446],"tags":[29,4840,265,4844,5902,4841],"class_list":["post-82017","post","type-post","status-publish","format-standard","hentry","category-java","tag-gorm","tag-grails","tag-hibernate","tag-java","tag-jpa","tag-spring"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"The bug that shouldn&#039;t have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Srishti Mittal\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"TO THE NEW BLOG\" \/>\n\t\t<meta property=\"og:type\" content=\"blog\" \/>\n\t\t<meta property=\"og:title\" content=\"How Hibernate Turned a Preview into a Write | TO THE NEW Blog\" \/>\n\t\t<meta property=\"og:description\" content=\"The bug that shouldn&#039;t have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/tothenewone.online\/blog\/wp-content\/themes\/ttn\/images\/social-logo.png\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/tothenewone.online\/blog\/wp-content\/themes\/ttn\/images\/social-logo.png\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:site\" content=\"@tothenew\" \/>\n\t\t<meta name=\"twitter:title\" content=\"How Hibernate Turned a Preview into a Write | TO THE NEW Blog\" \/>\n\t\t<meta name=\"twitter:description\" content=\"The bug that shouldn&#039;t have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/tothenewone.online\/blog\/wp-content\/themes\/ttn\/images\/social-logo.png\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#article\",\"name\":\"How Hibernate Turned a Preview into a Write | TO THE NEW Blog\",\"headline\":\"How Hibernate Turned a Preview into a Write\",\"author\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/author\\\/srishti-mittal\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/#organization\"},\"datePublished\":\"2026-09-08T12:03:39+05:30\",\"dateModified\":\"2026-10-27T23:14:59+05:30\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#webpage\"},\"articleSection\":\"Java\\\/JVM, GORM, Grails, hibernate, Java, JPA, Spring\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.tothenew.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/category\\\/java\\\/#listItem\",\"name\":\"Java\\\/JVM\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/category\\\/java\\\/#listItem\",\"position\":2,\"name\":\"Java\\\/JVM\",\"item\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/category\\\/java\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#listItem\",\"name\":\"How Hibernate Turned a Preview into a Write\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#listItem\",\"position\":3,\"name\":\"How Hibernate Turned a Preview into a Write\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/category\\\/java\\\/#listItem\",\"name\":\"Java\\\/JVM\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/#organization\",\"name\":\"TO THE NEW Blog\",\"url\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/author\\\/srishti-mittal\\\/#author\",\"url\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/author\\\/srishti-mittal\\\/\",\"name\":\"Srishti Mittal\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#authorImage\",\"url\":\"https:\\\/\\\/newersworld-sf-static.tothenew.net\\\/prod\\\/profilePicFolder\\\/3f5d3f5e-d3ef-44e3-856e-d8959deb902c_5350-Srishti-Mittal-PROFILEPICTURE.jpeg\",\"width\":96,\"height\":96,\"caption\":\"Srishti Mittal\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#webpage\",\"url\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/\",\"name\":\"How Hibernate Turned a Preview into a Write | TO THE NEW Blog\",\"description\":\"The bug that shouldn't have existed The report read like a riddle: edit a record, preview the impact (to confirm\\\/or not confirm), click Cancel, refresh \\u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \\u2014 and yet the changes had been saved\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/how-hibernate-turned-a-preview-into-a-write\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/author\\\/srishti-mittal\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/author\\\/srishti-mittal\\\/#author\"},\"datePublished\":\"2026-09-08T12:03:39+05:30\",\"dateModified\":\"2026-10-27T23:14:59+05:30\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/\",\"name\":\"TO THE NEW Blog\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.tothenew.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"How Hibernate Turned a Preview into a Write | TO THE NEW Blog","description":"The bug that shouldn't have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved","canonical_url":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#article","name":"How Hibernate Turned a Preview into a Write | TO THE NEW Blog","headline":"How Hibernate Turned a Preview into a Write","author":{"@id":"https:\/\/tothenewone.online\/blog\/author\/srishti-mittal\/#author"},"publisher":{"@id":"https:\/\/tothenewone.online\/blog\/#organization"},"datePublished":"2026-09-08T12:03:39+05:30","dateModified":"2026-10-27T23:14:59+05:30","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#webpage"},"isPartOf":{"@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#webpage"},"articleSection":"Java\/JVM, GORM, Grails, hibernate, Java, JPA, Spring"},{"@type":"BreadcrumbList","@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog#listItem","position":1,"name":"Home","item":"https:\/\/tothenewone.online\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog\/category\/java\/#listItem","name":"Java\/JVM"}},{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog\/category\/java\/#listItem","position":2,"name":"Java\/JVM","item":"https:\/\/tothenewone.online\/blog\/category\/java\/","nextItem":{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#listItem","name":"How Hibernate Turned a Preview into a Write"},"previousItem":{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#listItem","position":3,"name":"How Hibernate Turned a Preview into a Write","previousItem":{"@type":"ListItem","@id":"https:\/\/tothenewone.online\/blog\/category\/java\/#listItem","name":"Java\/JVM"}}]},{"@type":"Organization","@id":"https:\/\/tothenewone.online\/blog\/#organization","name":"TO THE NEW Blog","url":"https:\/\/tothenewone.online\/blog\/"},{"@type":"Person","@id":"https:\/\/tothenewone.online\/blog\/author\/srishti-mittal\/#author","url":"https:\/\/tothenewone.online\/blog\/author\/srishti-mittal\/","name":"Srishti Mittal","image":{"@type":"ImageObject","@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#authorImage","url":"https:\/\/newersworld-sf-static.tothenew.net\/prod\/profilePicFolder\/3f5d3f5e-d3ef-44e3-856e-d8959deb902c_5350-Srishti-Mittal-PROFILEPICTURE.jpeg","width":96,"height":96,"caption":"Srishti Mittal"}},{"@type":"WebPage","@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#webpage","url":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/","name":"How Hibernate Turned a Preview into a Write | TO THE NEW Blog","description":"The bug that shouldn't have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/tothenewone.online\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/#breadcrumblist"},"author":{"@id":"https:\/\/tothenewone.online\/blog\/author\/srishti-mittal\/#author"},"creator":{"@id":"https:\/\/tothenewone.online\/blog\/author\/srishti-mittal\/#author"},"datePublished":"2026-09-08T12:03:39+05:30","dateModified":"2026-10-27T23:14:59+05:30"},{"@type":"WebSite","@id":"https:\/\/tothenewone.online\/blog\/#website","url":"https:\/\/tothenewone.online\/blog\/","name":"TO THE NEW Blog","inLanguage":"en-US","publisher":{"@id":"https:\/\/tothenewone.online\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"TO THE NEW BLOG","og:type":"blog","og:title":"How Hibernate Turned a Preview into a Write | TO THE NEW Blog","og:description":"The bug that shouldn't have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved","og:url":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/","og:image":"https:\/\/tothenewone.online\/blog\/wp-content\/themes\/ttn\/images\/social-logo.png","og:image:secure_url":"https:\/\/tothenewone.online\/blog\/wp-content\/themes\/ttn\/images\/social-logo.png","twitter:card":"summary","twitter:site":"@tothenew","twitter:title":"How Hibernate Turned a Preview into a Write | TO THE NEW Blog","twitter:description":"The bug that shouldn't have existed The report read like a riddle: edit a record, preview the impact (to confirm\/or not confirm), click Cancel, refresh \u2014 and the changes are still there. No error. Cancel is the one button whose entire job is to not do things \u2014 and yet the changes had been saved","twitter:image":"https:\/\/tothenewone.online\/blog\/wp-content\/themes\/ttn\/images\/social-logo.png"},"aioseo_meta_data":{"post_id":"82017","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"limit_modified_date":false,"created":"2026-09-01 11:28:05","updated":"2026-10-27 17:45:01","focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/tothenewone.online\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/tothenewone.online\/blog\/category\/java\/\" title=\"Java\/JVM\">Java\/JVM<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tHow Hibernate Turned a Preview into a Write\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/tothenewone.online\/blog"},{"label":"Java\/JVM","link":"https:\/\/tothenewone.online\/blog\/category\/java\/"},{"label":"How Hibernate Turned a Preview into a Write","link":"https:\/\/tothenewone.online\/blog\/how-hibernate-turned-a-preview-into-a-write\/"}],"_links":{"self":[{"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/posts\/82017","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/users\/2278"}],"replies":[{"embeddable":true,"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/comments?post=82017"}],"version-history":[{"count":17,"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/posts\/82017\/revisions"}],"predecessor-version":[{"id":84094,"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/posts\/82017\/revisions\/84094"}],"wp:attachment":[{"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/media?parent=82017"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/categories?post=82017"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tothenewone.online\/blog\/wp-json\/wp\/v2\/tags?post=82017"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}