<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Aleksey Stukalov</title>
    <description>The latest articles on Forem by Aleksey Stukalov (@aleksey).</description>
    <link>https://forem.com/aleksey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F375687%2F82a218e9-f0c7-46ef-9f9b-763e4158ca62.jpeg</url>
      <title>Forem: Aleksey Stukalov</title>
      <link>https://forem.com/aleksey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aleksey"/>
    <language>en</language>
    <item>
      <title>Soft Deletion in Hibernate: Things You May Miss</title>
      <dc:creator>Aleksey Stukalov</dc:creator>
      <pubDate>Mon, 30 Aug 2021 10:09:34 +0000</pubDate>
      <link>https://forem.com/aleksey/soft-deletion-in-hibernate-things-you-may-miss-2gjp</link>
      <guid>https://forem.com/aleksey/soft-deletion-in-hibernate-things-you-may-miss-2gjp</guid>
      <description>&lt;p&gt;Soft Deletion is a commonly used pattern in enterprise applications. Hibernate is a widely used persistence framework in the Java ecosystem. Therefore, it is not surprising that Hibernate provides a way to implement soft deletion. However, this approach has some awkward aspects, which are not highlighted by the docs and articles on that matter. In this article, we will dive into some details that you may miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  @SQLDelete + @Where
&lt;/h2&gt;

&lt;p&gt;If you Google 'soft deletion hibernate,' you’ll most likely find tutorials by &lt;a href="https://www.baeldung.com/spring-jpa-soft-delete" rel="noopener noreferrer"&gt;Eugen Paraschiv&lt;/a&gt;, &lt;a href="https://vladmihalcea.com/the-best-way-to-soft-delete-with-hibernate/" rel="noopener noreferrer"&gt;Vlad Mihalcea&lt;/a&gt;, or &lt;a href="https://thorben-janssen.com/implement-soft-delete-hibernate/" rel="noopener noreferrer"&gt;Thorben Janssen&lt;/a&gt;. They suggest using Hibernate &lt;code&gt;@SQLDelete&lt;/code&gt; and &lt;code&gt;@Where&lt;/code&gt; annotations which let you automatically set the deleted flag and filter by it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"article"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@SQLDelete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"update article set deleted=true where id=?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deleted = false"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="nd"&gt;@Id&lt;/span&gt;
   &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SEQUENCE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

   &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deleted"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;// other properties, getters and setters omitted&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;@SQLDelete&lt;/code&gt; takes a native SQL query that is executed whenever a Hibernate-managed entity gets deleted. &lt;code&gt;@Where&lt;/code&gt; takes a condition and appends it to 'select' queries automatically, allowing you to filter entities based on the &lt;code&gt;deleted&lt;/code&gt; attribute automatically.&lt;/p&gt;

&lt;p&gt;This looks like a silver bullet, however, the devil is in the details. Plain queries will work absolutely fine, but what's going to happen with associations?&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems With Associations
&lt;/h2&gt;

&lt;p&gt;Let's think for a second, what behavior would you expect when you fetch an entity that has an association field to a collection, where some entities were soft-deleted, or to a single soft-deleted entity? In fact, there is no wide range of options. Rather, you want deleted records to be excluded or included in the resulting dataset. And your choice may vary depending on the use case. E.g., we have a collection of items in the e-store cart. Deleting an item we would expect this to disappear from the order, right? However, deleted products should stay in the historical invoices. Isn't it a contradiction?&lt;/p&gt;

&lt;p&gt;Let's examine how the &lt;code&gt;@Where&lt;/code&gt; + &lt;code&gt;@SQLDelete&lt;/code&gt; solution works for different types of associations, fetch the type and API used for executing the query. See the ER diagram below, we are going to use it for our further experiments:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_1_0898629894.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_1_0898629894.png" alt="ER Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Article has a ManyToMany association to Author, OneToMany association to Comment, and a OneToOne association to ArticleDetails. The question is, what should happen if one of them is deleted but still referenced from a live entity?&lt;/p&gt;

&lt;h3&gt;
  
  
  OneToMany and ManyToMany
&lt;/h3&gt;

&lt;p&gt;Hibernate filters out deleted entities from all ToMany associations. If you run the following code before and after marking one author entity as deleted, the number of names printed will change:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="n"&gt;articleOptional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAuthors&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The good news, such behavior consistently happens regardless of the fetching type (lazy or eager) and way of calling it (via entityManager, Criteria API, Spring Data JPA, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy ManyToOne and OneToOne
&lt;/h3&gt;

&lt;p&gt;Let's imagine that we soft-delete an article from our example. However, we don't want to delete comments under this article, so that when recovering the article it appears back with its comments.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"comment"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
   &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"article_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, let's try to fetch a comment associated with the soft-deleted article:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;commentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getArticle&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you add a breakpoint after the first line, you’ll see that the article is initialized with a proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_2_35d268f859.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_2_35d268f859.png" alt="Hibernate Proxy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, this is understandable since Hibernate does not know in advance whether the entity is deleted or not. Once you call &lt;code&gt;com.getArticle().getText()&lt;/code&gt;, the &lt;code&gt;EntityNotFoundException&lt;/code&gt; is thrown! Is that what you would expect?&lt;/p&gt;

&lt;h3&gt;
  
  
  Eager ManyToOne and OneToOne
&lt;/h3&gt;

&lt;p&gt;Let’s repeat the same experiment but change the fetch type to the Eager mode. Now comments get fetched with their article and no proxy is required. Hibernate knows for sure that the article is deleted. Let's run the same test:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;commentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getArticle&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The associated soft-deleted article is silently loaded with no exception with the deleted attribute set to true:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_3_521eb05b76.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_3_521eb05b76.png" alt="Deleted Attribute Set to True"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Such inconsistent behavior can be easily explained. The eager fetch type makes Hibernate join the article table right away. So, Hibernate has a choice to throw the &lt;code&gt;EntityNotFoundException&lt;/code&gt; right away or load it normally. Since the article is loaded, the &lt;code&gt;@Where&lt;/code&gt; mechanism is out of power, and Hibernate simply maps it to the target class.&lt;/p&gt;

&lt;p&gt;Let's fetch a collection of comments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nc"&gt;Iterable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;commentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we get &lt;code&gt;EntityNotFoundException&lt;/code&gt; again! This starts to happen again because &lt;code&gt;findAll&lt;/code&gt; leads to separate queries for the associated &lt;code&gt;Article&lt;/code&gt; entities, as noted &lt;a href="https://stackoverflow.com/questions/49192255/spring-data-findall-does-not-fetch-eagerly/49193254" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Any time a soft-deleted entity gets fetched by a separate query it causes the above exception&lt;/strong&gt;. This happens because generating a separate query Hibernate applies the &lt;code&gt;@Where&lt;/code&gt; clause, which makes it impossible to find a soft-deleted entity. Obviously, such queries always return an empty result, which, in its turn, causes &lt;code&gt;EntityNotFoundException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What's even more amusing is that you need to experiment to see how exactly Hibernate will fetch data using different APIs. So, using QueryDSL you are going to hit the same exception fetching a collection or a single entity. At the same time, criteria API returns the deleted entity for eager OneToOne but throws &lt;code&gt;EntityNotFoundException&lt;/code&gt; for eager ManyToOne. Total mess, isn't it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Way to Avoid EntityNotFoundException
&lt;/h3&gt;

&lt;p&gt;There is a cure against the annoying &lt;code&gt;EntityNotFoundException&lt;/code&gt;. Hibernate &lt;a href="https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#associations-not-found" rel="noopener noreferrer"&gt;introduces&lt;/a&gt; the &lt;code&gt;@NotFound&lt;/code&gt; annotation, which can change the behavior from raising the exception to silently setting null into the association field.&lt;/p&gt;

&lt;p&gt;This may look like a panacea, however, it brings a significant downside: all ToOne associations become eagerly fetched, regardless of the declared fetch type. This fact may massively impact the performance of your application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_4_e272a40fda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jpa-buddy.com%2Fuploads%2FSD_4_e272a40fda.png" alt="@NotFound Hibernate docs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems with Constraints and Indexes
&lt;/h2&gt;

&lt;p&gt;Both deleted and live entities will share unique constraints and indexes. Hence, creating regular indexes will not work any longer. Let's take an example where an author has a unique login constraint. After soft-deletion, our record stays in the table, so no live author can reuse the same login of the deleted ones.&lt;/p&gt;

&lt;p&gt;You are lucky if you use PostgreSQL and can use partial indexes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;author_login_idx&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But if you build your application on top of MySQL, this task turns out to be unsolvable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you may see, Soft Deletion is an easy pattern to understand, but not that easy to implement. Seems there is no ideal implementation for soft deletion. At least Hibernate doesn't provide one.&lt;/p&gt;

&lt;p&gt;In simple cases, you definitely can use &lt;code&gt;@SQLDelete&lt;/code&gt; + &lt;code&gt;@Where&lt;/code&gt;. However, in the case when soft-deleted entities appear in OneToOne and ManyToOne associations, you barely can count on the consistent behavior. Simply changing the fetch type, or introducing &lt;code&gt;@EntityGraph&lt;/code&gt;, or porting your query from Criteria API to QueryDSL or whatever else is likely to change the result: from getting the unexpected exception to the unexpected successful load of the deleted entity or even getting the unexpected null.&lt;/p&gt;

</description>
      <category>java</category>
      <category>hibernate</category>
      <category>jpa</category>
      <category>persistence</category>
    </item>
    <item>
      <title>Lombok and JPA: What may go wrong?</title>
      <dc:creator>Aleksey Stukalov</dc:creator>
      <pubDate>Mon, 19 Apr 2021 16:35:58 +0000</pubDate>
      <link>https://forem.com/aleksey/lombok-and-jpa-what-may-go-wrong-1lcm</link>
      <guid>https://forem.com/aleksey/lombok-and-jpa-what-may-go-wrong-1lcm</guid>
      <description>&lt;p&gt;Lombok is a great tool that makes your Java code concise and clean. However, there are a few things to consider when using it with JPA. In this article, we’ll look at how the misuse of Lombok can hurt the performance of JPA applications or even crash them, and how to avoid that but still gain the benefits of using Lombok.&lt;/p&gt;

&lt;p&gt;We develop &lt;a href="https://plugins.jetbrains.com/plugin/15075-jpa-buddy" rel="noopener noreferrer"&gt;JPA Buddy&lt;/a&gt; – a plugin for IntelliJ IDEA designed to make the use of JPA easier. Before writing a single line of code for it, we went through a ton of projects on GitHub to understand how people work with JPA. Turns out, a lot of them use Lombok for their entities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvwn8vo0v2a9bk8ilaxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvwn8vo0v2a9bk8ilaxp.png" alt="Using Lombok with JPA"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is absolutely fine to use Lombok in your JPA projects, but it has some caveats. Analyzing the projects, we see people stumble into the same pitfalls over and over again. This is why we introduced a number of code inspections for Lombok to JPA Buddy. This article shows the most common issues you may face using Lombok with JPA entities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Broken HashSets (and HashMaps)
&lt;/h2&gt;

&lt;p&gt;Entity classes often get annotated with &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; or &lt;code&gt;@Data&lt;/code&gt;. The documentation of &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, it'll use all non-static, non-transient fields, but you can modify which fields are used (and even specify that the output of various methods is to be used) by marking type members with &lt;code&gt;@EqualsAndHashCode.Include&lt;/code&gt; or &lt;code&gt;@EqualsAndHashCode.Exclude&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Equals()/hashCode()&lt;/code&gt; implementation for JPA entities is a sensitive subject. Naturally, entities are mutable. Even the id of an entity is often generated by a database, so it gets changed after the entity is first persisted. This means there are no fields we can rely on to calculate the hashCode.&lt;/p&gt;

&lt;p&gt;For example, let’s create a test entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@EqualsAndHashCode
public class TestEntity {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(nullable = false)
   private Long id;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And execute the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TestEntity testEntity = new TestEntity();
Set&amp;lt;TestEntity&amp;gt; set = new HashSet&amp;lt;&amp;gt;();

set.add(testEntity);
testEntityRepository.save(testEntity);

Assert.isTrue(set.contains(testEntity), "Entity not found in the set");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assertion in the last line fails, even though the entity is added to the set just a couple of lines above. Delomboking the &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; gives us the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public int hashCode() {
   final int PRIME = 59;
   int result = 1;
   final Object $id = this.getId();
   result = result * PRIME + ($id == null ? 43 : $id.hashCode());
   return result;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the id is generated (on its first save) the hashCode gets changed. So the HashSet looks for the entity in a different bucket and cannot find it. It wouldn’t be an issue if the id was set during the entity object creation (e.g. was a UUID set by the app), but DB-generated ids are more common.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accidentally Loading Lazy Attributes
&lt;/h2&gt;

&lt;p&gt;As mentioned above, &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; includes all the object fields by default. The same is right for &lt;code&gt;@ToString&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Any class definition may be annotated with &lt;code&gt;@ToString&lt;/code&gt; to let lombok generate an implementation of the &lt;code&gt;toString()&lt;/code&gt; method. By default, it'll print your class name, along with each field, in order, separated by commas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These methods call &lt;code&gt;equals()/hashCode()/toString()&lt;/code&gt; on every field of an object. This can have an unwanted side-effect for JPA entities: accidentally loading lazy attributes.&lt;/p&gt;

&lt;p&gt;For example, calling &lt;code&gt;hashCode()&lt;/code&gt; on a lazy &lt;code&gt;@OneToMany&lt;/code&gt; may fetch all the entities it contains. This can easily harm the application performance. It can also lead to a LazyInitializationException if it happens outside a transaction.&lt;/p&gt;

&lt;p&gt;We believe &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; and &lt;code&gt;@Data&lt;/code&gt; should not be used for entities at all, so JPA Buddy alerts developers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf3mg0n13y8z52efs43i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf3mg0n13y8z52efs43i.jpg" alt="Data annotation alert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@ToString&lt;/code&gt; can still be used, but all the lazy fields need to be excluded. This can be achieved by placing &lt;code&gt;@ToString.Exclude&lt;/code&gt; on the desired fields, or by using &lt;code&gt;@ToString(onlyExplicitlyIncluded = true)&lt;/code&gt; on the class and &lt;code&gt;@ToString.Include&lt;/code&gt; on non-lazy fields. JPA Buddy has a special action for it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadb0musqefy75p1ehsfg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadb0musqefy75p1ehsfg.jpg" alt="ToString alert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Missing No-Argument Constructor
&lt;/h2&gt;

&lt;p&gt;According to the JPA specification, all entity classes are required to have a public or protected no-argument constructor. Obviously, when &lt;code&gt;@AllArgsConstructor&lt;/code&gt; is used the compiler does not generate the default constructor, the same is true for &lt;code&gt;@Builder&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Applying &lt;a class="mentioned-user" href="https://dev.to/builder"&gt;@builder&lt;/a&gt; to a class is as if you added &lt;code&gt;@AllArgsConstructor(access = AccessLevel.PACKAGE)&lt;/code&gt; to the class and applied the &lt;a class="mentioned-user" href="https://dev.to/builder"&gt;@builder&lt;/a&gt; annotation to this all-args-constructor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So make sure to always use them with &lt;code&gt;@NoArgsConstructor&lt;/code&gt; or an explicit no-argument constructor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevy0amesl36j3zji3hx2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevy0amesl36j3zji3hx2.png" alt="All args constructor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Lombok makes your code look nicer, but as with any magic-like tool, it is important to understand how exactly it works and when to use it. You can also rely on development tools to predict potential issues for you. Otherwise, you might accidentally hurt the performance of your application or even break something.&lt;/p&gt;

&lt;p&gt;When working with JPA and Lombok, remember these rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid using &lt;code&gt;@EqualsAndHashCode&lt;/code&gt; and &lt;code&gt;@Data&lt;/code&gt; with JPA entities;&lt;/li&gt;
&lt;li&gt;Always exclude lazy attributes when using &lt;code&gt;@ToString&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Don’t forget to add &lt;code&gt;@NoArgsConstructor&lt;/code&gt; to entities with -&lt;code&gt;@Builder&lt;/code&gt; or &lt;code&gt;@AllArgsConstructor&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or let &lt;a href="https://plugins.jetbrains.com/plugin/15075-jpa-buddy" rel="noopener noreferrer"&gt;JPA Buddy&lt;/a&gt; remember them for you: its code inspections are always at your service.&lt;/p&gt;

</description>
      <category>java</category>
      <category>jpa</category>
      <category>hibernate</category>
      <category>lombok</category>
    </item>
    <item>
      <title>Hibernate Naming Strategies: JPA Specification vs Spring Boot Opinionation</title>
      <dc:creator>Aleksey Stukalov</dc:creator>
      <pubDate>Tue, 16 Mar 2021 12:59:34 +0000</pubDate>
      <link>https://forem.com/aleksey/hibernate-naming-strategies-jpa-specification-vs-spring-boot-opinionation-m1c</link>
      <guid>https://forem.com/aleksey/hibernate-naming-strategies-jpa-specification-vs-spring-boot-opinionation-m1c</guid>
      <description>&lt;p&gt;Each time we inject a dependency into our project, we sign a contract, which often has lots of hidden things "written in the fine print". In this article, we will take a look at something you could miss when signing a tripartite contract between you, Hibernate and Spring Boot. We will talk about naming strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defaults in JPA Naming
&lt;/h2&gt;

&lt;p&gt;The ultimate rule about defaults: they must be intuitive. Let's check if this rule applies to a standard Spring Boot application with default configuration using Hibernate as a JPA implementation. Imagine you have an entity &lt;em&gt;“PetType”&lt;/em&gt;. Let’s guess what table name in the database it is associated with.&lt;/p&gt;

&lt;p&gt;First example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetType&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// fields omitted&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To me, the most intuitive table name would be the class name, which is &lt;code&gt;PetType&lt;/code&gt;. Running a test against PostgreSQL we find out that the associated table name is actually &lt;code&gt;pet_type&lt;/code&gt;.&lt;br&gt;
Let’s set the name explicitly using &lt;code&gt;@Table&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PetType"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetType&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// fields omitted&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This time we expect to see PetType for sure, but if we run the test… &lt;code&gt;pet_type&lt;/code&gt; again!&lt;/p&gt;

&lt;p&gt;Well, let's wrap the table name in quotes. This should keep not only the defined name but also the case.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"\"PetType\""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetType&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// fields omitted&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Again, our expectations were wrong, and we see &lt;code&gt;“pet_type”&lt;/code&gt;, but now in quotes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Hibernate Naming Strategies
&lt;/h2&gt;

&lt;p&gt;Googling &lt;em&gt;"jpa entity default table name"&lt;/em&gt; you’ll most probably stumble onto the following &lt;a href="https://en.wikibooks.org/wiki/Java_Persistence/Tables#:~:text=The%20JPA%20default%20table%20name,a%20column%20in%20the%20table." rel="noopener noreferrer"&gt;result&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The JPA default table name is the name of the class (minus the package) with the first letter capitalized. Each attribute of the class will be stored in a column in the table. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is exactly what we expected to see in the first example, isn't it? Obviously, something breaks the standard.&lt;/p&gt;

&lt;p&gt;Let's dive deeper into Hibernate. According to the documentation, there are two interfaces responsible for naming your tables, columns etc. in Hibernate: &lt;code&gt;ImplicitNamingStrategy&lt;/code&gt; and &lt;code&gt;PhysicalNamingStrategy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ImplicitNamingStrategy&lt;/code&gt; is in charge of naming all objects that were not explicitly named by a developer: e.g. entity name, table name, column name, index, FK etc. The resulting name is called the logical name, it is used internally by Hibernate to identify an object. It is not the name that gets put into the DB.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PhysicalNamingStrategy&lt;/code&gt; provides the actual physical name used in the DB based on the logical JPA object name. &lt;strong&gt;Effectively, this means that using Hibernate you cannot specify database object names directly, but only logical ones.&lt;/strong&gt; To have a better understanding of what's happening under the hood, see the diagram below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs222274kavjwy1z1douf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs222274kavjwy1z1douf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hibernate default implementations of these interfaces are &lt;code&gt;ImplicitNamingStrategyJpaCompliantImpl&lt;/code&gt; and &lt;code&gt;PhysicalNamingStrategyStandardImpl&lt;/code&gt;. The former generates logical names in accordance with the JPA specification, and the latter uses them as physical names without any modifications. This is best described in the documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JPA defines inherent rules about implicit logical name determination. If JPA provider portability is a major concern, or if you really just like the JPA-defined implicit naming rules, be sure to stick with &lt;code&gt;ImplicitNamingStrategyJpaCompliantImpl&lt;/code&gt; (the default).&lt;br&gt;
Also, JPA defines no separation between logical and physical name. Following the JPA specification, the logical name is the physical name. If JPA provider portability is important, applications should prefer not to specify a &lt;code&gt;PhysicalNamingStrategy&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, our application shows different behaviour. And this is why. Spring Boot overrides Hibernate default implementations for both interfaces and uses &lt;code&gt;SpringImplicitNamingStrategy&lt;/code&gt; and &lt;code&gt;SpringPhysicalNamingStrategy&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Effectively, &lt;code&gt;SpringImplicitNamingStrategy&lt;/code&gt; copies the behaviour of &lt;code&gt;ImplicitNamingStrategyJpaCompliantImpl&lt;/code&gt; with only a minor difference in join table naming. So, it must be &lt;code&gt;SpringPhysicalNamingStrategy&lt;/code&gt; that produces the results we’ve seen. The &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-configure-hibernate-naming-strategy" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; states the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, Spring Boot configures the physical naming strategy with &lt;code&gt;SpringPhysicalNamingStrategy&lt;/code&gt;. This implementation provides the same table structure as Hibernate 4: all dots are replaced by underscores and camel casing is replaced by underscores as well. Additionally, by default, all table names are generated in the lower case. For example, a &lt;code&gt;TelephoneNumber&lt;/code&gt; entity is mapped to the &lt;code&gt;telephone_number&lt;/code&gt; table.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, it always transforms &lt;em&gt;camelCase&lt;/em&gt; and &lt;em&gt;PascalCase&lt;/em&gt; to &lt;em&gt;snake_case&lt;/em&gt;. In fact, using it isn’t possible to work with &lt;em&gt;non_snake_case&lt;/em&gt; at all. Personally, I would never use &lt;em&gt;camelCase&lt;/em&gt; or &lt;em&gt;PascalCase&lt;/em&gt; for naming database objects, but there are DB admins who would. If your Spring Boot application deals with a third-party database where at least one table or column is defined in a pascal or camel case, the default Spring Boot setup will not work for you. So, make sure the used physical naming strategy supports the given database naming convention. Learn how to change the default naming strategy in this article or, if required, learn how to provide your own implementation here.&lt;/p&gt;

&lt;p&gt;So, Hibernate complies with the JPA spec and Spring Boot doesn’t. This may look like a bug, but Spring Boot claims to be an opinionated framework. In other words, it has the full right to apply its own opinion over all standards and specs of the technology used under the hood. For a developer this means the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opinionation may override any specification. In other words, specs and standards state how it should be, while the used implementation defines what it actually is.&lt;/li&gt;
&lt;li&gt;If something even works by default, you should always learn what this default is and how exactly it works.&lt;/li&gt;
&lt;li&gt;Defaults may change with library version upgrade, which can lead to unpredictable side effects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The magic of default configurations may work until you hit some unexpected behaviour. To avoid such risk, you may prefer explicit definition to implicit use of defaults. So, in our case the recommendation would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always name your JPA objects explicitly, so that no Implicit Naming Strategy affects your code&lt;/li&gt;
&lt;li&gt;Use the &lt;em&gt;snake_case&lt;/em&gt; for columns, tables, indexes and other JPA objects names in order to avoid their transformation by any implementation of the Physical Naming Strategy&lt;/li&gt;
&lt;li&gt;In case of &lt;em&gt;snake_case&lt;/em&gt; doesn't work for you (e.g. using a legacy database), set Physical Naming Strategy to &lt;code&gt;PhysicalNamingStrategyStandardImpl&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Explicit naming of JPA objects will also prevent database schema from unwanted changes in case of an entity class or a field name refactoring.&lt;/p&gt;

&lt;p&gt;You may notice that solving potential problems in runtime we simply transfer responsibilities to developers by introducing naming conventions. Now we need to make sure that all developers follow the same rules. This can be automated by shifting these responsibilities further to development tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny5a139srcz4uu3uk2p4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny5a139srcz4uu3uk2p4.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, if you use IntelliJ IDEA, you may try &lt;a href="https://www.jpa-buddy.com/" rel="noopener noreferrer"&gt;JPA Buddy&lt;/a&gt; – a plugin intended to help developers with JPA, Hibernate, Spring Data JPA, Liquibase and other related technology. JPA Buddy enables a team to &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a21t6qpecgdlylwxkk1v.gif" rel="noopener noreferrer"&gt;set up agreed code conventions&lt;/a&gt; and applies them for newly generated JPA entities.&lt;/p&gt;

</description>
      <category>hiberante</category>
      <category>java</category>
      <category>springboot</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating associations with JPA Buddy</title>
      <dc:creator>Aleksey Stukalov</dc:creator>
      <pubDate>Sun, 14 Mar 2021 20:25:48 +0000</pubDate>
      <link>https://forem.com/aleksey/creating-associations-with-jpa-buddy-43nn</link>
      <guid>https://forem.com/aleksey/creating-associations-with-jpa-buddy-43nn</guid>
      <description>&lt;p&gt;Have a look at how easy you can create and modify associations with &lt;a href="https://www.jpa-buddy.com/"&gt;JPA Buddy&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q6sBPKQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ow4i1lxlnznfsegxoe5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q6sBPKQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ow4i1lxlnznfsegxoe5.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jpa</category>
      <category>hibernate</category>
      <category>intellij</category>
      <category>java</category>
    </item>
    <item>
      <title>JPA goes even easier with its Buddy</title>
      <dc:creator>Aleksey Stukalov</dc:creator>
      <pubDate>Sat, 13 Mar 2021 18:36:53 +0000</pubDate>
      <link>https://forem.com/aleksey/jpa-goes-even-easier-with-its-buddy-1l6n</link>
      <guid>https://forem.com/aleksey/jpa-goes-even-easier-with-its-buddy-1l6n</guid>
      <description>&lt;p&gt;So, Hello World... After almost a year of development, the first version of JPA Buddy has finally been released! This is a tool that is supposed to become your faithful coding assistant for projects with JPA and everything related: Hibernate, Spring Data, Liquibase and other mainstream stack.&lt;/p&gt;

&lt;p&gt;“Why would I use it?” - this is a fair question towards any new tool or framework. In short, if you use JPA for data persistence, JPA Buddy will help you to be more efficient. In this article, I'll present an overview of the tool. I hope it will take its fair place among the most loved tools for Java developers, ones who use JPA, Spring, Liquibase and of course the most advanced Java IDE - IntelliJ IDEA.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Story Behind
&lt;/h2&gt;

&lt;p&gt;We are the creators of CUBA Platform (btw, a few weeks ago it was renamed to Jmix:)) - a rapid application development environment on Java. CUBA Platform is quite a unique product. It has both parts: a framework and a development tool. One of the most loved parts of CUBA Studio (specialized IDE for CUBA) is "Entity Designer" - it amazed over 20 000 members of CUBA community by how easy and fast anyone can design their data model. Even for those who have never heard of JPA, things like creating JPA entities, constraints, DDL scripts become a no-brainer.&lt;/p&gt;

&lt;p&gt;Since 2016, when CUBA went open-source we have participated in dozens of conferences all around the world, gathering feedback and getting a better understanding of what developers need. And one of the most annoying questions was - "Can we use your entity designer in our application without CUBA?". I'm happy to announce that with JPA Buddy we managed to change our negative answer towards... yes!&lt;/p&gt;

&lt;p&gt;JPA Buddy is noninvasive, it doesn't require any extra dependencies - it is a tool and only a tool. This means you can gain benefits from it not only for new but also for already existing projects which employ JPA for the persistence layer. It works as an addition to your IntelliJ IDEA enabling a number of JPA-related features. JPA Buddy helps you to generate code, find and fix potential bugs, refactor and perform other laborious boilerplate operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope
&lt;/h2&gt;

&lt;p&gt;Before JPA Buddy got its first line of code we had conducted a survey in order to gather various use cases of JPA and surrounding technology. The result doesn't look very surprising - an average application nowadays is a Spring Boot application with Hibernate as an ORM implementation, Spring Data JPA as the data manipulation mechanism and Flyway or Liquibase for the database migration system. Ah, almost forgot about Lombok... This technology stack became our main focus for the first release.&lt;/p&gt;

&lt;p&gt;Speaking about the aim of the instrument we pursued the following targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimize manual boilerplate coding - the tool should generate code that you would write manually but faster&lt;/li&gt;
&lt;li&gt;Save time on reading the documentation - the tool must provide intuitive and self-explanatory visual designers&lt;/li&gt;
&lt;li&gt;Leave freedom of choice - the tool must not dictate any particular coding style, but provide various options&lt;/li&gt;
&lt;li&gt;Detect potential issues and provide ways to fix the most common problems - the tool should alert a developer about such problems as early as possible, ideally at the coding stage, not in runtime&lt;/li&gt;
&lt;li&gt;Provide a data-centric view on a project and convenient navigation between related objects
For the first release, we were able to deliver quite a significant number of features covering most aspects of data model development. Good news for those who use Liquibase - it is implemented and available in the first release of JPA Buddy. Not so good news for Flyway users - it is in the high-priority list of features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In longer-term planning we are going to introduce the following set of features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hibernate-specific annotations, like @Where, @NaturalId, @Formula, Hibernate search ones and others…&lt;/li&gt;
&lt;li&gt;Visual query designer&lt;/li&gt;
&lt;li&gt;Audit using Envers and Spring Data JPA&lt;/li&gt;
&lt;li&gt;Reverse engineering of a database schema&lt;/li&gt;
&lt;li&gt;Support for Kotlin
There are also some features we also keep in mind on the second stage: Quarkus and Micronaut support, REST API and UI generation for CRUD operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you may see, there is a long way ahead and we would much appreciate your help in prioritizing all the above features - don't hesitate to let us know your point of view via our contact form, Twitter or join Discord chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Let me give a short overview of what it looks like. Since you install JPA Buddy you will find 3 additional tool windows: JPA Structure, JPA Palette and JPA Inspector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftudpwonxeof45v82ekh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftudpwonxeof45v82ekh6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  JPA Structure
&lt;/h3&gt;

&lt;p&gt;JPA Structure is always available on the bottom-left side. It provides a comprehensive data-centric view of the project. You can use it for many different purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traverse through the data model. The entity structure is represented in a hierarchical way. You can easily observe and navigate to entities referencing the current one and ones the current entity refers to. This is an extremely useful feature, especially for those who are just diving into an existing project with a large entity graph or for code reviewers, who often see parts of the data model for the first time and have limited time to understand how it is designed.&lt;/li&gt;
&lt;li&gt;Create data-related objects: entities, JPA converters / Hibernate types, Spring Data repositories and Liquibase changelog.&lt;/li&gt;
&lt;li&gt;Observe related Spring Data repositories for each entity.&lt;/li&gt;
&lt;li&gt;View Liquibase changelogs along with their internal structure for easier navigation.&lt;/li&gt;
&lt;li&gt;Specify plugin-related settings such as DB connection, persistence units and others, which the plugin was not able to detect automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jozquimyp2a8uyu7ala.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jozquimyp2a8uyu7ala.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More features to come…&lt;/p&gt;

&lt;h3&gt;
  
  
  JPA Palette and Inspector
&lt;/h3&gt;

&lt;p&gt;JPA Palette is sitting at the top right and its content is context-dependent. It is available only when Buddy is ready to offer something for fast code generation. Currently, the tool comes up when editing following objects: JPA Entity, Spring Data repository and Liquibase changelog. Simply find the desired option from the list and double-click on it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxpajd7424yw32ds371k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxpajd7424yw32ds371k.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5q92t84khl9u5gikk0u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5q92t84khl9u5gikk0u.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfnwja4im9ayuqlubfiq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfnwja4im9ayuqlubfiq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;JPA Inspector is placed at the bottom right, beneath the Palette and becomes visible and invisible synchronously with it. The Inspector is also context-dependent. While the Palette gives you a number of options for generating new parts of code, the Inspector is intended to modify already existing code. Here you can see what capabilities you have to tune a selected part of code, e.g. a field of an entity or a statement from a Liquibase changeset.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff33p2ozrrrlvh8ffwmxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff33p2ozrrrlvh8ffwmxr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmwziudlan4ln1dwcepa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmwziudlan4ln1dwcepa.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These features are much helpful for both newbies and experienced users. Juniors can evaluate the full list of options, pick the proper one and generate valid code using intuitive visual designers. Seniors often forget minor things that are not in daily use and Buddy will save a few seconds of their valuable time on searching through the documentation in order to remember the right annotation or syntax.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdky7m238aaf2oh8zvi2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdky7m238aaf2oh8zvi2h.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthar047hx5wpkus3acmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthar047hx5wpkus3acmn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9sdtubixc8az9jam3d8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9sdtubixc8az9jam3d8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Liquibase Integration
&lt;/h2&gt;

&lt;p&gt;Good news for those who use Liquibase as a database schema versioning solution. JPA Buddy provides tight integration with it, enabling convenient changelog editing, smart changelogs generation and flexible type management.&lt;/p&gt;

&lt;p&gt;First of all, you get a visual designer for editing changelogs. Explore various available commands in the Palette and use the Inspector toolwindow to see different settings that could be applied for the selected statement.&lt;/p&gt;

&lt;p&gt;Secondly, you can define your own mappings to match Java types (Converters/Hibernate types) and the ones that should be used for each specific DBMS. Let me give you a few examples when this feature will be extremely useful:&lt;/p&gt;

&lt;p&gt;Using a byte array entity field, Hibernate scheme generator as well as Liquibase would map your Byte[] to a quite exotic type OID. I guess quite a few developers would prefer to use bytea instead of the defaultly proposed type. This could be easily solved by specifying bytea as the desired mapping type for byte[].&lt;br&gt;
Using a JPA converter or a Hibernate custom type causes unpredictable behaviour or even failures in Hibernate scheme generator and Liquibase changelog generator. You can define the right type in JPA Buddy to resolve this issue.&lt;br&gt;
Using a String field we may want to map it to &lt;em&gt;nvarchar&lt;/em&gt; instead of the default &lt;em&gt;varchar&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0m7mjqvchl8p1j7i888.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0m7mjqvchl8p1j7i888.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All these cases could be traditionally solved by using the &lt;em&gt;columnDefinition&lt;/em&gt; attribute, however, this solution would not work for cross-DBMS solutions.&lt;/p&gt;

&lt;p&gt;Finally, the most time-saving feature: Liquibase changelogs generator. From my experience (proved by the previously-mentioned survey) there are two major ways of creating Liquibase changelogs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing changelogs manually.&lt;/li&gt;
&lt;li&gt;Generating scripts by comparing two databases, source one (representing the actual state of the model) against the target one (with the previous state of the model).&lt;/li&gt;
&lt;li&gt;For those who prefer the first option JPA Buddy enables the already mentioned changelog designer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second option seems to be the most used one. So, we compare two databases. The target database is relatively easy to obtain, for instance, we can simply create a dump of the production environment. Problems come with the source database. Let's start with a minor issue: it may happen that the database used for development on the developer's laptop is not the same DBMS type as the production one. Well, this can be solved by, for example, running MS SQL in Docker on Mac. More serious thing is that it is hard to find a "clean" source database, as it normally gets formed while development by the Hibernate schema generator. Often, this leads to a bunch of garbage tables and attributes, which finally appear in the production DB. To avoid such junk you would need to spend another hour or two reviewing your database schema before running the generation of the changelogs.&lt;/p&gt;

&lt;p&gt;JPA Buddy offers an admirable function of generating changelogs directly by comparing your JPA entities with the target database (or a snapshot of the target database). You can run H2 for development purposes and still provide correct changelogs for Oracle or MS SQL or whatever you use in production. This ensures that if you have garbage in changelogs, this is exactly garbage you have in your source code. All you need is to keep your data model clean and be sure that migration will not bring unwanted artefacts to the production database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3djxiwkzmmuycrm10sq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3djxiwkzmmuycrm10sq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another feature of the changelogs generator is the ability to filter resulting statements into 3 categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;containing only safe statements, ones which cannot fail while update or ruin your application, e.g. adding a new column&lt;/li&gt;
&lt;li&gt;containing statements that may fail while update, but not lead to data-loss, e.g. applying a unique constraint&lt;/li&gt;
&lt;li&gt;containing statements that will cause data-loss, e.g. dropping a table or a column&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsrbmw6335x8kg5mbxi1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsrbmw6335x8kg5mbxi1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You decide how to split such statements: put them into separate changelogs or just tag them with some label or context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article presents an overview of the very first release of &lt;a href="https://plugins.jetbrains.com/plugin/15075-jpa-buddy" rel="noopener noreferrer"&gt;JPA Buddy&lt;/a&gt;. Obviously, as with any brand new product, it cannot avoid some issues and defects. Today the main focus is polishing the existing functionality, detecting and implementing essential features we miss. Directly speaking, we would like to inspire you to become an early adopter and form an initial community of enthusiasts. Install JPA Buddy, try it out and share your feedback with the development team - this will help a lot to steer the product in the right direction!&lt;/p&gt;

</description>
      <category>java</category>
      <category>jpa</category>
      <category>hibernate</category>
      <category>springdata</category>
    </item>
  </channel>
</rss>
